Skip to content

Commit

Permalink
Refactor conversationsSelectors to use unread_msgs data (#939)
Browse files Browse the repository at this point in the history
  • Loading branch information
borisyankov committed Jul 28, 2017
1 parent d33e0db commit 4ed873b
Show file tree
Hide file tree
Showing 13 changed files with 445 additions and 312 deletions.
224 changes: 1 addition & 223 deletions src/chat/__tests__/chatSelectors-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import deepFreeze from 'deep-freeze';

import {
getAnchor,
getRecentConversations,
getUnreadPrivateMessagesCount,
getCurrentTypingUsers,
getLastTopicInActiveNarrow,
} from '../chatSelectors';
import { homeNarrow, specialNarrow, privateNarrow, groupNarrow } from '../../utils/narrow';
import { homeNarrow, specialNarrow, privateNarrow } from '../../utils/narrow';

describe('getAnchor', () => {
test('return undefined when there are no messages', () => {
Expand Down Expand Up @@ -56,226 +54,6 @@ describe('getAnchor', () => {
});
});

describe('getRecentConversations', () => {
const privatesNarrowStr = JSON.stringify(specialNarrow('private'));

test('when no messages, return no conversations', () => {
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
flags: { read: {} },
chat: {
narrow: homeNarrow(),
messages: {
[privatesNarrowStr]: [],
},
},
});

const actual = getRecentConversations(state);

expect(actual).toEqual([]);
});

test('returns unique list of recipients, includes conversations with self', () => {
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
flags: { read: {} },
chat: {
messages: {
[privatesNarrowStr]: [
{ display_recipient: [{ email: 'me@example.com' }, { email: 'john@example.com' }] },
{ display_recipient: [{ email: 'mark@example.com' }] },
{ display_recipient: [{ email: 'john@example.com' }] },
{ display_recipient: [{ email: 'me@example.com' }] },
{ display_recipient: [{ email: 'john@example.com' }, { email: 'mark@example.com' }] },
],
},
},
});

const expectedPrivate = [
{ recipients: 'john@example.com', timestamp: 0, unread: 2 },
{ recipients: 'mark@example.com', timestamp: 0, unread: 1 },
{ recipients: 'me@example.com', timestamp: 0, unread: 1 },
{ recipients: 'john@example.com,mark@example.com', timestamp: 0, unread: 1 },
];

const actual = getRecentConversations(state);

expect(actual).toEqual(expectedPrivate);
});

test('returns recipients sorted by last activity', () => {
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
flags: { read: {} },
chat: {
messages: {
[privatesNarrowStr]: [
{
display_recipient: [{ email: 'me@example.com' }, { email: 'john@example.com' }],
timestamp: 2,
},
{
display_recipient: [{ email: 'mark@example.com' }],
timestamp: 1,
},
{
display_recipient: [{ email: 'john@example.com' }],
timestamp: 4,
},
{
display_recipient: [{ email: 'mark@example.com' }],
timestamp: 3,
},
{
display_recipient: [{ email: 'john@example.com' }, { email: 'mark@example.com' }],
timestamp: 5,
},
{
display_recipient: [{ email: 'me@example.com' }],
timestamp: 6,
},
],
},
},
});

const expectedPrivate = [
{
recipients: 'me@example.com',
timestamp: 6,
unread: 1,
},
{
recipients: 'john@example.com,mark@example.com',
timestamp: 5,
unread: 1,
},
{
recipients: 'john@example.com',
timestamp: 4,
unread: 2,
},
{
recipients: 'mark@example.com',
timestamp: 3,
unread: 2,
},
];

const actual = getRecentConversations(state);

expect(actual).toEqual(expectedPrivate);
});
});

describe('getCurrentTypingUsers', () => {
test('return undefined when current narrow is not private or group', () => {
const state = deepFreeze({
accounts: [{}],
chat: {
narrow: homeNarrow(),
},
});

const typingUsers = getCurrentTypingUsers(state);

expect(typingUsers).toEqual(undefined);
});

test('when in private narrow and the same user is typing return details', () => {
const expectedUser = {
id: 1,
email: 'john@example.com',
avatarUrl: 'http://example.com/avatar.png',
fullName: 'John Doe',
};
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
chat: {
narrow: privateNarrow('john@example.com'),
},
typing: {
'john@example.com': [1],
},
users: [expectedUser],
});

const typingUsers = getCurrentTypingUsers(state);

expect(typingUsers).toEqual([expectedUser]);
});

test('when two people are typing, return details for all of them', () => {
const user1 = {
id: 1,
email: 'john@example.com',
avatarUrl: 'http://example.com/avatar1.png',
fullName: 'John Doe',
};
const user2 = {
id: 2,
email: 'mark@example.com',
avatarUrl: 'http://example.com/avatar2.png',
fullName: 'Mark Dark',
};
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
chat: {
narrow: groupNarrow(['john@example.com', 'mark@example.com']),
},
typing: {
'john@example.com,mark@example.com': [1, 2],
},
users: [user1, user2],
});

const typingUsers = getCurrentTypingUsers(state);

expect(typingUsers).toEqual([user1, user2]);
});

test('when in private narrow but different user is typing return undefined', () => {
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
chat: {
narrow: privateNarrow('mark@example.com'),
},
typing: {
'john@example.com': [1],
},
});

const typingUsers = getCurrentTypingUsers(state);

expect(typingUsers).toEqual(undefined);
});

test('when in group narrow and someone is typing in that narrow return details', () => {
const expectedUser = {
id: 1,
email: 'john@example.com',
avatarUrl: 'http://example.com/avatar.png',
fullName: 'John Doe',
};
const state = deepFreeze({
accounts: [{ email: 'me@example.com' }],
chat: {
narrow: groupNarrow(['mark@example.com', 'john@example.com']),
},
typing: {
'john@example.com,mark@example.com': [1],
},
users: [expectedUser],
});

const typingUsers = getCurrentTypingUsers(state);

expect(typingUsers).toEqual([expectedUser]);
});
});

describe('getUnreadPrivateMessagesCount', () => {
test('when no private messages, unread count is 0', () => {
const state = deepFreeze({
Expand Down
8 changes: 4 additions & 4 deletions src/chat/__tests__/flagsReducers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ describe('flagsReducers', () => {
read: { 1: true },
starred: { 1: true },
collapsed: { 1: true },
mentioned: { 1: true },
wildcard_mentioned: { 1: true },
mentions: { 1: true },
wildcard_mentions: { 1: true },
summarize_in_home: { 1: true },
summarize_in_stream: { 1: true },
force_expand: { 1: true },
Expand All @@ -332,8 +332,8 @@ describe('flagsReducers', () => {
read: {},
starred: {},
collapsed: {},
mentioned: {},
wildcard_mentioned: {},
mentions: {},
wildcard_mentions: {},
summarize_in_home: {},
summarize_in_stream: {},
force_expand: {},
Expand Down
70 changes: 3 additions & 67 deletions src/chat/chatSelectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@
import { createSelector } from 'reselect';

import type { GlobalState } from '../types';
import { getOwnEmail } from '../account/accountSelectors';
import { getUserById } from '../users/userSelectors';
import {
getAllMessages,
getSubscriptions,
getActiveNarrow,
getMute,
getTyping,
getUsers,
getReadFlags,
getStreams,
} from '../selectors';
import { specialNarrow, isPrivateOrGroupNarrow } from '../utils/narrow';
import { normalizeRecipients, normalizeRecipientsSansMe, shouldBeMuted } from '../utils/message';
} from '../baseSelectors';
import { specialNarrow } from '../utils/narrow';
import { shouldBeMuted } from '../utils/message';
import { countUnread } from '../utils/unread';
import { NULL_MESSAGE, NULL_USER, NULL_SUBSCRIPTION } from '../nullObjects';

Expand All @@ -27,9 +24,6 @@ export const getIsFetching = (state: GlobalState): boolean =>
export const getActiveNarrowString = (state: GlobalState): string =>
JSON.stringify(state.chat.narrow);

export const getPrivateNarrowString = (state: GlobalState): string =>
JSON.stringify(state.chat.narrow);

export const getMessagesInActiveNarrow = createSelector(
getAllMessages,
getActiveNarrowString,
Expand Down Expand Up @@ -61,70 +55,12 @@ export const getPrivateMessages = createSelector(
messages => messages[privateNarrowStr] || [],
);

export const getRecentConversations = createSelector(
getOwnEmail,
getPrivateMessages,
getReadFlags,
(ownEmail, messages, read) => {
const recipients = messages.map(msg => ({
emails: normalizeRecipientsSansMe(msg.display_recipient, ownEmail),
timestamp: msg.timestamp,
isRead: read[msg.id] || 0,
}));

const groupedRecipients = recipients.reduce((uniqueMap, recipient) => {
if (!uniqueMap.has(recipient.emails)) {
// new entry
uniqueMap.set(recipient.emails, {
recipients: recipient.emails,
timestamp: recipient.timestamp || 0,
unread: +!recipient.isRead,
});
} else {
// update existing entry
const prev = uniqueMap.get(recipient.emails);
uniqueMap.set(recipient.emails, {
recipients: recipient.emails,
timestamp: Math.max(prev.timestamp || 0, recipient.timestamp || 0),
unread: prev.unread + +!recipient.isRead,
});
}
return uniqueMap;
}, new Map());

// sort by most recent timestamp
return Array.from(groupedRecipients.values()).sort((a, b) => +b.timestamp - +a.timestamp);
},
);

export const getUnreadPrivateMessagesCount = createSelector(
getPrivateMessages,
getReadFlags,
(privateMessages, readFlags) => countUnread(privateMessages.map(msg => msg.id), readFlags),
);

export const getCurrentTypingUsers = createSelector(
getActiveNarrow,
getTyping,
getUsers,
getOwnEmail,
(activeNarrow, typing, users, ownEmail) => {
if (!isPrivateOrGroupNarrow(activeNarrow)) {
return undefined;
}

const recipients = activeNarrow[0].operand.split(',').map(email => ({ email }));
const normalizedRecipients = normalizeRecipients(recipients);
const currentTyping = typing[normalizedRecipients];

if (!currentTyping) {
return undefined;
}

return currentTyping.map(userId => getUserById(users, userId));
},
);

export const getLastTopicInActiveNarrow = createSelector(
getMessagesInActiveNarrow,
messagesInActiveNarrow => {
Expand Down
4 changes: 2 additions & 2 deletions src/chat/flagsReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const initialState = {
read: {},
starred: {},
collapsed: {},
mentioned: {},
wildcard_mentioned: {},
mentions: {},
wildcard_mentions: {},
summarize_in_home: {},
summarize_in_stream: {},
force_expand: {},
Expand Down
Loading

0 comments on commit 4ed873b

Please sign in to comment.