Implementing grouping messages by date

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

@ -1,14 +1,16 @@
```jsx ```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 tokens = ['foo', 'bar', 'baz', 'qux', 'quux'];
const fileExtensions = ['docx', 'pdf', 'txt', 'mp3', 'wmv', 'tiff']; const fileExtensions = ['docx', 'pdf', 'txt', 'mp3', 'wmv', 'tiff'];
const createRandomMessage = (props) => { const createRandomMessage = ({startTime, timeWindow} = {}) => (props) => {
const now = Date.now(); const now = Date.now();
const fileName = const fileName =
`${_.sample(tokens)}${_.sample(tokens)}.${_.sample(fileExtensions)}`; `${_.sample(tokens)}${_.sample(tokens)}.${_.sample(fileExtensions)}`;
return { return {
id: _.random(now).toString(), id: _.random(now).toString(),
received_at: _.random(now - YEAR_MS, now), received_at: _.random(startTime, startTime + timeWindow),
attachments: [{ attachments: [{
data: null, data: null,
fileName, 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 startTime = Date.now();
const messages = _.sortBy( 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 message => -message.received_at
); );

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

@ -5,11 +5,25 @@ import moment from 'moment';
import { compact, groupBy, sortBy } from 'lodash'; import { compact, groupBy, sortBy } from 'lodash';
import { Message } from './propTypes/Message'; 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 = ( export const groupMessagesByDate = (
timestamp: number, timestamp: number,
messages: Array<Message> messages: Array<Message>
): any => { ): Array<Section> => {
const referenceDateTime = moment.utc(timestamp); const referenceDateTime = moment.utc(timestamp);
const today = moment(referenceDateTime).startOf('day'); const today = moment(referenceDateTime).startOf('day');
const yesterday = moment(referenceDateTime) const yesterday = moment(referenceDateTime)
@ -18,42 +32,132 @@ export const groupMessagesByDate = (
const thisWeek = moment(referenceDateTime).startOf('isoWeek'); const thisWeek = moment(referenceDateTime).startOf('isoWeek');
const thisMonth = moment(referenceDateTime).startOf('month'); const thisMonth = moment(referenceDateTime).startOf('month');
const sorted = sortBy(messages, message => -message.received_at); const sortedMessages = sortBy(messages, message => -message.received_at);
const annotations = sorted.map(message => { const messagesWithSection = sortedMessages.map(
const date = moment.utc(message.received_at); 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)) { const toSection = (
return { messagesWithSection: Array<MessageWithSection> | undefined
order: 0, ): Section | null => {
label: 'today', if (!messagesWithSection || messagesWithSection.length === 0) {
message, return null;
}; }
} else if (date.isAfter(yesterday)) {
return { const firstMessageWithSection: MessageWithSection = messagesWithSection[0];
order: 1, if (!firstMessageWithSection) {
label: 'yesterday', return null;
message, }
};
} else if (date.isAfter(thisWeek)) { const messages = messagesWithSection.map(
messageWithSection => messageWithSection.message
);
switch (firstMessageWithSection.type) {
case 'today':
case 'yesterday':
case 'thisWeek':
case 'thisMonth':
return { return {
order: 2, type: firstMessageWithSection.type,
label: 'thisWeek', messages: messages,
message,
}; };
} else if (date.isAfter(thisMonth)) { case 'yearMonth':
return { return {
order: 3, type: firstMessageWithSection.type,
label: 'thisMonth', year: firstMessageWithSection.year,
message, 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 { return {
order: date.year() * 100 + date.month(), order: 3,
label: 'yearMonth', type: 'thisMonth',
message, 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 * @prettier
*/ */
import 'mocha'; import 'mocha';
import { assert } from 'chai'; 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'; import { Message } from '../../../components/conversation/media-gallery/propTypes/Message';
const toMessage = (date: Date): Message => ({ const toMessage = (date: Date): Message => ({
@ -16,7 +21,7 @@ const toMessage = (date: Date): Message => ({
describe('groupMessagesByDate', () => { describe('groupMessagesByDate', () => {
it('should group messages', () => { it('should group messages', () => {
const referenceTime = new Date('2018-04-12T18:00Z').getTime(); // Thu const referenceTime = new Date('2018-04-12T18:00Z').getTime(); // Thu
const input: Array<Message> = [ const input: Array<Message> = shuffle([
// Today // Today
toMessage(new Date('2018-04-12T12:00Z')), // Thu toMessage(new Date('2018-04-12T12:00Z')), // Thu
toMessage(new Date('2018-04-12T00:01Z')), // Thu toMessage(new Date('2018-04-12T00:01Z')), // Thu
@ -32,110 +37,94 @@ describe('groupMessagesByDate', () => {
// February 2011 // February 2011
toMessage(new Date('2011-02-28T23:59Z')), toMessage(new Date('2011-02-28T23:59Z')),
toMessage(new Date('2011-02-01T10:00Z')), toMessage(new Date('2011-02-01T10:00Z')),
]; ]);
const expected = { const expected: Array<Section> = [
today: [ {
{ type: 'today',
order: 0, messages: [
label: 'today', {
message: {
id: 'Thu, 12 Apr 2018 12:00:00 GMT', id: 'Thu, 12 Apr 2018 12:00:00 GMT',
received_at: 1523534400000, received_at: 1523534400000,
attachments: [], attachments: [],
}, },
}, {
{
order: 0,
label: 'today',
message: {
id: 'Thu, 12 Apr 2018 00:01:00 GMT', id: 'Thu, 12 Apr 2018 00:01:00 GMT',
received_at: 1523491260000, received_at: 1523491260000,
attachments: [], attachments: [],
}, },
}, ],
], },
yesterday: [ {
{ type: 'yesterday',
order: 1, messages: [
label: 'yesterday', {
message: {
id: 'Wed, 11 Apr 2018 23:59:00 GMT', id: 'Wed, 11 Apr 2018 23:59:00 GMT',
received_at: 1523491140000, received_at: 1523491140000,
attachments: [], attachments: [],
}, },
}, ],
], },
thisWeek: [ {
{ type: 'thisWeek',
order: 2, messages: [
label: 'thisWeek', {
message: {
id: 'Mon, 09 Apr 2018 00:01:00 GMT', id: 'Mon, 09 Apr 2018 00:01:00 GMT',
received_at: 1523232060000, received_at: 1523232060000,
attachments: [], attachments: [],
}, },
}, ],
], },
thisMonth: [ {
{ type: 'thisMonth',
order: 3, messages: [
label: 'thisMonth', {
message: {
id: 'Sun, 08 Apr 2018 23:59:00 GMT', id: 'Sun, 08 Apr 2018 23:59:00 GMT',
received_at: 1523231940000, received_at: 1523231940000,
attachments: [], attachments: [],
}, },
}, {
{
order: 3,
label: 'thisMonth',
message: {
id: 'Sun, 01 Apr 2018 00:01:00 GMT', id: 'Sun, 01 Apr 2018 00:01:00 GMT',
received_at: 1522540860000, received_at: 1522540860000,
attachments: [], attachments: [],
}, },
}, ],
], },
yearMonth: [ {
{ type: 'yearMonth',
order: 201802, year: 2018,
label: 'yearMonth', month: 2,
message: { messages: [
{
id: 'Sat, 31 Mar 2018 23:59:00 GMT', id: 'Sat, 31 Mar 2018 23:59:00 GMT',
received_at: 1522540740000, received_at: 1522540740000,
attachments: [], attachments: [],
}, },
}, {
{
order: 201802,
label: 'yearMonth',
message: {
id: 'Thu, 01 Mar 2018 14:00:00 GMT', id: 'Thu, 01 Mar 2018 14:00:00 GMT',
received_at: 1519912800000, received_at: 1519912800000,
attachments: [], attachments: [],
}, },
}, ],
{ },
order: 201101, {
label: 'yearMonth', type: 'yearMonth',
message: { year: 2011,
month: 1,
messages: [
{
id: 'Mon, 28 Feb 2011 23:59:00 GMT', id: 'Mon, 28 Feb 2011 23:59:00 GMT',
received_at: 1298937540000, received_at: 1298937540000,
attachments: [], attachments: [],
}, },
}, {
{
order: 201101,
label: 'yearMonth',
message: {
id: 'Tue, 01 Feb 2011 10:00:00 GMT', id: 'Tue, 01 Feb 2011 10:00:00 GMT',
received_at: 1296554400000, received_at: 1296554400000,
attachments: [], attachments: [],
}, },
}, ],
], },
}; ];
const actual = groupMessagesByDate(referenceTime, input); const actual = groupMessagesByDate(referenceTime, input);
assert.deepEqual(actual, expected); assert.deepEqual(actual, expected);

Loading…
Cancel
Save