diff --git a/src/nav/__tests__/navReducer-test.js b/src/nav/__tests__/navReducer-test.js index b68862e5bba..7716328c4a2 100644 --- a/src/nav/__tests__/navReducer-test.js +++ b/src/nav/__tests__/navReducer-test.js @@ -6,7 +6,7 @@ import { NULL_OBJECT } from '../../nullObjects'; describe('navReducer', () => { describe('LOGIN_SUCCESS', () => { - test('replaces the existing route stack with "main" on sign in', () => { + test('replaces the existing route stack with "loading" on sign in', () => { const prevState = deepFreeze({ index: 2, routes: [{ key: 'one' }, { key: 'two' }, { key: 'password' }], @@ -18,7 +18,7 @@ describe('navReducer', () => { const expectedState = { index: 0, - routes: [{ routeName: 'main' }], + routes: [{ routeName: 'loading' }], }; const newState = navReducer(prevState, action); diff --git a/src/nav/navReducer.js b/src/nav/navReducer.js index 040ed5ce33c..1a3a533031c 100644 --- a/src/nav/navReducer.js +++ b/src/nav/navReducer.js @@ -77,10 +77,8 @@ export default (state: NavigationState = initialState, action: Action): Navigati return rehydrate(state, action); case ACCOUNT_SWITCH: - return getStateForRoute('loading'); - case LOGIN_SUCCESS: - return getStateForRoute('main'); + return getStateForRoute('loading'); case INITIAL_FETCH_COMPLETE: return state.routes[0].routeName === 'main' ? state : getStateForRoute('main'); diff --git a/src/pm-conversations/__tests__/pmConversationsSelectors-test.js b/src/pm-conversations/__tests__/pmConversationsSelectors-test.js index 52fa55a8851..f665209a1ac 100644 --- a/src/pm-conversations/__tests__/pmConversationsSelectors-test.js +++ b/src/pm-conversations/__tests__/pmConversationsSelectors-test.js @@ -7,6 +7,7 @@ describe('getRecentConversations', () => { test('when no messages, return no conversations', () => { const state = deepFreeze({ realm: { email: 'me@example.com' }, + users: [{ user_id: 0, email: 'me@example.com' }], narrows: { [ALL_PRIVATE_NARROW_STR]: [], }, @@ -24,6 +25,11 @@ describe('getRecentConversations', () => { test('returns unique list of recipients, includes conversations with self', () => { const state = deepFreeze({ realm: { email: 'me@example.com' }, + users: [ + { user_id: 0, email: 'me@example.com' }, + { user_id: 1, email: 'john@example.com' }, + { user_id: 2, email: 'mark@example.com' }, + ], narrows: { [ALL_PRIVATE_NARROW_STR]: [0, 1, 2, 3, 4], }, @@ -126,6 +132,11 @@ describe('getRecentConversations', () => { test('returns recipients sorted by last activity', () => { const state = deepFreeze({ realm: { email: 'me@example.com' }, + users: [ + { user_id: 0, email: 'me@example.com' }, + { user_id: 1, email: 'john@example.com' }, + { user_id: 2, email: 'mark@example.com' }, + ], narrows: { [ALL_PRIVATE_NARROW_STR]: [1, 2, 3, 4, 5, 6], }, diff --git a/src/pm-conversations/pmConversationsSelectors.js b/src/pm-conversations/pmConversationsSelectors.js index 65eaae5d60a..f8efbd8ba21 100644 --- a/src/pm-conversations/pmConversationsSelectors.js +++ b/src/pm-conversations/pmConversationsSelectors.js @@ -1,26 +1,26 @@ /* @flow strict-local */ import { createSelector } from 'reselect'; -import type { Message, PmConversationData, Selector } from '../types'; +import type { Message, PmConversationData, Selector, User } from '../types'; import { getPrivateMessages } from '../message/messageSelectors'; -import { getOwnEmail } from '../users/userSelectors'; +import { getOwnUser } from '../users/userSelectors'; import { getUnreadByPms, getUnreadByHuddles } from '../unread/unreadSelectors'; import { normalizeRecipientsSansMe, pmUnreadsKeyFromMessage } from '../utils/recipient'; export const getRecentConversations: Selector = createSelector( - getOwnEmail, + getOwnUser, getPrivateMessages, getUnreadByPms, getUnreadByHuddles, ( - ownEmail: string, + ownUser: User, messages: Message[], unreadPms: { [number]: number }, unreadHuddles: { [string]: number }, ): PmConversationData[] => { const recipients = messages.map(msg => ({ - ids: pmUnreadsKeyFromMessage(msg, ownEmail), - emails: normalizeRecipientsSansMe(msg.display_recipient, ownEmail), + ids: pmUnreadsKeyFromMessage(msg, ownUser.user_id), + emails: normalizeRecipientsSansMe(msg.display_recipient, ownUser.email), msgId: msg.id, })); diff --git a/src/realm/__tests__/realmReducer-test.js b/src/realm/__tests__/realmReducer-test.js index 36ae905a01d..2e788f0f5b9 100644 --- a/src/realm/__tests__/realmReducer-test.js +++ b/src/realm/__tests__/realmReducer-test.js @@ -11,7 +11,7 @@ import { NULL_OBJECT } from '../../nullObjects'; describe('realmReducer', () => { describe('ACCOUNT_SWITCH', () => { - test('resets state to blank state', () => { + test('resets state', () => { const initialState = NULL_OBJECT; const action = deepFreeze({ @@ -21,8 +21,8 @@ describe('realmReducer', () => { const expectedState = { canCreateStreams: true, crossRealmBots: [], - email: '', - user_id: 0, + email: undefined, + user_id: undefined, isAdmin: false, twentyFourHourTime: false, emoji: {}, diff --git a/src/realm/realmReducer.js b/src/realm/realmReducer.js index 4dfe450ab77..e2a6ea915ad 100644 --- a/src/realm/realmReducer.js +++ b/src/realm/realmReducer.js @@ -11,35 +11,18 @@ import { } from '../actionConstants'; import { objectFromEntries } from '../jsBackport'; -// Initial state const initialState = { - canCreateStreams: true, crossRealmBots: [], + + nonActiveUsers: [], + filters: [], + emoji: {}, + email: undefined, user_id: undefined, twentyFourHourTime: false, - emoji: {}, - filters: [], + canCreateStreams: true, isAdmin: false, - nonActiveUsers: [], -}; - -/** - * A version of `initialState` with some made-up blank data. - * - * On `LOGIN_SUCCESS`, we go straight to showing the main app UI (see - * `navReducer`) even though we're still loading the actual data from the - * server. So we need some fake data that the UI code will swallow. - * TODO: Probably stop doing that. - * - * Also: On `ACCOUNT_SWITCH`, during the transition animation, some old - * components can still be mounted from the UI for the previous account that - * make no sense without server data. Probably ditto `LOGOUT`. - */ -const fakeBlankState = { - ...initialState, - email: '', - user_id: 0, }; const convertRealmEmoji = (data): RealmEmojiById => @@ -50,20 +33,21 @@ export default (state: RealmState = initialState, action: Action): RealmState => case LOGOUT: case LOGIN_SUCCESS: case ACCOUNT_SWITCH: - return fakeBlankState; + return initialState; case REALM_INIT: { return { - ...state, - canCreateStreams: action.data.can_create_streams, crossRealmBots: action.data.cross_realm_bots, + + nonActiveUsers: action.data.realm_non_active_users, + filters: action.data.realm_filters, + emoji: convertRealmEmoji(action.data.realm_emoji), + email: action.data.email, user_id: action.data.user_id, - emoji: convertRealmEmoji(action.data.realm_emoji), - filters: action.data.realm_filters, - isAdmin: action.data.is_admin, - nonActiveUsers: action.data.realm_non_active_users, twentyFourHourTime: action.data.twenty_four_hour_time, + canCreateStreams: action.data.can_create_streams, + isAdmin: action.data.is_admin, }; } diff --git a/src/reduxTypes.js b/src/reduxTypes.js index aba9d0e4f95..a7533442acb 100644 --- a/src/reduxTypes.js +++ b/src/reduxTypes.js @@ -233,6 +233,7 @@ export type PresenceState = {| * * About the user: * @prop email + * @prop user_id * @prop twentyFourHourTime * @prop canCreateStreams * @prop isAdmin diff --git a/src/utils/recipient.js b/src/utils/recipient.js index 0e7f6f44b4b..b6f8ee456c2 100644 --- a/src/utils/recipient.js +++ b/src/utils/recipient.js @@ -108,35 +108,33 @@ export const pmKeyRecipientsFromMessage = ( * * `UnreadState`, the type of `state.unread`, which is the data structure * these keys appear in. * - * @param ownEmail - Required if the message could be a 1:1 PM; optional if + * @param ownUserId - Required if the message could be a 1:1 PM; optional if * it is definitely a group PM. */ // Specifically, this includes all user IDs for group PMs and self-PMs, // and just the other user ID for non-self 1:1s; and in each case the list // is sorted numerically and encoded in ASCII-decimal, comma-separated. // See the `unread_msgs` data structure in `src/api/initialDataTypes.js`. -export const pmUnreadsKeyFromMessage = (message: Message, ownEmail?: string): string => { +export const pmUnreadsKeyFromMessage = (message: Message, ownUserId?: number): string => { if (message.type !== 'private') { throw new Error('pmUnreadsKeyFromMessage: expected PM, got stream message'); } + const recipients: PmRecipientUser[] = message.display_recipient; // This includes all users in the thread; see `Message#display_recipient`. - const recipients = message.display_recipient; + const userIds = recipients.map(r => r.id); - if (recipients.length === 1) { + if (userIds.length === 1) { // Self-PM. - return recipients[0].id.toString(); - } else if (recipients.length === 2) { + return userIds[0].toString(); + } else if (userIds.length === 2) { // Non-self 1:1 PM. Unlike display_recipient, leave out the self user. - if (ownEmail === undefined) { - throw new Error('getRecipientsIds: got 1:1 PM, but ownEmail omitted'); + if (ownUserId === undefined) { + throw new Error('getRecipientsIds: got 1:1 PM, but ownUserId omitted'); } - return recipients.filter(r => r.email !== ownEmail)[0].id.toString(); + return userIds.filter(userId => userId !== ownUserId)[0].toString(); } else { // Group PM. - return recipients - .map(s => s.id) - .sort((a, b) => a - b) - .join(','); + return userIds.sort((a, b) => a - b).join(','); } };