| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | import memoizee from 'memoizee'; | 
					
						
							| 
									
										
										
										
											2019-08-23 12:56:49 -07:00
										 |  |  | import { fromPairs, isNumber } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | import { createSelector } from 'reselect'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { StateType } from '../reducer'; | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   ConversationLookupType, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   ConversationMessageType, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   ConversationsStateType, | 
					
						
							|  |  |  |   ConversationType, | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   MessageLookupType, | 
					
						
							|  |  |  |   MessagesByConversationType, | 
					
						
							|  |  |  |   MessageType, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | } from '../ducks/conversations'; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | import { getBubbleProps } from '../../shims/Whisper'; | 
					
						
							|  |  |  | import { PropsDataType as TimelinePropsType } from '../../components/conversation/Timeline'; | 
					
						
							|  |  |  | import { TimelineItemType } from '../../components/conversation/TimelineItem'; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  | import { | 
					
						
							|  |  |  |   getInteractionMode, | 
					
						
							|  |  |  |   getIntl, | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   getUserConversationId, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |   getUserNumber, | 
					
						
							|  |  |  | } from './user'; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getConversations = (state: StateType): ConversationsStateType => | 
					
						
							|  |  |  |   state.conversations; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getConversationLookup = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): ConversationLookupType => { | 
					
						
							|  |  |  |     return state.conversationLookup; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getSelectedConversation = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): string | undefined => { | 
					
						
							|  |  |  |     return state.selectedConversation; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | type SelectedMessageType = { | 
					
						
							|  |  |  |   id: string; | 
					
						
							|  |  |  |   counter: number; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | export const getSelectedMessage = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): SelectedMessageType | undefined => { | 
					
						
							|  |  |  |     if (!state.selectedMessage) { | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |       return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       id: state.selectedMessage, | 
					
						
							|  |  |  |       counter: state.selectedMessageCounter, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | export const getShowArchived = createSelector( | 
					
						
							|  |  |  |   getConversations, | 
					
						
							|  |  |  |   (state: ConversationsStateType): boolean => { | 
					
						
							|  |  |  |     return Boolean(state.showArchived); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  |   selectedConversation?: string | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | ): { | 
					
						
							|  |  |  |   conversations: Array<ConversationType>; | 
					
						
							|  |  |  |   archivedConversations: Array<ConversationType>; | 
					
						
							|  |  |  | } => { | 
					
						
							|  |  |  |   const conversations: Array<ConversationType> = []; | 
					
						
							|  |  |  |   const archivedConversations: Array<ConversationType> = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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]; | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |     if (conversation.activeAt) { | 
					
						
							|  |  |  |       if (selectedConversation === conversation.id) { | 
					
						
							|  |  |  |         conversation = { | 
					
						
							|  |  |  |           ...conversation, | 
					
						
							|  |  |  |           isSelected: true, | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  |   return { conversations, archivedConversations }; | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  |   getSelectedConversation, | 
					
						
							| 
									
										
										
										
											2019-03-11 17:20:16 -07:00
										 |  |  |   _getLeftPaneLists | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getMe = createSelector( | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   [getConversationLookup, getUserConversationId], | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     lookup: ConversationLookupType, | 
					
						
							|  |  |  |     ourConversationId: string | 
					
						
							|  |  |  |   ): ConversationType => { | 
					
						
							|  |  |  |     return lookup[ourConversationId]; | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											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( | 
					
						
							|  |  |  |   conversation: ConversationType | 
					
						
							|  |  |  |   // regionCode: string,
 | 
					
						
							|  |  |  |   // userNumber: string
 | 
					
						
							|  |  |  | ): ConversationType { | 
					
						
							|  |  |  |   return conversation; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A little optimization to reset our selector cache when high-level application data
 | 
					
						
							|  |  |  | //   changes: regionCode and userNumber.
 | 
					
						
							|  |  |  | type CachedConversationSelectorType = ( | 
					
						
							|  |  |  |   conversation: ConversationType | 
					
						
							|  |  |  | ) => 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
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  | export type GetConversationByIdType = ( | 
					
						
							|  |  |  |   id: string | 
					
						
							|  |  |  | ) => ConversationType | undefined; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export const getConversationSelector = createSelector( | 
					
						
							|  |  |  |   getCachedSelectorForConversation, | 
					
						
							|  |  |  |   getConversationLookup, | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |     selector: CachedConversationSelectorType, | 
					
						
							|  |  |  |     lookup: ConversationLookupType | 
					
						
							|  |  |  |   ): GetConversationByIdType => { | 
					
						
							|  |  |  |     return (id: string) => { | 
					
						
							|  |  |  |       const conversation = lookup[id]; | 
					
						
							|  |  |  |       if (!conversation) { | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return selector(conversation); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | // For now we use a shim, as selector logic is still happening in the Backbone Model.
 | 
					
						
							|  |  |  | // What needs to happen to pull that selector logic here?
 | 
					
						
							|  |  |  | //   1) translate ~500 lines of selector logic into TypeScript
 | 
					
						
							|  |  |  | //   2) other places still rely on that prop-gen code - need to put these under Roots:
 | 
					
						
							|  |  |  | //     - quote compose
 | 
					
						
							|  |  |  | //     - message details
 | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export function _messageSelector( | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   message: MessageType, | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |   _ourNumber: string, | 
					
						
							|  |  |  |   _regionCode: string, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |   interactionMode: 'mouse' | 'keyboard', | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |   _conversation?: ConversationType, | 
					
						
							|  |  |  |   _author?: ConversationType, | 
					
						
							|  |  |  |   _quoted?: ConversationType, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   selectedMessageId?: string, | 
					
						
							|  |  |  |   selectedMessageCounter?: number | 
					
						
							|  |  |  | ): TimelineItemType { | 
					
						
							|  |  |  |   // Note: We don't use all of those parameters here, but the shim we call does.
 | 
					
						
							|  |  |  |   //   We want to call this function again if any of those parameters change.
 | 
					
						
							|  |  |  |   const props = getBubbleProps(message); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (selectedMessageId === message.id) { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       ...props, | 
					
						
							|  |  |  |       data: { | 
					
						
							|  |  |  |         ...props.data, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |         interactionMode, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |         isSelected: true, | 
					
						
							|  |  |  |         isSelectedCounter: selectedMessageCounter, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     ...props, | 
					
						
							|  |  |  |     data: { | 
					
						
							|  |  |  |       ...props.data, | 
					
						
							|  |  |  |       interactionMode, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A little optimization to reset our selector cache whenever high-level application data
 | 
					
						
							|  |  |  | //   changes: regionCode and userNumber.
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | type CachedMessageSelectorType = ( | 
					
						
							|  |  |  |   message: MessageType, | 
					
						
							|  |  |  |   ourNumber: string, | 
					
						
							|  |  |  |   regionCode: string, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |   interactionMode: 'mouse' | 'keyboard', | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   conversation?: ConversationType, | 
					
						
							|  |  |  |   author?: ConversationType, | 
					
						
							|  |  |  |   quoted?: ConversationType, | 
					
						
							|  |  |  |   selectedMessageId?: string, | 
					
						
							|  |  |  |   selectedMessageCounter?: number | 
					
						
							|  |  |  | ) => TimelineItemType; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export const getCachedSelectorForMessage = createSelector( | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							|  |  |  |   getUserNumber, | 
					
						
							|  |  |  |   (): CachedMessageSelectorType => { | 
					
						
							| 
									
										
										
										
											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(_messageSelector, { max: 2000 }); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | type GetMessageByIdType = (id: string) => TimelineItemType | undefined; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export const getMessageSelector = createSelector( | 
					
						
							|  |  |  |   getCachedSelectorForMessage, | 
					
						
							|  |  |  |   getMessages, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   getSelectedMessage, | 
					
						
							|  |  |  |   getConversationSelector, | 
					
						
							|  |  |  |   getRegionCode, | 
					
						
							|  |  |  |   getUserNumber, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |   getInteractionMode, | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   ( | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     messageSelector: CachedMessageSelectorType, | 
					
						
							|  |  |  |     messageLookup: MessageLookupType, | 
					
						
							|  |  |  |     selectedMessage: SelectedMessageType | undefined, | 
					
						
							|  |  |  |     conversationSelector: GetConversationByIdType, | 
					
						
							|  |  |  |     regionCode: string, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |     ourNumber: string, | 
					
						
							|  |  |  |     interactionMode: 'keyboard' | 'mouse' | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |   ): GetMessageByIdType => { | 
					
						
							|  |  |  |     return (id: string) => { | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       const message = messageLookup[id]; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |       if (!message) { | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       const { conversationId, source, type, quote } = message; | 
					
						
							|  |  |  |       const conversation = conversationSelector(conversationId); | 
					
						
							|  |  |  |       let author: ConversationType | undefined; | 
					
						
							|  |  |  |       let quoted: ConversationType | undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (type === 'incoming') { | 
					
						
							|  |  |  |         author = conversationSelector(source); | 
					
						
							|  |  |  |       } else if (type === 'outgoing') { | 
					
						
							|  |  |  |         author = conversationSelector(ourNumber); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (quote) { | 
					
						
							|  |  |  |         quoted = conversationSelector(quote.author); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return messageSelector( | 
					
						
							|  |  |  |         message, | 
					
						
							|  |  |  |         ourNumber, | 
					
						
							|  |  |  |         regionCode, | 
					
						
							| 
									
										
										
										
											2019-11-21 11:16:06 -08:00
										 |  |  |         interactionMode, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |         conversation, | 
					
						
							|  |  |  |         author, | 
					
						
							|  |  |  |         quoted, | 
					
						
							|  |  |  |         selectedMessage ? selectedMessage.id : undefined, | 
					
						
							|  |  |  |         selectedMessage ? selectedMessage.counter : undefined | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function _conversationMessagesSelector( | 
					
						
							|  |  |  |   conversation: ConversationMessageType | 
					
						
							|  |  |  | ): TimelinePropsType { | 
					
						
							|  |  |  |   const { | 
					
						
							|  |  |  |     heightChangeMessageIds, | 
					
						
							|  |  |  |     isLoadingMessages, | 
					
						
							| 
									
										
										
										
											2019-09-03 13:06:17 -07:00
										 |  |  |     isNearBottom, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     loadCountdownStart, | 
					
						
							|  |  |  |     messageIds, | 
					
						
							|  |  |  |     metrics, | 
					
						
							|  |  |  |     resetCounter, | 
					
						
							|  |  |  |     scrollToMessageId, | 
					
						
							|  |  |  |     scrollToMessageCounter, | 
					
						
							|  |  |  |   } = conversation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const firstId = messageIds[0]; | 
					
						
							|  |  |  |   const lastId = | 
					
						
							|  |  |  |     messageIds.length === 0 ? undefined : messageIds[messageIds.length - 1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { oldestUnread } = metrics; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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
										 |  |  | 
 | 
					
						
							|  |  |  |   const messageHeightChangeLookup = | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     heightChangeMessageIds && heightChangeMessageIds.length | 
					
						
							| 
									
										
										
										
											2019-08-23 12:56:49 -07:00
										 |  |  |       ? fromPairs(heightChangeMessageIds.map(id => [id, true])) | 
					
						
							|  |  |  |       : null; | 
					
						
							|  |  |  |   const messageHeightChangeIndex = messageHeightChangeLookup | 
					
						
							|  |  |  |     ? messageIds.findIndex(id => messageHeightChangeLookup[id]) | 
					
						
							|  |  |  |     : undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const oldestUnreadIndex = oldestUnread | 
					
						
							|  |  |  |     ? messageIds.findIndex(id => id === oldestUnread.id) | 
					
						
							|  |  |  |     : undefined; | 
					
						
							|  |  |  |   const scrollToIndex = scrollToMessageId | 
					
						
							|  |  |  |     ? messageIds.findIndex(id => id === scrollToMessageId) | 
					
						
							|  |  |  |     : undefined; | 
					
						
							|  |  |  |   const { totalUnread } = metrics; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     haveNewest, | 
					
						
							|  |  |  |     haveOldest, | 
					
						
							|  |  |  |     isLoadingMessages, | 
					
						
							|  |  |  |     loadCountdownStart, | 
					
						
							|  |  |  |     items, | 
					
						
							| 
									
										
										
										
											2019-09-03 13:06:17 -07:00
										 |  |  |     isNearBottom, | 
					
						
							| 
									
										
										
										
											2019-08-23 12:56:49 -07:00
										 |  |  |     messageHeightChangeIndex: | 
					
						
							|  |  |  |       isNumber(messageHeightChangeIndex) && messageHeightChangeIndex >= 0 | 
					
						
							|  |  |  |         ? messageHeightChangeIndex | 
					
						
							|  |  |  |         : undefined, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     oldestUnreadIndex: | 
					
						
							| 
									
										
										
										
											2019-08-15 07:59:56 -07:00
										 |  |  |       isNumber(oldestUnreadIndex) && oldestUnreadIndex >= 0 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |         ? oldestUnreadIndex | 
					
						
							|  |  |  |         : undefined, | 
					
						
							|  |  |  |     resetCounter, | 
					
						
							|  |  |  |     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, | 
					
						
							|  |  |  |     totalUnread, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  |   ) => { | 
					
						
							|  |  |  |     return (id: string): TimelinePropsType | undefined => { | 
					
						
							|  |  |  |       const conversation = messagesByConversation[id]; | 
					
						
							|  |  |  |       if (!conversation) { | 
					
						
							| 
									
										
										
										
											2020-09-14 14:56:35 -07:00
										 |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return conversationMessagesSelector(conversation); | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); |