| 
									
										
										
										
											2023-01-03 11:55:46 -08:00
										 |  |  | // Copyright 2019 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-10-30 15:34:04 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | import memoizee from 'memoizee'; | 
					
						
							| 
									
										
										
										
											2022-09-26 10:24:52 -06:00
										 |  |  | import { isNumber, pick } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | import { createSelector } from 'reselect'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { StateType } from '../reducer'; | 
					
						
							| 
									
										
										
										
											2021-11-01 12:13:35 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   ConversationLookupType, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   ConversationMessageType, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   ConversationsStateType, | 
					
						
							|  |  |  |   ConversationType, | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |   ConversationVerificationData, | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   MessageLookupType, | 
					
						
							|  |  |  |   MessagesByConversationType, | 
					
						
							| 
									
										
										
										
											2023-03-20 15:23:53 -07:00
										 |  |  |   MessageTimestamps, | 
					
						
							| 
									
										
										
										
											2021-01-29 14:16:48 -08:00
										 |  |  |   PreJoinConversationType, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | } from '../ducks/conversations'; | 
					
						
							| 
									
										
										
										
											2022-11-08 13:01:59 -07:00
										 |  |  | import type { StoriesStateType, StoryDataType } from '../ducks/stories'; | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  | import { | 
					
						
							|  |  |  |   ComposerStep, | 
					
						
							|  |  |  |   OneTimeModalState, | 
					
						
							|  |  |  |   ConversationVerificationState, | 
					
						
							|  |  |  | } from '../ducks/conversationsEnums'; | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  | import { getOwn } from '../../util/getOwn'; | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  | import type { UUIDFetchStateType } from '../../util/uuidFetchState'; | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | import { deconstructLookup } from '../../util/deconstructLookup'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { PropsDataType as TimelinePropsType } from '../../components/conversation/Timeline'; | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  | import { assertDev } from '../../util/assert'; | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  | import { isConversationUnregistered } from '../../util/isConversationUnregistered'; | 
					
						
							| 
									
										
										
										
											2023-06-21 12:33:59 -04:00
										 |  |  | import { | 
					
						
							|  |  |  |   filterAndSortConversationsAlphabetically, | 
					
						
							|  |  |  |   filterAndSortConversationsByRecent, | 
					
						
							|  |  |  | } from '../../util/filterAndSortConversations'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ContactNameColorType } from '../../types/Colors'; | 
					
						
							|  |  |  | import { ContactNameColors } from '../../types/Colors'; | 
					
						
							|  |  |  | import type { AvatarDataType } from '../../types/Avatar'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | import type { UUIDStringType } from '../../types/UUID'; | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  | import { isInSystemContacts } from '../../util/isInSystemContacts'; | 
					
						
							| 
									
										
										
										
											2022-08-02 15:31:55 -04:00
										 |  |  | import { isSignalConnection } from '../../util/getSignalConnections'; | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | import { sortByTitle } from '../../util/sortByTitle'; | 
					
						
							| 
									
										
										
										
											2022-11-16 12:18:02 -08:00
										 |  |  | import { DurationInSeconds } from '../../util/durations'; | 
					
						
							| 
									
										
										
										
											2021-09-02 16:29:46 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   isDirectConversation, | 
					
						
							|  |  |  |   isGroupV1, | 
					
						
							|  |  |  |   isGroupV2, | 
					
						
							|  |  |  | } from '../../util/whatTypeOfConversation'; | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  | import { isGroupInStoryMode } from '../../util/isGroupInStoryMode'; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  | import { | 
					
						
							|  |  |  |   getIntl, | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   getUserConversationId, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |   getUserNumber, | 
					
						
							|  |  |  | } from './user'; | 
					
						
							| 
									
										
										
										
											2021-07-21 14:21:16 -05:00
										 |  |  | import { getPinnedConversationIds } from './items'; | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  | import * as log from '../../logging/log'; | 
					
						
							| 
									
										
										
										
											2022-03-11 16:31:21 -06:00
										 |  |  | import { TimelineMessageLoadingState } from '../../util/timelineUtil'; | 
					
						
							| 
									
										
										
										
											2022-11-08 21:38:19 -05:00
										 |  |  | import { isSignalConversation } from '../../util/isSignalConversation'; | 
					
						
							| 
									
										
										
										
											2022-11-08 13:01:59 -07:00
										 |  |  | import { reduce } from '../../util/iterables'; | 
					
						
							| 
									
										
										
										
											2022-12-14 13:41:04 -05:00
										 |  |  | import { getConversationTitleForPanelType } from '../../util/getConversationTitleForPanelType'; | 
					
						
							| 
									
										
										
										
											2022-12-21 15:44:23 -05:00
										 |  |  | import type { PanelRenderType } from '../../types/Panels'; | 
					
						
							| 
									
										
										
										
											2023-03-07 21:15:25 -05:00
										 |  |  | import type { HasStories } from '../../types/Stories'; | 
					
						
							|  |  |  | import { getHasStoriesSelector } from './stories2'; | 
					
						
							| 
									
										
										
										
											2023-05-11 20:27:19 -04:00
										 |  |  | import { canEditMessage } from '../../util/canEditMessage'; | 
					
						
							|  |  |  | import { isOutgoing } from '../../messages/helpers'; | 
					
						
							| 
									
										
										
										
											2023-03-07 21:15:25 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | export type ConversationWithStoriesType = ConversationType & { | 
					
						
							|  |  |  |   hasStories?: HasStories; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  | let placeholderContact: ConversationType; | 
					
						
							|  |  |  | export const getPlaceholderContact = (): ConversationType => { | 
					
						
							|  |  |  |   if (placeholderContact) { | 
					
						
							|  |  |  |     return placeholderContact; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   placeholderContact = { | 
					
						
							| 
									
										
										
										
											2021-05-07 17:21:10 -05:00
										 |  |  |     acceptedMessageRequest: false, | 
					
						
							| 
									
										
										
										
											2021-11-02 18:01:13 -05:00
										 |  |  |     badges: [], | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |     id: 'placeholder-contact', | 
					
						
							|  |  |  |     type: 'direct', | 
					
						
							| 
									
										
										
										
											2023-03-29 17:03:25 -07:00
										 |  |  |     title: window.i18n('icu:unknownContact'), | 
					
						
							| 
									
										
										
										
											2021-05-07 17:21:10 -05:00
										 |  |  |     isMe: false, | 
					
						
							|  |  |  |     sharedGroupNames: [], | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  |   return placeholderContact; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | export const getConversations = (state: StateType): ConversationsStateType => | 
					
						
							|  |  |  |   state.conversations; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-29 14:16:48 -08:00
										 |  |  | export const getPreJoinConversation = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): PreJoinConversationType | undefined => { | 
					
						
							|  |  |  |     return state.preJoinConversation; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | export const getConversationLookup = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ConversationLookupType => { | 
					
						
							|  |  |  |     return state.conversationLookup; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  | export const getConversationsByUuid = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ConversationLookupType => { | 
					
						
							|  |  |  |     return state.conversationsByUuid; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getConversationsByE164 = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ConversationLookupType => { | 
					
						
							|  |  |  |     return state.conversationsByE164; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getConversationsByGroupId = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ConversationLookupType => { | 
					
						
							|  |  |  |     return state.conversationsByGroupId; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2023-07-26 18:23:32 -04:00
										 |  |  | export const getHasPanelOpen = createSelector( | 
					
						
							| 
									
										
										
										
											2023-05-09 18:23:56 -07:00
										 |  |  |   getConversations, | 
					
						
							| 
									
										
										
										
											2023-07-26 18:23:32 -04:00
										 |  |  |   (state: ConversationsStateType): boolean => { | 
					
						
							|  |  |  |     return state.targetedConversationPanels.watermark > 0; | 
					
						
							| 
									
										
										
										
											2023-05-09 18:23:56 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  | export const getConversationsByUsername = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ConversationLookupType => { | 
					
						
							|  |  |  |     return state.conversationsByUsername; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 11:47:12 -07:00
										 |  |  | export const getAllConversations = createSelector( | 
					
						
							| 
									
										
										
										
											2021-04-21 11:31:12 -05:00
										 |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (lookup): Array<ConversationType> => Object.values(lookup) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 15:31:55 -04:00
										 |  |  | export const getAllSignalConnections = createSelector( | 
					
						
							|  |  |  |   getAllConversations, | 
					
						
							|  |  |  |   (conversations): Array<ConversationType> => | 
					
						
							|  |  |  |     conversations.filter(isSignalConnection) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-21 11:31:12 -05:00
										 |  |  | export const getConversationsByTitleSelector = createSelector( | 
					
						
							|  |  |  |   getAllConversations, | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |   (conversations): ((title: string) => Array<ConversationType>) => | 
					
						
							|  |  |  |     (title: string) => | 
					
						
							|  |  |  |       conversations.filter(conversation => conversation.title === title) | 
					
						
							| 
									
										
										
										
											2021-04-21 11:31:12 -05:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-12 15:58:14 -06:00
										 |  |  | export const getSelectedConversationId = createSelector( | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): string | undefined => { | 
					
						
							| 
									
										
										
										
											2021-02-12 15:58:14 -06:00
										 |  |  |     return state.selectedConversationId; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:23:53 -07:00
										 |  |  | type TargetedMessageType = { | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   id: string; | 
					
						
							|  |  |  |   counter: number; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2023-03-20 15:23:53 -07:00
										 |  |  | export const getTargetedMessage = createSelector( | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   getConversations, | 
					
						
							| 
									
										
										
										
											2023-03-20 15:23:53 -07:00
										 |  |  |   (state: ConversationsStateType): TargetedMessageType | undefined => { | 
					
						
							|  |  |  |     if (!state.targetedMessage) { | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |       return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							| 
									
										
										
										
											2023-03-20 15:23:53 -07:00
										 |  |  |       id: state.targetedMessage, | 
					
						
							|  |  |  |       counter: state.targetedMessageCounter, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2023-03-20 15:23:53 -07:00
										 |  |  | export const getSelectedMessageIds = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ReadonlyArray<string> | undefined => { | 
					
						
							|  |  |  |     return state.selectedMessageIds; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | export const getLastSelectedMessage = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): MessageTimestamps | undefined => { | 
					
						
							|  |  |  |     return state.lastSelectedMessage; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | export const getShowArchived = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): boolean => { | 
					
						
							|  |  |  |     return Boolean(state.showArchived); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  | const getComposerState = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType) => state.composer | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | export const getComposerStep = createSelector( | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   getComposerState, | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |   (composerState): undefined | ComposerStep => composerState?.step | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const hasGroupCreationError = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composerState): boolean => { | 
					
						
							|  |  |  |     if (composerState?.step === ComposerStep.SetGroupMetadata) { | 
					
						
							|  |  |  |       return composerState.hasError; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const isCreatingGroup = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composerState): boolean => | 
					
						
							|  |  |  |     composerState?.step === ComposerStep.SetGroupMetadata && | 
					
						
							|  |  |  |     composerState.isCreating | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  | export const isEditingAvatar = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composerState): boolean => | 
					
						
							|  |  |  |     composerState?.step === ComposerStep.SetGroupMetadata && | 
					
						
							|  |  |  |     composerState.isEditingAvatar | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getComposeAvatarData = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composerState): ReadonlyArray<AvatarDataType> => | 
					
						
							|  |  |  |     composerState?.step === ComposerStep.SetGroupMetadata | 
					
						
							|  |  |  |       ? composerState.userAvatarData | 
					
						
							|  |  |  |       : [] | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export const getMessages = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): MessageLookupType => { | 
					
						
							|  |  |  |     return state.messagesLookup; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | export const getMessagesByConversation = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): MessagesByConversationType => { | 
					
						
							|  |  |  |     return state.messagesByConversation; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 20:27:19 -04:00
										 |  |  | export const getConversationMessages = createSelector( | 
					
						
							|  |  |  |   getSelectedConversationId, | 
					
						
							|  |  |  |   getMessagesByConversation, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     conversationId, | 
					
						
							|  |  |  |     messagesByConversation | 
					
						
							|  |  |  |   ): ConversationMessageType | undefined => { | 
					
						
							|  |  |  |     return conversationId ? messagesByConversation[conversationId] : undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | const collator = new Intl.Collator(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 18:35:32 -07:00
										 |  |  | // Note: we will probably want to put i18n and regionCode back when we are formatting
 | 
					
						
							|  |  |  | //   phone numbers and contacts from scratch here again.
 | 
					
						
							|  |  |  | export const _getConversationComparator = () => { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   return (left: ConversationType, right: ConversationType): number => { | 
					
						
							|  |  |  |     const leftTimestamp = left.timestamp; | 
					
						
							|  |  |  |     const rightTimestamp = right.timestamp; | 
					
						
							|  |  |  |     if (leftTimestamp && !rightTimestamp) { | 
					
						
							|  |  |  |       return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (rightTimestamp && !leftTimestamp) { | 
					
						
							|  |  |  |       return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (leftTimestamp && rightTimestamp && leftTimestamp !== rightTimestamp) { | 
					
						
							|  |  |  |       return rightTimestamp - leftTimestamp; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-09 17:43:09 -07:00
										 |  |  |     if ( | 
					
						
							|  |  |  |       typeof left.inboxPosition === 'number' && | 
					
						
							|  |  |  |       typeof right.inboxPosition === 'number' | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       return right.inboxPosition > left.inboxPosition ? -1 : 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (typeof left.inboxPosition === 'number' && right.inboxPosition == null) { | 
					
						
							|  |  |  |       return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (typeof right.inboxPosition === 'number' && left.inboxPosition == null) { | 
					
						
							|  |  |  |       return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 18:35:32 -07:00
										 |  |  |     return collator.compare(left.title, right.title); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | export const getConversationComparator = createSelector( | 
					
						
							|  |  |  |   getIntl, | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							|  |  |  |   _getConversationComparator | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | export const _getLeftPaneLists = ( | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   lookup: ConversationLookupType, | 
					
						
							|  |  |  |   comparator: (left: ConversationType, right: ConversationType) => number, | 
					
						
							| 
									
										
										
										
											2020-12-04 12:41:40 -08:00
										 |  |  |   selectedConversation?: string, | 
					
						
							| 
									
										
										
										
											2022-12-21 16:07:02 -08:00
										 |  |  |   pinnedConversationIds?: ReadonlyArray<string> | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | ): { | 
					
						
							|  |  |  |   conversations: Array<ConversationType>; | 
					
						
							|  |  |  |   archivedConversations: Array<ConversationType>; | 
					
						
							| 
									
										
										
										
											2020-09-29 15:07:03 -07:00
										 |  |  |   pinnedConversations: Array<ConversationType>; | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | } => { | 
					
						
							|  |  |  |   const conversations: Array<ConversationType> = []; | 
					
						
							|  |  |  |   const archivedConversations: Array<ConversationType> = []; | 
					
						
							| 
									
										
										
										
											2020-09-29 15:07:03 -07:00
										 |  |  |   const pinnedConversations: Array<ConversationType> = []; | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-20 00:58:54 +02:00
										 |  |  |   const values = Object.values(lookup); | 
					
						
							|  |  |  |   const max = values.length; | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  |   for (let i = 0; i < max; i += 1) { | 
					
						
							| 
									
										
										
										
											2019-06-20 00:58:54 +02:00
										 |  |  |     let conversation = values[i]; | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |     if (selectedConversation === conversation.id) { | 
					
						
							|  |  |  |       conversation = { | 
					
						
							|  |  |  |         ...conversation, | 
					
						
							|  |  |  |         isSelected: true, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-08 21:38:19 -05:00
										 |  |  |     if (isSignalConversation(conversation)) { | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |     // We always show pinned conversations
 | 
					
						
							|  |  |  |     if (conversation.isPinned) { | 
					
						
							|  |  |  |       pinnedConversations.push(conversation); | 
					
						
							| 
									
										
										
										
											2021-05-04 15:16:59 -07:00
										 |  |  |       continue; | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |     if (conversation.activeAt) { | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |       if (conversation.isArchived) { | 
					
						
							|  |  |  |         archivedConversations.push(conversation); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         conversations.push(conversation); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-20 00:58:54 +02:00
										 |  |  |   conversations.sort(comparator); | 
					
						
							|  |  |  |   archivedConversations.sort(comparator); | 
					
						
							| 
									
										
										
										
											2020-10-10 07:25:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   pinnedConversations.sort( | 
					
						
							|  |  |  |     (a, b) => | 
					
						
							| 
									
										
										
										
											2020-12-04 12:41:40 -08:00
										 |  |  |       (pinnedConversationIds || []).indexOf(a.id) - | 
					
						
							|  |  |  |       (pinnedConversationIds || []).indexOf(b.id) | 
					
						
							| 
									
										
										
										
											2020-10-10 07:25:17 -07:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-06-20 00:58:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-29 15:07:03 -07:00
										 |  |  |   return { conversations, archivedConversations, pinnedConversations }; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | export const getLeftPaneLists = createSelector( | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   getConversationComparator, | 
					
						
							| 
									
										
										
										
											2021-02-12 15:58:14 -06:00
										 |  |  |   getSelectedConversationId, | 
					
						
							| 
									
										
										
										
											2020-12-04 12:41:40 -08:00
										 |  |  |   getPinnedConversationIds, | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  |   _getLeftPaneLists | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | export const getMaximumGroupSizeModalState = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composerState): OneTimeModalState => { | 
					
						
							|  |  |  |     switch (composerState?.step) { | 
					
						
							|  |  |  |       case ComposerStep.ChooseGroupMembers: | 
					
						
							|  |  |  |       case ComposerStep.SetGroupMetadata: | 
					
						
							|  |  |  |         return composerState.maximumGroupSizeModalState; | 
					
						
							|  |  |  |       default: | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |         assertDev( | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |           false, | 
					
						
							|  |  |  |           'Can\'t get the maximum group size modal state in this composer state; returning "never shown"' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         return OneTimeModalState.NeverShown; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getRecommendedGroupSizeModalState = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composerState): OneTimeModalState => { | 
					
						
							|  |  |  |     switch (composerState?.step) { | 
					
						
							|  |  |  |       case ComposerStep.ChooseGroupMembers: | 
					
						
							|  |  |  |       case ComposerStep.SetGroupMetadata: | 
					
						
							|  |  |  |         return composerState.recommendedGroupSizeModalState; | 
					
						
							|  |  |  |       default: | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |         assertDev( | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |           false, | 
					
						
							|  |  |  |           'Can\'t get the recommended group size modal state in this composer state; returning "never shown"' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         return OneTimeModalState.NeverShown; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | export const getMe = createSelector( | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   [getConversationLookup, getUserConversationId], | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     lookup: ConversationLookupType, | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |     ourConversationId: string | undefined | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   ): ConversationType => { | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |     if (!ourConversationId) { | 
					
						
							|  |  |  |       return getPlaceholderContact(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return lookup[ourConversationId] || getPlaceholderContact(); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  | export const getComposerConversationSearchTerm = createSelector( | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   getComposerState, | 
					
						
							|  |  |  |   (composer): string => { | 
					
						
							|  |  |  |     if (!composer) { | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |       assertDev( | 
					
						
							|  |  |  |         false, | 
					
						
							|  |  |  |         'getComposerConversationSearchTerm: composer is not open' | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |       return ''; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |     if (composer.step === ComposerStep.SetGroupMetadata) { | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |       assertDev( | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |         false, | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |         'getComposerConversationSearchTerm: composer does not have a search term' | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |       ); | 
					
						
							|  |  |  |       return ''; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |     return composer.searchTerm; | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  | export const getComposerUUIDFetchState = createSelector( | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |   getComposerState, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |   (composer): UUIDFetchStateType => { | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |     if (!composer) { | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |       assertDev(false, 'getIsFetchingUsername: composer is not open'); | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |       return {}; | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |     if ( | 
					
						
							|  |  |  |       composer.step !== ComposerStep.StartDirectConversation && | 
					
						
							|  |  |  |       composer.step !== ComposerStep.ChooseGroupMembers | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |       assertDev( | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |         false, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |         `getComposerUUIDFetchState: step ${composer.step} ` + | 
					
						
							|  |  |  |           'has no uuidFetchState key' | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |       ); | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |       return {}; | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |     return composer.uuidFetchState; | 
					
						
							| 
									
										
										
										
											2021-11-11 17:17:29 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  | function isTrusted(conversation: ConversationType): boolean { | 
					
						
							|  |  |  |   if (conversation.type === 'group') { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Boolean( | 
					
						
							| 
									
										
										
										
											2021-06-02 12:24:22 -05:00
										 |  |  |     isInSystemContacts(conversation) || | 
					
						
							| 
									
										
										
										
											2022-03-28 14:13:24 -07:00
										 |  |  |       conversation.sharedGroupNames.length > 0 || | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |       conversation.profileSharing || | 
					
						
							|  |  |  |       conversation.isMe | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function hasDisplayInfo(conversation: ConversationType): boolean { | 
					
						
							|  |  |  |   if (conversation.type === 'group') { | 
					
						
							|  |  |  |     return Boolean(conversation.name); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Boolean( | 
					
						
							|  |  |  |     conversation.name || | 
					
						
							|  |  |  |       conversation.profileName || | 
					
						
							|  |  |  |       conversation.phoneNumber || | 
					
						
							|  |  |  |       conversation.isMe | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  | function canComposeConversation(conversation: ConversationType): boolean { | 
					
						
							|  |  |  |   return Boolean( | 
					
						
							| 
									
										
										
										
											2022-11-08 21:38:19 -05:00
										 |  |  |     !isSignalConversation(conversation) && | 
					
						
							|  |  |  |       !conversation.isBlocked && | 
					
						
							| 
									
										
										
										
											2023-04-05 13:48:00 -07:00
										 |  |  |       !conversation.removalStage && | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |       !isConversationUnregistered(conversation) && | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |       hasDisplayInfo(conversation) && | 
					
						
							|  |  |  |       isTrusted(conversation) | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-27 15:35:35 -07:00
										 |  |  | export const getAllComposableConversations = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (conversationLookup: ConversationLookupType): Array<ConversationType> => | 
					
						
							|  |  |  |     Object.values(conversationLookup).filter( | 
					
						
							| 
									
										
										
										
											2021-04-28 13:44:48 -07:00
										 |  |  |       conversation => | 
					
						
							| 
									
										
										
										
											2022-11-08 21:38:19 -05:00
										 |  |  |         !isSignalConversation(conversation) && | 
					
						
							| 
									
										
										
										
											2021-04-28 13:44:48 -07:00
										 |  |  |         !conversation.isBlocked && | 
					
						
							| 
									
										
										
										
											2023-04-05 13:48:00 -07:00
										 |  |  |         !conversation.removalStage && | 
					
						
							| 
									
										
										
										
											2021-04-28 13:44:48 -07:00
										 |  |  |         !conversation.isGroupV1AndDisabled && | 
					
						
							|  |  |  |         !isConversationUnregistered(conversation) && | 
					
						
							|  |  |  |         // All conversation should have a title except in weird cases where
 | 
					
						
							|  |  |  |         // they don't, in that case we don't want to show these for Forwarding.
 | 
					
						
							| 
									
										
										
										
											2022-09-26 13:18:11 -07:00
										 |  |  |         conversation.titleNoDefault && | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |         hasDisplayInfo(conversation) | 
					
						
							| 
									
										
										
										
											2021-04-27 15:35:35 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 10:24:52 -06:00
										 |  |  | export const getAllGroupsWithInviteAccess = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (conversationLookup: ConversationLookupType): Array<ConversationType> => | 
					
						
							|  |  |  |     Object.values(conversationLookup).filter(conversation => { | 
					
						
							|  |  |  |       return ( | 
					
						
							|  |  |  |         conversation.type === 'group' && | 
					
						
							|  |  |  |         conversation.title && | 
					
						
							|  |  |  |         conversation.canAddNewMembers | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |  * getComposableContacts/getCandidateContactsForNewGroup both return contacts for the | 
					
						
							|  |  |  |  * composer and group members, a different list from your primary system contacts. | 
					
						
							|  |  |  |  * This list may include false positives, which is better than missing contacts. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Note: the key difference between them: | 
					
						
							|  |  |  |  *   getComposableContacts includes Note to Self | 
					
						
							|  |  |  |  *   getCandidateContactsForNewGroup does not include Note to Self | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |  * Because they filter unregistered contacts and that's (partially) determined by the | 
					
						
							|  |  |  |  * current time, it's possible for them to return stale contacts that have unregistered | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |  * if no other conversations change. This should be a rare false positive. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  | export const getComposableContacts = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (conversationLookup: ConversationLookupType): Array<ConversationType> => | 
					
						
							|  |  |  |     Object.values(conversationLookup).filter( | 
					
						
							|  |  |  |       conversation => | 
					
						
							|  |  |  |         conversation.type === 'direct' && canComposeConversation(conversation) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  | export const getCandidateContactsForNewGroup = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (conversationLookup: ConversationLookupType): Array<ConversationType> => | 
					
						
							|  |  |  |     Object.values(conversationLookup).filter( | 
					
						
							|  |  |  |       conversation => | 
					
						
							|  |  |  |         conversation.type === 'direct' && | 
					
						
							|  |  |  |         !conversation.isMe && | 
					
						
							|  |  |  |         canComposeConversation(conversation) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  | export const getComposableGroups = createSelector( | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (conversationLookup: ConversationLookupType): Array<ConversationType> => | 
					
						
							|  |  |  |     Object.values(conversationLookup).filter( | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |       conversation => | 
					
						
							|  |  |  |         conversation.type === 'group' && canComposeConversation(conversation) | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |     ) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  | const getConversationIdsWithStories = createSelector( | 
					
						
							|  |  |  |   (state: StateType): StoriesStateType => state.stories, | 
					
						
							|  |  |  |   (stories: StoriesStateType): Set<string> => { | 
					
						
							|  |  |  |     return new Set(stories.stories.map(({ conversationId }) => conversationId)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  | export const getNonGroupStories = createSelector( | 
					
						
							|  |  |  |   getComposableGroups, | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  |   getConversationIdsWithStories, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     groups: Array<ConversationType>, | 
					
						
							|  |  |  |     conversationIdsWithStories: Set<string> | 
					
						
							|  |  |  |   ): Array<ConversationType> => { | 
					
						
							|  |  |  |     return groups.filter( | 
					
						
							| 
									
										
										
										
											2023-01-13 11:20:35 -08:00
										 |  |  |       group => | 
					
						
							|  |  |  |         !isGroupInStoryMode(group, conversationIdsWithStories) && !group.left | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-08 13:01:59 -07:00
										 |  |  | export const selectMostRecentActiveStoryTimestampByGroupOrDistributionList = | 
					
						
							|  |  |  |   createSelector( | 
					
						
							| 
									
										
										
										
											2022-11-09 20:24:42 -08:00
										 |  |  |     (state: StateType): ReadonlyArray<StoryDataType> => state.stories.stories, | 
					
						
							|  |  |  |     (stories: ReadonlyArray<StoryDataType>): Record<string, number> => { | 
					
						
							| 
									
										
										
										
											2022-11-08 13:01:59 -07:00
										 |  |  |       return reduce<StoryDataType, Record<string, number>>( | 
					
						
							|  |  |  |         stories, | 
					
						
							|  |  |  |         (acc, story) => { | 
					
						
							|  |  |  |           const distributionListOrConversationId = | 
					
						
							|  |  |  |             story.storyDistributionListId ?? story.conversationId; | 
					
						
							|  |  |  |           const cur = acc[distributionListOrConversationId]; | 
					
						
							|  |  |  |           if (cur && story.timestamp < cur) { | 
					
						
							|  |  |  |             return acc; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return { | 
					
						
							|  |  |  |             ...acc, | 
					
						
							|  |  |  |             [distributionListOrConversationId]: story.timestamp, | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         {} | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  | export const getGroupStories = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  |   getConversationIdsWithStories, | 
					
						
							| 
									
										
										
										
											2023-03-07 21:15:25 -05:00
										 |  |  |   getHasStoriesSelector, | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  |   ( | 
					
						
							|  |  |  |     conversationLookup: ConversationLookupType, | 
					
						
							| 
									
										
										
										
											2023-03-07 21:15:25 -05:00
										 |  |  |     conversationIdsWithStories: Set<string>, | 
					
						
							|  |  |  |     hasStoriesSelector | 
					
						
							|  |  |  |   ): Array<ConversationWithStoriesType> => { | 
					
						
							|  |  |  |     return Object.values(conversationLookup) | 
					
						
							|  |  |  |       .filter( | 
					
						
							|  |  |  |         conversation => | 
					
						
							|  |  |  |           isGroupInStoryMode(conversation, conversationIdsWithStories) && | 
					
						
							|  |  |  |           !conversation.left | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .map(conversation => ({ | 
					
						
							|  |  |  |         ...conversation, | 
					
						
							|  |  |  |         hasStories: hasStoriesSelector(conversation.id), | 
					
						
							|  |  |  |       })); | 
					
						
							| 
									
										
										
										
											2022-10-07 17:19:02 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  | const getNormalizedComposerConversationSearchTerm = createSelector( | 
					
						
							|  |  |  |   getComposerConversationSearchTerm, | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   (searchTerm: string): string => searchTerm.trim() | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  | export const getFilteredComposeContacts = createSelector( | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |   getNormalizedComposerConversationSearchTerm, | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  |   getComposableContacts, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |   getRegionCode, | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   ( | 
					
						
							|  |  |  |     searchTerm: string, | 
					
						
							| 
									
										
										
										
											2022-12-21 16:07:02 -08:00
										 |  |  |     contacts: ReadonlyArray<ConversationType>, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |     regionCode: string | undefined | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   ): Array<ConversationType> => { | 
					
						
							| 
									
										
										
										
											2023-06-21 12:33:59 -04:00
										 |  |  |     return filterAndSortConversationsAlphabetically( | 
					
						
							|  |  |  |       contacts, | 
					
						
							|  |  |  |       searchTerm, | 
					
						
							|  |  |  |       regionCode | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  | export const getFilteredComposeGroups = createSelector( | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |   getNormalizedComposerConversationSearchTerm, | 
					
						
							|  |  |  |   getComposableGroups, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |   getRegionCode, | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |   ( | 
					
						
							|  |  |  |     searchTerm: string, | 
					
						
							| 
									
										
										
										
											2022-12-21 16:07:02 -08:00
										 |  |  |     groups: ReadonlyArray<ConversationType>, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |     regionCode: string | undefined | 
					
						
							| 
									
										
										
										
											2023-01-25 16:51:08 -07:00
										 |  |  |   ): Array< | 
					
						
							|  |  |  |     ConversationType & { | 
					
						
							|  |  |  |       membersCount: number; | 
					
						
							|  |  |  |       disabledReason: undefined; | 
					
						
							|  |  |  |       memberships: ReadonlyArray<unknown>; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   > => { | 
					
						
							| 
									
										
										
										
											2023-06-21 12:33:59 -04:00
										 |  |  |     return filterAndSortConversationsAlphabetically( | 
					
						
							| 
									
										
										
										
											2023-01-25 16:51:08 -07:00
										 |  |  |       groups, | 
					
						
							|  |  |  |       searchTerm, | 
					
						
							|  |  |  |       regionCode | 
					
						
							|  |  |  |     ).map(group => ({ | 
					
						
							|  |  |  |       ...group, | 
					
						
							|  |  |  |       // we don't disable groups when composing, already filtered
 | 
					
						
							|  |  |  |       disabledReason: undefined, | 
					
						
							|  |  |  |       // should always be populated for a group
 | 
					
						
							|  |  |  |       membersCount: group.membersCount ?? 0, | 
					
						
							|  |  |  |       memberships: group.memberships ?? [], | 
					
						
							|  |  |  |     })); | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-04 09:17:32 -07:00
										 |  |  | export const getFilteredCandidateContactsForNewGroup = createSelector( | 
					
						
							|  |  |  |   getCandidateContactsForNewGroup, | 
					
						
							| 
									
										
										
										
											2021-04-20 16:16:49 -07:00
										 |  |  |   getNormalizedComposerConversationSearchTerm, | 
					
						
							| 
									
										
										
										
											2022-04-04 17:38:22 -07:00
										 |  |  |   getRegionCode, | 
					
						
							| 
									
										
										
										
											2022-05-31 09:28:31 -07:00
										 |  |  |   filterAndSortConversationsByRecent | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const getGroupCreationComposerState = createSelector( | 
					
						
							|  |  |  |   getComposerState, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     composerState | 
					
						
							|  |  |  |   ): { | 
					
						
							|  |  |  |     groupName: string; | 
					
						
							| 
									
										
										
										
											2021-09-23 17:49:05 -07:00
										 |  |  |     groupAvatar: undefined | Uint8Array; | 
					
						
							| 
									
										
										
										
											2022-11-16 12:18:02 -08:00
										 |  |  |     groupExpireTimer: DurationInSeconds; | 
					
						
							| 
									
										
										
										
											2022-12-21 16:07:02 -08:00
										 |  |  |     selectedConversationIds: ReadonlyArray<string>; | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |   } => { | 
					
						
							|  |  |  |     switch (composerState?.step) { | 
					
						
							|  |  |  |       case ComposerStep.ChooseGroupMembers: | 
					
						
							|  |  |  |       case ComposerStep.SetGroupMetadata: | 
					
						
							|  |  |  |         return composerState; | 
					
						
							|  |  |  |       default: | 
					
						
							| 
									
										
										
										
											2022-09-15 12:17:15 -07:00
										 |  |  |         assertDev( | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |           false, | 
					
						
							|  |  |  |           'getSetGroupMetadataComposerState: expected step to be SetGroupMetadata' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           groupName: '', | 
					
						
							|  |  |  |           groupAvatar: undefined, | 
					
						
							| 
									
										
										
										
											2022-11-16 12:18:02 -08:00
										 |  |  |           groupExpireTimer: DurationInSeconds.ZERO, | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |           selectedConversationIds: [], | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getComposeGroupAvatar = createSelector( | 
					
						
							|  |  |  |   getGroupCreationComposerState, | 
					
						
							| 
									
										
										
										
											2021-09-23 17:49:05 -07:00
										 |  |  |   (composerState): undefined | Uint8Array => composerState.groupAvatar | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getComposeGroupName = createSelector( | 
					
						
							|  |  |  |   getGroupCreationComposerState, | 
					
						
							|  |  |  |   (composerState): string => composerState.groupName | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-25 16:52:56 -07:00
										 |  |  | export const getComposeGroupExpireTimer = createSelector( | 
					
						
							|  |  |  |   getGroupCreationComposerState, | 
					
						
							| 
									
										
										
										
											2022-11-16 12:18:02 -08:00
										 |  |  |   (composerState): DurationInSeconds => composerState.groupExpireTimer | 
					
						
							| 
									
										
										
										
											2021-06-25 16:52:56 -07:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | export const getComposeSelectedContacts = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   getGroupCreationComposerState, | 
					
						
							|  |  |  |   (conversationLookup, composerState): Array<ConversationType> => | 
					
						
							|  |  |  |     deconstructLookup(conversationLookup, composerState.selectedConversationIds) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | // This is where we will put Conversation selector logic, replicating what
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | // is currently in models/conversation.getProps()
 | 
					
						
							|  |  |  | // What needs to happen to pull that selector logic here?
 | 
					
						
							|  |  |  | //   1) contactTypingTimers - that UI-only state needs to be moved to redux
 | 
					
						
							|  |  |  | //   2) all of the message selectors need to be reselect-based; today those
 | 
					
						
							|  |  |  | //      Backbone-based prop-generation functions expect to get Conversation information
 | 
					
						
							|  |  |  | //      directly via ConversationController
 | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export function _conversationSelector( | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |   conversation?: ConversationType | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   // regionCode: string,
 | 
					
						
							|  |  |  |   // userNumber: string
 | 
					
						
							|  |  |  | ): ConversationType { | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |   if (conversation) { | 
					
						
							|  |  |  |     return conversation; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return getPlaceholderContact(); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A little optimization to reset our selector cache when high-level application data
 | 
					
						
							|  |  |  | //   changes: regionCode and userNumber.
 | 
					
						
							|  |  |  | type CachedConversationSelectorType = ( | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |   conversation?: ConversationType | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | ) => ConversationType; | 
					
						
							|  |  |  | export const getCachedSelectorForConversation = createSelector( | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							|  |  |  |   getUserNumber, | 
					
						
							|  |  |  |   (): CachedConversationSelectorType => { | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     // Note: memoizee will check all parameters provided, and only run our selector
 | 
					
						
							|  |  |  |     //   if any of them have changed.
 | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  |     return memoizee(_conversationSelector, { max: 2000 }); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  | export type GetConversationByIdType = (id?: string) => ConversationType; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export const getConversationSelector = createSelector( | 
					
						
							|  |  |  |   getCachedSelectorForConversation, | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |   getConversationsByUuid, | 
					
						
							|  |  |  |   getConversationsByE164, | 
					
						
							|  |  |  |   getConversationsByGroupId, | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   ( | 
					
						
							|  |  |  |     selector: CachedConversationSelectorType, | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |     byId: ConversationLookupType, | 
					
						
							|  |  |  |     byUuid: ConversationLookupType, | 
					
						
							|  |  |  |     byE164: ConversationLookupType, | 
					
						
							|  |  |  |     byGroupId: ConversationLookupType | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   ): GetConversationByIdType => { | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |     return (id?: string) => { | 
					
						
							|  |  |  |       if (!id) { | 
					
						
							|  |  |  |         return selector(undefined); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 07:08:59 -08:00
										 |  |  |       const onUuid = getOwn(byUuid, id.toLowerCase ? id.toLowerCase() : id); | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |       if (onUuid) { | 
					
						
							|  |  |  |         return selector(onUuid); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-07-13 17:46:02 -07:00
										 |  |  |       const onE164 = getOwn(byE164, id); | 
					
						
							|  |  |  |       if (onE164) { | 
					
						
							|  |  |  |         return selector(onE164); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |       const onGroupId = getOwn(byGroupId, id); | 
					
						
							|  |  |  |       if (onGroupId) { | 
					
						
							|  |  |  |         return selector(onGroupId); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const onId = getOwn(byId, id); | 
					
						
							|  |  |  |       if (onId) { | 
					
						
							|  |  |  |         return selector(onId); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |       log.warn(`getConversationSelector: No conversation found for id ${id}`); | 
					
						
							| 
									
										
										
										
											2021-01-06 07:41:43 -08:00
										 |  |  |       // This will return a placeholder contact
 | 
					
						
							|  |  |  |       return selector(undefined); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 13:32:38 -05:00
										 |  |  | export const getConversationByIdSelector = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |   conversationLookup => | 
					
						
							|  |  |  |     (id: string): undefined | ConversationType => | 
					
						
							|  |  |  |       getOwn(conversationLookup, id) | 
					
						
							| 
									
										
										
										
											2021-04-29 13:32:38 -05:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | export const getConversationByUuidSelector = createSelector( | 
					
						
							|  |  |  |   getConversationsByUuid, | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |   conversationsByUuid => | 
					
						
							|  |  |  |     (uuid: UUIDStringType): undefined | ConversationType => | 
					
						
							|  |  |  |       getOwn(conversationsByUuid, uuid) | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  | const getCachedConversationMemberColorsSelector = createSelector( | 
					
						
							|  |  |  |   getConversationSelector, | 
					
						
							|  |  |  |   getUserConversationId, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     conversationSelector: GetConversationByIdType, | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |     ourConversationId: string | undefined | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  |   ) => { | 
					
						
							|  |  |  |     return memoizee( | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |       (conversationId: string | undefined) => { | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  |         const contactNameColors: Map<string, ContactNameColorType> = new Map(); | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           sortedGroupMembers = [], | 
					
						
							|  |  |  |           type, | 
					
						
							|  |  |  |           id: theirId, | 
					
						
							|  |  |  |         } = conversationSelector(conversationId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (type === 'direct') { | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |           if (ourConversationId) { | 
					
						
							|  |  |  |             contactNameColors.set(ourConversationId, ContactNameColors[0]); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  |           contactNameColors.set(theirId, ContactNameColors[0]); | 
					
						
							|  |  |  |           return contactNameColors; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         [...sortedGroupMembers] | 
					
						
							|  |  |  |           .sort((left, right) => | 
					
						
							|  |  |  |             String(left.uuid) > String(right.uuid) ? 1 : -1 | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |           .forEach((member, i) => { | 
					
						
							|  |  |  |             contactNameColors.set( | 
					
						
							|  |  |  |               member.id, | 
					
						
							|  |  |  |               ContactNameColors[i % ContactNameColors.length] | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return contactNameColors; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { max: 100 } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type ContactNameColorSelectorType = ( | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |   contactId: string | undefined | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  | ) => ContactNameColorType; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getContactNameColorSelector = createSelector( | 
					
						
							|  |  |  |   getCachedConversationMemberColorsSelector, | 
					
						
							|  |  |  |   conversationMemberColorsSelector => { | 
					
						
							|  |  |  |     return ( | 
					
						
							|  |  |  |       conversationId: string, | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |       contactId: string | undefined | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  |     ): ContactNameColorType => { | 
					
						
							| 
									
										
										
										
											2022-02-23 10:48:40 -08:00
										 |  |  |       if (!contactId) { | 
					
						
							|  |  |  |         log.warn('No color generated for missing contactId'); | 
					
						
							|  |  |  |         return ContactNameColors[0]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |       const contactNameColors = | 
					
						
							|  |  |  |         conversationMemberColorsSelector(conversationId); | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  |       const color = contactNameColors.get(contactId); | 
					
						
							|  |  |  |       if (!color) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |         log.warn(`No color generated for contact ${contactId}`); | 
					
						
							| 
									
										
										
										
											2021-08-19 13:14:41 -07:00
										 |  |  |         return ContactNameColors[0]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return color; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | export function _conversationMessagesSelector( | 
					
						
							|  |  |  |   conversation: ConversationMessageType | 
					
						
							|  |  |  | ): TimelinePropsType { | 
					
						
							|  |  |  |   const { | 
					
						
							| 
									
										
										
										
											2019-09-03 13:06:17 -07:00
										 |  |  |     isNearBottom, | 
					
						
							| 
									
										
										
										
											2022-05-11 15:41:45 -07:00
										 |  |  |     messageChangeCounter, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     messageIds, | 
					
						
							| 
									
										
										
										
											2022-03-11 16:31:21 -06:00
										 |  |  |     messageLoadingState, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     metrics, | 
					
						
							|  |  |  |     scrollToMessageCounter, | 
					
						
							| 
									
										
										
										
											2022-03-11 16:31:21 -06:00
										 |  |  |     scrollToMessageId, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   } = conversation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const firstId = messageIds[0]; | 
					
						
							|  |  |  |   const lastId = | 
					
						
							|  |  |  |     messageIds.length === 0 ? undefined : messageIds[messageIds.length - 1]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 11:35:14 -07:00
										 |  |  |   const { oldestUnseen } = metrics; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const haveNewest = !metrics.newest || !lastId || lastId === metrics.newest.id; | 
					
						
							|  |  |  |   const haveOldest = | 
					
						
							|  |  |  |     !metrics.oldest || !firstId || firstId === metrics.oldest.id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const items = messageIds; | 
					
						
							| 
									
										
										
										
											2019-08-23 12:56:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 11:35:14 -07:00
										 |  |  |   const oldestUnseenIndex = oldestUnseen | 
					
						
							|  |  |  |     ? messageIds.findIndex(id => id === oldestUnseen.id) | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     : undefined; | 
					
						
							|  |  |  |   const scrollToIndex = scrollToMessageId | 
					
						
							|  |  |  |     ? messageIds.findIndex(id => id === scrollToMessageId) | 
					
						
							|  |  |  |     : undefined; | 
					
						
							| 
									
										
										
										
											2022-04-22 11:35:14 -07:00
										 |  |  |   const { totalUnseen } = metrics; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     haveNewest, | 
					
						
							|  |  |  |     haveOldest, | 
					
						
							| 
									
										
										
										
											2021-11-30 03:25:24 -08:00
										 |  |  |     isNearBottom, | 
					
						
							| 
									
										
										
										
											2022-03-03 14:23:10 -06:00
										 |  |  |     items, | 
					
						
							| 
									
										
										
										
											2022-05-11 15:41:45 -07:00
										 |  |  |     messageChangeCounter, | 
					
						
							| 
									
										
										
										
											2022-03-11 16:31:21 -06:00
										 |  |  |     messageLoadingState, | 
					
						
							| 
									
										
										
										
											2022-04-22 11:35:14 -07:00
										 |  |  |     oldestUnseenIndex: | 
					
						
							|  |  |  |       isNumber(oldestUnseenIndex) && oldestUnseenIndex >= 0 | 
					
						
							|  |  |  |         ? oldestUnseenIndex | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |         : undefined, | 
					
						
							|  |  |  |     scrollToIndex: | 
					
						
							| 
									
										
										
										
											2019-08-23 12:56:49 -07:00
										 |  |  |       isNumber(scrollToIndex) && scrollToIndex >= 0 ? scrollToIndex : undefined, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     scrollToIndexCounter: scrollToMessageCounter, | 
					
						
							| 
									
										
										
										
											2022-04-22 11:35:14 -07:00
										 |  |  |     totalUnseen, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type CachedConversationMessagesSelectorType = ( | 
					
						
							|  |  |  |   conversation: ConversationMessageType | 
					
						
							|  |  |  | ) => TimelinePropsType; | 
					
						
							|  |  |  | export const getCachedSelectorForConversationMessages = createSelector( | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							|  |  |  |   getUserNumber, | 
					
						
							|  |  |  |   (): CachedConversationMessagesSelectorType => { | 
					
						
							|  |  |  |     // Note: memoizee will check all parameters provided, and only run our selector
 | 
					
						
							|  |  |  |     //   if any of them have changed.
 | 
					
						
							|  |  |  |     return memoizee(_conversationMessagesSelector, { max: 50 }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getConversationMessagesSelector = createSelector( | 
					
						
							|  |  |  |   getCachedSelectorForConversationMessages, | 
					
						
							|  |  |  |   getMessagesByConversation, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     conversationMessagesSelector: CachedConversationMessagesSelectorType, | 
					
						
							|  |  |  |     messagesByConversation: MessagesByConversationType | 
					
						
							|  |  |  |   ) => { | 
					
						
							| 
									
										
										
										
											2021-09-12 19:36:41 -07:00
										 |  |  |     return (id: string): TimelinePropsType => { | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       const conversation = messagesByConversation[id]; | 
					
						
							|  |  |  |       if (!conversation) { | 
					
						
							| 
									
										
										
										
											2021-09-12 19:36:41 -07:00
										 |  |  |         // TODO: DESKTOP-2340
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           haveNewest: false, | 
					
						
							|  |  |  |           haveOldest: false, | 
					
						
							| 
									
										
										
										
											2022-05-11 15:41:45 -07:00
										 |  |  |           messageChangeCounter: 0, | 
					
						
							| 
									
										
										
										
											2022-03-11 16:31:21 -06:00
										 |  |  |           messageLoadingState: TimelineMessageLoadingState.DoingInitialLoad, | 
					
						
							| 
									
										
										
										
											2021-09-12 19:36:41 -07:00
										 |  |  |           scrollToIndexCounter: 0, | 
					
						
							| 
									
										
										
										
											2022-04-22 11:35:14 -07:00
										 |  |  |           totalUnseen: 0, | 
					
						
							| 
									
										
										
										
											2021-11-30 03:25:24 -08:00
										 |  |  |           items: [], | 
					
						
							| 
									
										
										
										
											2021-09-12 19:36:41 -07:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return conversationMessagesSelector(conversation); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getInvitedContactsForNewlyCreatedGroup = createSelector( | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   getConversationsByUuid, | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |   getConversations, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     conversationLookup, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     { invitedUuidsForNewlyCreatedGroup = [] } | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  |   ): Array<ConversationType> => | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     deconstructLookup(conversationLookup, invitedUuidsForNewlyCreatedGroup) | 
					
						
							| 
									
										
										
										
											2021-03-03 14:09:58 -06:00
										 |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-05-28 12:15:17 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getConversationsWithCustomColorSelector = createSelector( | 
					
						
							|  |  |  |   getAllConversations, | 
					
						
							|  |  |  |   conversations => { | 
					
						
							|  |  |  |     return (colorId: string): Array<ConversationType> => { | 
					
						
							|  |  |  |       return conversations.filter( | 
					
						
							|  |  |  |         conversation => conversation.customColorId === colorId | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function isMissingRequiredProfileSharing( | 
					
						
							|  |  |  |   conversation: ConversationType | 
					
						
							|  |  |  | ): boolean { | 
					
						
							| 
									
										
										
										
											2021-09-02 16:29:46 -07:00
										 |  |  |   const doesConversationRequireIt = | 
					
						
							| 
									
										
										
										
											2022-02-11 17:04:07 -08:00
										 |  |  |     !conversation.isMe && | 
					
						
							| 
									
										
										
										
											2021-09-02 16:29:46 -07:00
										 |  |  |     !conversation.left && | 
					
						
							|  |  |  |     (isGroupV1(conversation) || isDirectConversation(conversation)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  |   return Boolean( | 
					
						
							| 
									
										
										
										
											2021-09-02 16:29:46 -07:00
										 |  |  |     doesConversationRequireIt && | 
					
						
							|  |  |  |       !conversation.profileSharing && | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  |       window.Signal.RemoteConfig.isEnabled('desktop.mandatoryProfileSharing') && | 
					
						
							| 
									
										
										
										
											2022-12-22 16:13:23 -08:00
										 |  |  |       conversation.hasMessages | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-07-20 16:18:35 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getGroupAdminsSelector = createSelector( | 
					
						
							|  |  |  |   getConversationSelector, | 
					
						
							|  |  |  |   (conversationSelector: GetConversationByIdType) => { | 
					
						
							|  |  |  |     return (conversationId: string): Array<ConversationType> => { | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |       const { | 
					
						
							|  |  |  |         groupId, | 
					
						
							|  |  |  |         groupVersion, | 
					
						
							|  |  |  |         memberships = [], | 
					
						
							|  |  |  |       } = conversationSelector(conversationId); | 
					
						
							| 
									
										
										
										
											2021-07-20 16:18:35 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if ( | 
					
						
							|  |  |  |         !isGroupV2({ | 
					
						
							|  |  |  |           groupId, | 
					
						
							|  |  |  |           groupVersion, | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       ) { | 
					
						
							|  |  |  |         return []; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const admins: Array<ConversationType> = []; | 
					
						
							|  |  |  |       memberships.forEach(membership => { | 
					
						
							|  |  |  |         if (membership.isAdmin) { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |           const admin = conversationSelector(membership.uuid); | 
					
						
							| 
									
										
										
										
											2021-07-20 16:18:35 -04:00
										 |  |  |           admins.push(admin); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       return admins; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 10:24:52 -06:00
										 |  |  | export const getContactSelector = createSelector( | 
					
						
							|  |  |  |   getConversationSelector, | 
					
						
							|  |  |  |   conversationSelector => { | 
					
						
							|  |  |  |     return (contactId: string) => | 
					
						
							|  |  |  |       pick(conversationSelector(contactId), 'id', 'title', 'uuid'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 20:10:30 -08:00
										 |  |  | export const getConversationVerificationData = createSelector( | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |   getConversations, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     conversations: Readonly<ConversationsStateType> | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |   ): Record<string, ConversationVerificationData> => | 
					
						
							|  |  |  |     conversations.verificationDataByConversation | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  | export const getConversationIdsStoppedForVerification = createSelector( | 
					
						
							|  |  |  |   getConversationVerificationData, | 
					
						
							|  |  |  |   (verificationDataByConversation): Array<string> => | 
					
						
							|  |  |  |     Object.keys(verificationDataByConversation) | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-31 12:46:56 -07:00
										 |  |  | export const getConversationUuidsStoppingSend = createSelector( | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |   getConversationVerificationData, | 
					
						
							|  |  |  |   (pendingData): Array<string> => { | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |     const result = new Set<string>(); | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |     Object.values(pendingData).forEach(item => { | 
					
						
							|  |  |  |       if (item.type === ConversationVerificationState.PendingVerification) { | 
					
						
							| 
									
										
										
										
											2022-05-31 12:46:56 -07:00
										 |  |  |         item.uuidsNeedingVerification.forEach(conversationId => { | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |           result.add(conversationId); | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2022-11-10 20:10:30 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (item.byDistributionId) { | 
					
						
							|  |  |  |           Object.values(item.byDistributionId).forEach(distribution => { | 
					
						
							|  |  |  |             distribution.uuidsNeedingVerification.forEach(conversationId => { | 
					
						
							|  |  |  |               result.add(conversationId); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |     }); | 
					
						
							|  |  |  |     return Array.from(result); | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  | export const getConversationsStoppingSend = createSelector( | 
					
						
							| 
									
										
										
										
											2022-05-31 12:46:56 -07:00
										 |  |  |   getConversationSelector, | 
					
						
							|  |  |  |   getConversationUuidsStoppingSend, | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |   ( | 
					
						
							| 
									
										
										
										
											2022-05-31 12:46:56 -07:00
										 |  |  |     conversationSelector: GetConversationByIdType, | 
					
						
							|  |  |  |     uuids: ReadonlyArray<string> | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |   ): Array<ConversationType> => { | 
					
						
							| 
									
										
										
										
											2022-05-31 12:46:56 -07:00
										 |  |  |     const conversations = uuids.map(uuid => conversationSelector(uuid)); | 
					
						
							| 
									
										
										
										
											2022-02-16 10:36:21 -08:00
										 |  |  |     return sortByTitle(conversations); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | ); | 
					
						
							| 
									
										
										
										
											2022-09-21 20:55:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getHideStoryConversationIds = createSelector( | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   (conversationLookup): Array<string> => | 
					
						
							|  |  |  |     Object.keys(conversationLookup).filter( | 
					
						
							|  |  |  |       conversationId => conversationLookup[conversationId].hideStory | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2022-12-14 13:41:04 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-26 18:23:32 -04:00
										 |  |  | export const getActivePanel = createSelector( | 
					
						
							| 
									
										
										
										
											2022-12-14 13:41:04 -05:00
										 |  |  |   getConversations, | 
					
						
							|  |  |  |   (conversations): PanelRenderType | undefined => | 
					
						
							| 
									
										
										
										
											2023-07-26 18:23:32 -04:00
										 |  |  |     conversations.targetedConversationPanels.stack[ | 
					
						
							|  |  |  |       conversations.targetedConversationPanels.watermark | 
					
						
							| 
									
										
										
										
											2022-12-14 13:41:04 -05:00
										 |  |  |     ] | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-26 18:23:32 -04:00
										 |  |  | type PanelInformationType = { | 
					
						
							|  |  |  |   currPanel: PanelRenderType | undefined; | 
					
						
							|  |  |  |   direction: 'push' | 'pop'; | 
					
						
							|  |  |  |   prevPanel: PanelRenderType | undefined; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getPanelInformation = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   getActivePanel, | 
					
						
							|  |  |  |   (conversations, currPanel): PanelInformationType | undefined => { | 
					
						
							|  |  |  |     const { direction, watermark } = conversations.targetedConversationPanels; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!direction) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const watermarkDirection = | 
					
						
							|  |  |  |       direction === 'push' ? watermark - 1 : watermark + 1; | 
					
						
							|  |  |  |     const prevPanel = | 
					
						
							|  |  |  |       conversations.targetedConversationPanels.stack[watermarkDirection]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       currPanel, | 
					
						
							|  |  |  |       direction, | 
					
						
							|  |  |  |       prevPanel, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getIsPanelAnimating = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (conversations): boolean => { | 
					
						
							|  |  |  |     return conversations.targetedConversationPanels.isAnimating; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-14 13:41:04 -05:00
										 |  |  | export const getConversationTitle = createSelector( | 
					
						
							|  |  |  |   getIntl, | 
					
						
							| 
									
										
										
										
											2023-07-26 18:23:32 -04:00
										 |  |  |   getActivePanel, | 
					
						
							| 
									
										
										
										
											2022-12-14 13:41:04 -05:00
										 |  |  |   (i18n, panel): string | undefined => | 
					
						
							|  |  |  |     getConversationTitleForPanelType(i18n, panel?.type) | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2023-05-11 20:27:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getLastEditableMessageId = createSelector( | 
					
						
							|  |  |  |   getConversationMessages, | 
					
						
							|  |  |  |   getMessages, | 
					
						
							|  |  |  |   (conversationMessages, messagesLookup): string | undefined => { | 
					
						
							|  |  |  |     if (!conversationMessages) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (let i = conversationMessages.messageIds.length - 1; i >= 0; i -= 1) { | 
					
						
							|  |  |  |       const messageId = conversationMessages.messageIds[i]; | 
					
						
							|  |  |  |       const message = messagesLookup[messageId]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!message) { | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (isOutgoing(message)) { | 
					
						
							|  |  |  |         return canEditMessage(message) ? message.id : undefined; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); |