Implementing grouping messages by date

pull/1/head
Daniel Gasienica 7 years ago
parent dea56c0008
commit b0fefdbb98

@ -1,14 +1,16 @@
```jsx
const YEAR_MS = 1 * 12 * 30 * 24 * 60 * 60 * 1000;
const DAY_MS = 24 * 60 * 60 * 1000;
const MONTH_MS = 30 * DAY_MS;
const YEAR_MS = 12 * MONTH_MS;
const tokens = ['foo', 'bar', 'baz', 'qux', 'quux'];
const fileExtensions = ['docx', 'pdf', 'txt', 'mp3', 'wmv', 'tiff'];
const createRandomMessage = (props) => {
const createRandomMessage = ({startTime, timeWindow} = {}) => (props) => {
const now = Date.now();
const fileName =
`${_.sample(tokens)}${_.sample(tokens)}.${_.sample(fileExtensions)}`;
return {
id: _.random(now).toString(),
received_at: _.random(now - YEAR_MS, now),
received_at: _.random(startTime, startTime + timeWindow),
attachments: [{
data: null,
fileName,
@ -21,9 +23,34 @@ const createRandomMessage = (props) => {
};
};
const createRandomMessages = ({startTime, timeWindow}) =>
_.range(_.random(5, 10)).map(createRandomMessage({startTime, timeWindow}));
const startTime = Date.now();
const messages = _.sortBy(
_.range(25).map(createRandomMessage),
[
...createRandomMessages({
startTime,
timeWindow: DAY_MS,
}),
...createRandomMessages({
startTime: startTime - DAY_MS,
timeWindow: DAY_MS,
}),
...createRandomMessages({
startTime: startTime - 3 * DAY_MS,
timeWindow: 3 * DAY_MS,
}),
...createRandomMessages({
startTime: startTime - 30 * DAY_MS,
timeWindow: 15 * DAY_MS,
}),
...createRandomMessages({
startTime: startTime - 365 * DAY_MS,
timeWindow: 300 * DAY_MS,
}),
],
message => -message.received_at
);

@ -4,7 +4,6 @@
import React from 'react';
import moment from 'moment';
import { map } from 'lodash';
import { AttachmentListSection } from './AttachmentListSection';
import { groupMessagesByDate } from './groupMessagesByDate';
@ -123,23 +122,21 @@ export class MediaGallery extends React.Component<Props, State> {
}
const now = Date.now();
const groups = groupMessagesByDate(now, messages);
return map(groups, (annotations) => {
const first = annotations[0];
const date = moment(first.message.received_at);
const header = first.label === 'yearMonth'
? date.format(MONTH_FORMAT)
: i18n(first.label);
const groupMessages = map(annotations, 'message');
const sections = groupMessagesByDate(now, messages);
return sections.map(section => {
const first = section.messages[0];
const date = moment(first.received_at);
const header =
section.type === 'yearMonth'
? date.format(MONTH_FORMAT)
: i18n(section.type);
return (
<AttachmentListSection
key={header}
header={header}
i18n={i18n}
type={type}
messages={groupMessages}
messages={section.messages}
/>
);
});

@ -5,11 +5,25 @@ import moment from 'moment';
import { compact, groupBy, sortBy } from 'lodash';
import { Message } from './propTypes/Message';
// import { missingCaseError } from '../../../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>
): any => {
): Array<Section> => {
const referenceDateTime = moment.utc(timestamp);
const today = moment(referenceDateTime).startOf('day');
const yesterday = moment(referenceDateTime)
@ -18,42 +32,132 @@ export const groupMessagesByDate = (
const thisWeek = moment(referenceDateTime).startOf('isoWeek');
const thisMonth = moment(referenceDateTime).startOf('month');
const sorted = sortBy(messages, message => -message.received_at);
const annotations = sorted.map(message => {
const date = moment.utc(message.received_at);
const sortedMessages = sortBy(messages, message => -message.received_at);
const messagesWithSection = sortedMessages.map(
withSection({
today,
yesterday,
thisWeek,
thisMonth,
})
);
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(group => toSection(group)),
]);
};
if (date.isAfter(today)) {
return {
order: 0,
label: 'today',
message,
};
} else if (date.isAfter(yesterday)) {
return {
order: 1,
label: 'yesterday',
message,
};
} else if (date.isAfter(thisWeek)) {
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 {
order: 2,
label: 'thisWeek',
message,
type: firstMessageWithSection.type,
messages: messages,
};
} else if (date.isAfter(thisMonth)) {
case 'yearMonth':
return {
order: 3,
label: 'thisMonth',
message,
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;
}
};
type 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 = ({
today,
yesterday,
thisWeek,
thisMonth,
}: {
today: moment.Moment;
yesterday: moment.Moment;
thisWeek: moment.Moment;
thisMonth: moment.Moment;
}) => (message: Message): MessageWithSection => {
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: date.year() * 100 + date.month(),
label: 'yearMonth',
order: 3,
type: 'thisMonth',
message,
};
});
}
return groupBy(annotations, 'label');
const month: number = messageReceivedDate.month();
const year: number = messageReceivedDate.year();
return {
order: year * 100 + month,
type: 'yearMonth',
month,
year,
message,
};
};

@ -2,9 +2,14 @@
* @prettier
*/
import 'mocha';
import { assert } from 'chai';
import { shuffle } from 'lodash';
import { groupMessagesByDate } from '../../../components/conversation/media-gallery/groupMessagesByDate';
import {
groupMessagesByDate,
Section,
} from '../../../components/conversation/media-gallery/groupMessagesByDate';
import { Message } from '../../../components/conversation/media-gallery/propTypes/Message';
const toMessage = (date: Date): Message => ({
@ -16,7 +21,7 @@ const toMessage = (date: Date): Message => ({
describe('groupMessagesByDate', () => {
it('should group messages', () => {
const referenceTime = new Date('2018-04-12T18:00Z').getTime(); // Thu
const input: Array<Message> = [
const input: Array<Message> = shuffle([
// Today
toMessage(new Date('2018-04-12T12:00Z')), // Thu
toMessage(new Date('2018-04-12T00:01Z')), // Thu
@ -32,110 +37,94 @@ describe('groupMessagesByDate', () => {
// February 2011
toMessage(new Date('2011-02-28T23:59Z')),
toMessage(new Date('2011-02-01T10:00Z')),
];
]);
const expected = {
today: [
{
order: 0,
label: 'today',
message: {
const expected: Array<Section> = [
{
type: 'today',
messages: [
{
id: 'Thu, 12 Apr 2018 12:00:00 GMT',
received_at: 1523534400000,
attachments: [],
},
},
{
order: 0,
label: 'today',
message: {
{
id: 'Thu, 12 Apr 2018 00:01:00 GMT',
received_at: 1523491260000,
attachments: [],
},
},
],
yesterday: [
{
order: 1,
label: 'yesterday',
message: {
],
},
{
type: 'yesterday',
messages: [
{
id: 'Wed, 11 Apr 2018 23:59:00 GMT',
received_at: 1523491140000,
attachments: [],
},
},
],
thisWeek: [
{
order: 2,
label: 'thisWeek',
message: {
],
},
{
type: 'thisWeek',
messages: [
{
id: 'Mon, 09 Apr 2018 00:01:00 GMT',
received_at: 1523232060000,
attachments: [],
},
},
],
thisMonth: [
{
order: 3,
label: 'thisMonth',
message: {
],
},
{
type: 'thisMonth',
messages: [
{
id: 'Sun, 08 Apr 2018 23:59:00 GMT',
received_at: 1523231940000,
attachments: [],
},
},
{
order: 3,
label: 'thisMonth',
message: {
{
id: 'Sun, 01 Apr 2018 00:01:00 GMT',
received_at: 1522540860000,
attachments: [],
},
},
],
yearMonth: [
{
order: 201802,
label: 'yearMonth',
message: {
],
},
{
type: 'yearMonth',
year: 2018,
month: 2,
messages: [
{
id: 'Sat, 31 Mar 2018 23:59:00 GMT',
received_at: 1522540740000,
attachments: [],
},
},
{
order: 201802,
label: 'yearMonth',
message: {
{
id: 'Thu, 01 Mar 2018 14:00:00 GMT',
received_at: 1519912800000,
attachments: [],
},
},
{
order: 201101,
label: 'yearMonth',
message: {
],
},
{
type: 'yearMonth',
year: 2011,
month: 1,
messages: [
{
id: 'Mon, 28 Feb 2011 23:59:00 GMT',
received_at: 1298937540000,
attachments: [],
},
},
{
order: 201101,
label: 'yearMonth',
message: {
{
id: 'Tue, 01 Feb 2011 10:00:00 GMT',
received_at: 1296554400000,
attachments: [],
},
},
],
};
],
},
];
const actual = groupMessagesByDate(referenceTime, input);
assert.deepEqual(actual, expected);

Loading…
Cancel
Save