Implementing grouping messages by date
This commit is contained in:
parent
dea56c0008
commit
b0fefdbb98
4 changed files with 232 additions and 115 deletions
|
@ -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)) {
|
||||
return {
|
||||
order: 2,
|
||||
label: 'thisWeek',
|
||||
message,
|
||||
};
|
||||
} else if (date.isAfter(thisMonth)) {
|
||||
return {
|
||||
order: 3,
|
||||
label: 'thisMonth',
|
||||
message,
|
||||
};
|
||||
}
|
||||
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: 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;
|
||||
}
|
||||
};
|
||||
|
||||
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: date.year() * 100 + date.month(),
|
||||
label: 'yearMonth',
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
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 { groupMessagesByDate } from '../../../components/conversation/media-gallery/groupMessagesByDate';
|
||||
import { assert } from 'chai';
|
||||
import { shuffle } from 'lodash';
|
||||
|
||||
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…
Add table
Add a link
Reference in a new issue