Signal-Desktop/ts/components/conversation/media-gallery/groupMediaItemsByDate.ts

164 lines
4.3 KiB
TypeScript

// Copyright 2018-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import moment from 'moment';
import { compact, groupBy, sortBy } from 'lodash';
import { MediaItemType } from '../../LightboxGallery';
// import { missingCaseError } from '../../../util/missingCaseError';
type StaticSectionType = 'today' | 'yesterday' | 'thisWeek' | 'thisMonth';
type YearMonthSectionType = 'yearMonth';
interface GenericSection<T> {
type: T;
mediaItems: Array<MediaItemType>;
}
type StaticSection = GenericSection<StaticSectionType>;
type YearMonthSection = GenericSection<YearMonthSectionType> & {
year: number;
month: number;
};
export type Section = StaticSection | YearMonthSection;
export const groupMediaItemsByDate = (
timestamp: number,
mediaItems: Array<MediaItemType>
): Array<Section> => {
const referenceDateTime = moment.utc(timestamp);
const sortedMediaItem = sortBy(mediaItems, mediaItem => {
const { message } = mediaItem;
return -message.received_at;
});
const messagesWithSection = sortedMediaItem.map(
withSection(referenceDateTime)
);
const groupedMediaItem = groupBy(messagesWithSection, 'type');
const yearMonthMediaItem = Object.values(
groupBy(groupedMediaItem.yearMonth, 'order')
).reverse();
return compact([
toSection(groupedMediaItem.today),
toSection(groupedMediaItem.yesterday),
toSection(groupedMediaItem.thisWeek),
toSection(groupedMediaItem.thisMonth),
...yearMonthMediaItem.map(toSection),
]);
};
const toSection = (
messagesWithSection: Array<MediaItemWithSection> | undefined
): Section | undefined => {
if (!messagesWithSection || messagesWithSection.length === 0) {
return;
}
const firstMediaItemWithSection: MediaItemWithSection =
messagesWithSection[0];
if (!firstMediaItemWithSection) {
return;
}
const mediaItems = messagesWithSection.map(
messageWithSection => messageWithSection.mediaItem
);
switch (firstMediaItemWithSection.type) {
case 'today':
case 'yesterday':
case 'thisWeek':
case 'thisMonth':
// eslint-disable-next-line consistent-return
return {
type: firstMediaItemWithSection.type,
mediaItems,
};
case 'yearMonth':
// eslint-disable-next-line consistent-return
return {
type: firstMediaItemWithSection.type,
year: firstMediaItemWithSection.year,
month: firstMediaItemWithSection.month,
mediaItems,
};
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(firstMediaItemWithSection.type);
// eslint-disable-next-line no-useless-return
return;
}
};
interface GenericMediaItemWithSection<T> {
order: number;
type: T;
mediaItem: MediaItemType;
}
type MediaItemWithStaticSection = GenericMediaItemWithSection<
StaticSectionType
>;
type MediaItemWithYearMonthSection = GenericMediaItemWithSection<
YearMonthSectionType
> & {
year: number;
month: number;
};
type MediaItemWithSection =
| MediaItemWithStaticSection
| MediaItemWithYearMonthSection;
const withSection = (referenceDateTime: moment.Moment) => (
mediaItem: MediaItemType
): MediaItemWithSection => {
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 { message } = mediaItem;
const mediaItemReceivedDate = moment.utc(message.received_at);
if (mediaItemReceivedDate.isAfter(today)) {
return {
order: 0,
type: 'today',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(yesterday)) {
return {
order: 1,
type: 'yesterday',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(thisWeek)) {
return {
order: 2,
type: 'thisWeek',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(thisMonth)) {
return {
order: 3,
type: 'thisMonth',
mediaItem,
};
}
const month: number = mediaItemReceivedDate.month();
const year: number = mediaItemReceivedDate.year();
return {
order: year * 100 + month,
type: 'yearMonth',
month,
year,
mediaItem,
};
};