|  |  |  | import moment from 'moment'; | 
					
						
							|  |  |  | import { compact, groupBy, sortBy } from 'lodash'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { Message } from './types/Message'; | 
					
						
							|  |  |  | // import { missingCaseError } from '../../../util/missingCaseError';
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type StaticSectionType = 'today' | 'yesterday' | 'thisWeek' | 'thisMonth'; | 
					
						
							|  |  |  | type YearMonthSectionType = 'yearMonth'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface GenericSection<T> { | 
					
						
							|  |  |  |   type: T; | 
					
						
							|  |  |  |   messages: Array<Message>; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | type StaticSection = GenericSection<StaticSectionType>; | 
					
						
							|  |  |  | type YearMonthSection = GenericSection<YearMonthSectionType> & { | 
					
						
							|  |  |  |   year: number; | 
					
						
							|  |  |  |   month: number; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | export type Section = StaticSection | YearMonthSection; | 
					
						
							|  |  |  | export const groupMessagesByDate = ( | 
					
						
							|  |  |  |   timestamp: number, | 
					
						
							|  |  |  |   messages: Array<Message> | 
					
						
							|  |  |  | ): Array<Section> => { | 
					
						
							|  |  |  |   const referenceDateTime = moment.utc(timestamp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const sortedMessages = sortBy(messages, message => -message.received_at); | 
					
						
							|  |  |  |   const messagesWithSection = sortedMessages.map( | 
					
						
							|  |  |  |     withSection(referenceDateTime) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   const groupedMessages = groupBy(messagesWithSection, 'type'); | 
					
						
							|  |  |  |   const yearMonthMessages = Object.values( | 
					
						
							|  |  |  |     groupBy(groupedMessages.yearMonth, 'order') | 
					
						
							|  |  |  |   ).reverse(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return compact([ | 
					
						
							|  |  |  |     toSection(groupedMessages.today), | 
					
						
							|  |  |  |     toSection(groupedMessages.yesterday), | 
					
						
							|  |  |  |     toSection(groupedMessages.thisWeek), | 
					
						
							|  |  |  |     toSection(groupedMessages.thisMonth), | 
					
						
							|  |  |  |     ...yearMonthMessages.map(toSection), | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const toSection = ( | 
					
						
							|  |  |  |   messagesWithSection: Array<MessageWithSection> | undefined | 
					
						
							|  |  |  | ): Section | null => { | 
					
						
							|  |  |  |   if (!messagesWithSection || messagesWithSection.length === 0) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const firstMessageWithSection: MessageWithSection = messagesWithSection[0]; | 
					
						
							|  |  |  |   if (!firstMessageWithSection) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const messages = messagesWithSection.map( | 
					
						
							|  |  |  |     messageWithSection => messageWithSection.message | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   switch (firstMessageWithSection.type) { | 
					
						
							|  |  |  |     case 'today': | 
					
						
							|  |  |  |     case 'yesterday': | 
					
						
							|  |  |  |     case 'thisWeek': | 
					
						
							|  |  |  |     case 'thisMonth': | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         type: firstMessageWithSection.type, | 
					
						
							|  |  |  |         messages, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     case 'yearMonth': | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         type: firstMessageWithSection.type, | 
					
						
							|  |  |  |         year: firstMessageWithSection.year, | 
					
						
							|  |  |  |         month: firstMessageWithSection.month, | 
					
						
							|  |  |  |         messages, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       // NOTE: Investigate why we get the following error:
 | 
					
						
							|  |  |  |       // error TS2345: Argument of type 'any' is not assignable to parameter
 | 
					
						
							|  |  |  |       // of type 'never'.
 | 
					
						
							|  |  |  |       // return missingCaseError(firstMessageWithSection.type);
 | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface GenericMessageWithSection<T> { | 
					
						
							|  |  |  |   order: number; | 
					
						
							|  |  |  |   type: T; | 
					
						
							|  |  |  |   message: Message; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | type MessageWithStaticSection = GenericMessageWithSection<StaticSectionType>; | 
					
						
							|  |  |  | type MessageWithYearMonthSection = GenericMessageWithSection< | 
					
						
							|  |  |  |   YearMonthSectionType | 
					
						
							|  |  |  | > & { | 
					
						
							|  |  |  |   year: number; | 
					
						
							|  |  |  |   month: number; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | type MessageWithSection = | 
					
						
							|  |  |  |   | MessageWithStaticSection | 
					
						
							|  |  |  |   | MessageWithYearMonthSection; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const withSection = (referenceDateTime: moment.Moment) => ( | 
					
						
							|  |  |  |   message: Message | 
					
						
							|  |  |  | ): MessageWithSection => { | 
					
						
							|  |  |  |   const today = moment(referenceDateTime).startOf('day'); | 
					
						
							|  |  |  |   const yesterday = moment(referenceDateTime) | 
					
						
							|  |  |  |     .subtract(1, 'day') | 
					
						
							|  |  |  |     .startOf('day'); | 
					
						
							|  |  |  |   const thisWeek = moment(referenceDateTime).startOf('isoWeek'); | 
					
						
							|  |  |  |   const thisMonth = moment(referenceDateTime).startOf('month'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const messageReceivedDate = moment.utc(message.received_at); | 
					
						
							|  |  |  |   if (messageReceivedDate.isAfter(today)) { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       order: 0, | 
					
						
							|  |  |  |       type: 'today', | 
					
						
							|  |  |  |       message, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (messageReceivedDate.isAfter(yesterday)) { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       order: 1, | 
					
						
							|  |  |  |       type: 'yesterday', | 
					
						
							|  |  |  |       message, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (messageReceivedDate.isAfter(thisWeek)) { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       order: 2, | 
					
						
							|  |  |  |       type: 'thisWeek', | 
					
						
							|  |  |  |       message, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (messageReceivedDate.isAfter(thisMonth)) { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       order: 3, | 
					
						
							|  |  |  |       type: 'thisMonth', | 
					
						
							|  |  |  |       message, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const month: number = messageReceivedDate.month(); | 
					
						
							|  |  |  |   const year: number = messageReceivedDate.year(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     order: year * 100 + month, | 
					
						
							|  |  |  |     type: 'yearMonth', | 
					
						
							|  |  |  |     month, | 
					
						
							|  |  |  |     year, | 
					
						
							|  |  |  |     message, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; |