signal-desktop/ts/components/conversation/media-gallery/groupMediaItemsByDate.ts
2021-11-11 16:43:05 -06:00

159 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 * as log from '../../../logging/log';
import type { MediaItemType } from '../../../types/MediaItem';
import { getMessageTimestamp } from '../../../util/getMessageTimestamp';
import { missingCaseError } from '../../../util/missingCaseError';
type StaticSectionType = 'today' | 'yesterday' | 'thisWeek' | 'thisMonth';
type YearMonthSectionType = 'yearMonth';
type 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 undefined;
}
const firstMediaItemWithSection: undefined | MediaItemWithSection =
messagesWithSection[0];
if (!firstMediaItemWithSection) {
return undefined;
}
const mediaItems = messagesWithSection.map(
messageWithSection => messageWithSection.mediaItem
);
switch (firstMediaItemWithSection.type) {
case 'today':
case 'yesterday':
case 'thisWeek':
case 'thisMonth':
return {
type: firstMediaItemWithSection.type,
mediaItems,
};
case 'yearMonth':
return {
type: firstMediaItemWithSection.type,
year: firstMediaItemWithSection.year,
month: firstMediaItemWithSection.month,
mediaItems,
};
default:
log.error(missingCaseError(firstMediaItemWithSection));
return undefined;
}
};
type 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(getMessageTimestamp(message));
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,
};
};