You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			149 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			149 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
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,
 | 
						|
  };
 | 
						|
};
 |