diff --git a/src/__tests__/lib/exampleData.js b/src/__tests__/lib/exampleData.js index c6d4af8c6a0..fba60defcce 100644 --- a/src/__tests__/lib/exampleData.js +++ b/src/__tests__/lib/exampleData.js @@ -24,6 +24,7 @@ import type { MessageFetchStartAction, MessageFetchCompleteAction, Action, + PerAccountAction, PerAccountState, GlobalState, CaughtUpState, @@ -765,7 +766,7 @@ export const action = Object.freeze({ export const mkActionEventNewMessage = ( message: Message, args?: {| caughtUp?: CaughtUpState, local_message_id?: number, ownUserId?: UserId |}, -): Action => +): PerAccountAction => deepFreeze({ type: EVENT_NEW_MESSAGE, id: 1001, diff --git a/src/account/accountActions.js b/src/account/accountActions.js index 42439a648be..8d67c1615d4 100644 --- a/src/account/accountActions.js +++ b/src/account/accountActions.js @@ -1,10 +1,10 @@ /* @flow strict-local */ import * as NavigationService from '../nav/NavigationService'; -import type { Action, ThunkAction, GlobalThunkAction } from '../types'; +import type { AllAccountsAction, ThunkAction, GlobalThunkAction } from '../types'; import { ACCOUNT_SWITCH, ACCOUNT_REMOVE, LOGIN_SUCCESS, LOGOUT } from '../actionConstants'; import { resetToAccountPicker, resetToMainTabs } from '../nav/navActions'; -const accountSwitchPlain = (index: number): Action => ({ +const accountSwitchPlain = (index: number): AllAccountsAction => ({ type: ACCOUNT_SWITCH, index, }); @@ -14,12 +14,12 @@ export const accountSwitch = (index: number): GlobalThunkAction => (dispat dispatch(accountSwitchPlain(index)); }; -export const removeAccount = (index: number): Action => ({ +export const removeAccount = (index: number): AllAccountsAction => ({ type: ACCOUNT_REMOVE, index, }); -const loginSuccessPlain = (realm: URL, email: string, apiKey: string): Action => ({ +const loginSuccessPlain = (realm: URL, email: string, apiKey: string): AllAccountsAction => ({ type: LOGIN_SUCCESS, realm, email, @@ -34,7 +34,7 @@ export const loginSuccess = (realm: URL, email: string, apiKey: string): ThunkAc dispatch(loginSuccessPlain(realm, email, apiKey)); }; -const logoutPlain = (): Action => ({ +const logoutPlain = (): AllAccountsAction => ({ type: LOGOUT, }); diff --git a/src/actionConstants.js b/src/actionConstants.js index 1c7bb14e285..c246460b01e 100644 --- a/src/actionConstants.js +++ b/src/actionConstants.js @@ -50,7 +50,7 @@ export const MESSAGE_FETCH_ERROR: 'MESSAGE_FETCH_ERROR' = 'MESSAGE_FETCH_ERROR'; export const MESSAGE_FETCH_COMPLETE: 'MESSAGE_FETCH_COMPLETE' = 'MESSAGE_FETCH_COMPLETE'; export const PRESENCE_RESPONSE: 'PRESENCE_RESPONSE' = 'PRESENCE_RESPONSE'; -export const SETTINGS_CHANGE: 'SETTINGS_CHANGE' = 'SETTINGS_CHANGE'; +export const SET_GLOBAL_SETTINGS: 'SET_GLOBAL_SETTINGS' = 'SET_GLOBAL_SETTINGS'; export const DEBUG_FLAG_TOGGLE: 'DEBUG_FLAG_TOGGLE' = 'DEBUG_FLAG_TOGGLE'; export const DISMISS_SERVER_COMPAT_NOTICE: 'DISMISS_SERVER_COMPAT_NOTICE' = 'DISMISS_SERVER_COMPAT_NOTICE'; diff --git a/src/actionTypes.js b/src/actionTypes.js index d8aa59e9fd0..0b4f025dedf 100644 --- a/src/actionTypes.js +++ b/src/actionTypes.js @@ -1,4 +1,5 @@ /* @flow strict-local */ +import { ensureUnreachable } from './generics'; import { REHYDRATE, APP_ONLINE, @@ -18,7 +19,7 @@ import { REGISTER_START, REGISTER_ABORT, REGISTER_COMPLETE, - SETTINGS_CHANGE, + SET_GLOBAL_SETTINGS, DRAFT_UPDATE, PRESENCE_RESPONSE, MESSAGE_SEND_START, @@ -84,7 +85,7 @@ import type { Topic, PresenceState, RealmEmojiById, - SettingsState, + GlobalSettingsState, CaughtUpState, MuteState, AlertWordsState, @@ -559,9 +560,9 @@ export type EventAction = | EventUserGroupAction | EventUserStatusUpdateAction; -type SettingsChangeAction = {| - type: typeof SETTINGS_CHANGE, - update: $Shape, +type SetGlobalSettingsAction = {| + type: typeof SET_GLOBAL_SETTINGS, + update: $Shape<$Exact>, |}; type DraftUpdateAction = {| @@ -607,9 +608,17 @@ type InitTopicsAction = {| streamId: number, |}; +/* eslint-disable spaced-comment */ + +//// +// +// The `Action` union type, and some subtypes. // -// The `Action` union type. +//// + // +// First, some convenience unions without much meaning. +// (We should perhaps just inline these below.) type AccountAction = AccountSwitchAction | AccountRemoveAction | LoginSuccessAction | LogoutAction; @@ -623,29 +632,183 @@ type MessageAction = MessageFetchStartAction | MessageFetchErrorAction | Message type OutboxAction = MessageSendStartAction | MessageSendCompleteAction | DeleteOutboxMessageAction; -type RealmAction = RegisterCompleteAction | UnackPushTokenAction | AckPushTokenAction; +// +// Then, the primary subtypes of `Action`. Each of these should have some +// coherent meaning in terms of what kind of state it applies to; and they +// should have no overlap. (Subtypes that might overlap are formed below +// as unions of these primary subtypes.) -type SessionAction = - | RehydrateAction - | AppOnlineAction - | AppOrientationAction - | GotPushTokenAction - | DebugFlagToggleAction - | DismissServerCompatNoticeAction - | ToggleOutboxSendingAction; +/* eslint-disable semi-style */ -/** Covers all actions we ever `dispatch`. */ -// The grouping here is completely arbitrary; don't worry about it. -export type Action = +/** + * Plain actions applying to this account's state. + * + * That is, these should only be dispatched from a per-account context, and + * they apply to the account the caller is acting on. In a pre-#5006 world, + * that means the active account. + */ +// prettier-ignore +export type PerAccountAction = + // The grouping here is completely arbitrary; don't worry about it. | EventAction - | AccountAction | LoadingAction | MessageAction | OutboxAction - | RealmAction - | SessionAction + | RegisterCompleteAction | DraftUpdateAction | PresenceResponseAction - | SettingsChangeAction | InitTopicsAction - | ClearTypingAction; + | ClearTypingAction + // state.session + | DismissServerCompatNoticeAction + | ToggleOutboxSendingAction + ; + +/** Plain actions applying to other accounts' per-account state. */ +// prettier-ignore +export type AllAccountsAction = + // This affects all the per-account states as well as everything else. + | RehydrateAction + // These can rearrange the `state.accounts` list itself. + | AccountAction + // These two are about a specific account… but not just the active one, + // and they encode which one they mean. + | AckPushTokenAction | UnackPushTokenAction + ; + +/** Plain actions not affecting any per-account state. */ +// prettier-ignore +export type AccountIndependentAction = + | SetGlobalSettingsAction + // state.session + | AppOnlineAction + | AppOrientationAction + | GotPushTokenAction + | DebugFlagToggleAction + ; + +// +// `Action` itself. + +/** + * Covers all plain actions we ever `dispatch`. + * + * For *all* actions we ever dispatch, see also the thunk action types in + * `reduxTypes.js`. + */ +// prettier-ignore +export type Action = + // This should consist of the primary subtypes defined just above. + | PerAccountAction + | AllAccountsAction + | AccountIndependentAction + ; + +// +// Other subtypes of `Action`. +// +// These should be unions of the primary subtypes, to express different +// meanings about what contexts the actions can be used in. + +/** Plain actions that per-account reducers may respond to. */ +// prettier-ignore +export type PerAccountApplicableAction = + | PerAccountAction + | AllAccountsAction + ; + +// Plain actions that global reducers may respond to are... well, at the +// moment we have no reducers that act only on global state. Our state +// subtrees `session` and `settings` mix global with per-account state, +// while `accounts` contains per-account state for all accounts, and its +// reducer does respond to some of PerAccountAction as well as +// AllAccountsAction. +// TODO(#5006): Make a GlobalApplicableAction for global session and +// settings state, once those are separate from per-account. + +// TODO(#5006): would be nice to assert these types have empty intersection +// (a: PerAccountApplicableAction & AccountIndependentAction): empty => a; // eslint-disable-line +// (a: GlobalApplicableAction & PerAccountAction): empty => a; // eslint-disable-line + +/** Actions that can be dispatched without reference to a specific account. */ +// prettier-ignore +export type DispatchableWithoutAccountAction = + | AllAccountsAction + | AccountIndependentAction + ; + +/** True just if the action is a PerAccountApplicableAction. */ +export function isPerAccountApplicableAction(action: Action): boolean { + switch (action.type) { + case EVENT: + case EVENT_ALERT_WORDS: + case EVENT_MESSAGE_DELETE: + case EVENT_MUTED_TOPICS: + case EVENT_MUTED_USERS: + case EVENT_NEW_MESSAGE: + case EVENT_PRESENCE: + case EVENT_REACTION_ADD: + case EVENT_REACTION_REMOVE: + case EVENT_REALM_EMOJI_UPDATE: + case EVENT_REALM_FILTERS: + case EVENT_SUBMESSAGE: + case EVENT_SUBSCRIPTION: + case EVENT_TYPING_START: + case EVENT_TYPING_STOP: + case EVENT_UPDATE_DISPLAY_SETTINGS: + case EVENT_UPDATE_GLOBAL_NOTIFICATIONS_SETTINGS: + case EVENT_UPDATE_MESSAGE: + case EVENT_UPDATE_MESSAGE_FLAGS: + case EVENT_USER_ADD: + case EVENT_USER_GROUP_ADD: + case EVENT_USER_GROUP_ADD_MEMBERS: + case EVENT_USER_GROUP_REMOVE: + case EVENT_USER_GROUP_REMOVE_MEMBERS: + case EVENT_USER_GROUP_UPDATE: + case EVENT_USER_REMOVE: + case EVENT_USER_STATUS_UPDATE: + case EVENT_USER_UPDATE: + case DEAD_QUEUE: + case REGISTER_START: + case REGISTER_ABORT: + case REGISTER_COMPLETE: + case MESSAGE_FETCH_COMPLETE: + case MESSAGE_FETCH_ERROR: + case MESSAGE_FETCH_START: + case MESSAGE_SEND_COMPLETE: + case MESSAGE_SEND_START: + case DELETE_OUTBOX_MESSAGE: + case DRAFT_UPDATE: + case PRESENCE_RESPONSE: + case INIT_TOPICS: + case CLEAR_TYPING: + case DISMISS_SERVER_COMPAT_NOTICE: + case TOGGLE_OUTBOX_SENDING: + (action: PerAccountAction); + (action: PerAccountApplicableAction); + return true; + + case REHYDRATE: + case ACCOUNT_SWITCH: + case ACCOUNT_REMOVE: + case LOGIN_SUCCESS: + case LOGOUT: + case ACK_PUSH_TOKEN: + case UNACK_PUSH_TOKEN: + (action: AllAccountsAction); + (action: PerAccountApplicableAction); + return true; + + case SET_GLOBAL_SETTINGS: + case APP_ONLINE: + case APP_ORIENTATION: + case GOT_PUSH_TOKEN: + case DEBUG_FLAG_TOGGLE: + (action: AccountIndependentAction); + return false; + + default: + ensureUnreachable(action); + return false; + } +} diff --git a/src/alertWords/alertWordsReducer.js b/src/alertWords/alertWordsReducer.js index 44724d439d1..5efd814821c 100644 --- a/src/alertWords/alertWordsReducer.js +++ b/src/alertWords/alertWordsReducer.js @@ -1,11 +1,14 @@ /* @flow strict-local */ -import type { AlertWordsState, Action } from '../types'; +import type { AlertWordsState, PerAccountApplicableAction } from '../types'; import { REGISTER_COMPLETE, EVENT_ALERT_WORDS, ACCOUNT_SWITCH, LOGOUT } from '../actionConstants'; import { NULL_ARRAY } from '../nullObjects'; const initialState = NULL_ARRAY; -export default (state: AlertWordsState = initialState, action: Action): AlertWordsState => { +export default ( + state: AlertWordsState = initialState, + action: PerAccountApplicableAction, +): AlertWordsState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/boot/reducers.js b/src/boot/reducers.js index 7d475c90932..aaff0400d30 100644 --- a/src/boot/reducers.js +++ b/src/boot/reducers.js @@ -1,6 +1,14 @@ /* @flow strict-local */ import config from '../config'; -import type { Action, GlobalState, MigrationsState } from '../types'; +import type { + Action, + PerAccountState, + PerAccountApplicableAction, + GlobalState, + MigrationsState, +} from '../types'; +import { dubPerAccountState } from '../reduxTypes'; +import { isPerAccountApplicableAction } from '../actionTypes'; import accounts from '../account/accountsReducer'; import alertWords from '../alertWords/alertWordsReducer'; @@ -40,12 +48,19 @@ function maybeLogSlowReducer(action, key: $Keys, startMs, endMs) { } } -function applyReducer, State>( +/** + * Apply a sub-reducer, with perf logging if enabled. + * + * The `globalState` argument is the "global" state relative to this + * sub-reducer: so type GS is `GlobalState` for a global reducer, and + * `PerAccountState` for a per-account reducer. + */ +function applyReducer & $Keys, State>( key: Key, - reducer: (void | State, Action, GlobalState) => State, + reducer: (void | State, A, GS) => State, state: void | State, - action: Action, - globalState: void | GlobalState, + action: A, + globalState: void | GS, ): State { let startMs = undefined; if (enableReduxSlowReducerWarnings) { @@ -62,51 +77,74 @@ function applyReducer, State>( Then on the other hand it's helpful because we want each reducer that ever does use the globalState parameter to require it -- so that Flow can help us be sure to pass it at the reducer's many other call sites, - in tests. That means it has to be `globalState: GlobalState`, not - `globalState : void | GlobalState`. + in tests. That means it has to be `globalState: GS`, not + `globalState : void | GS`. */ - const castGlobalState: GlobalState = globalState; + const castGlobalState: GS = globalState; const nextState = reducer(state, action, castGlobalState); if (startMs !== undefined) { const endMs = Date.now(); - maybeLogSlowReducer(action, key, startMs, endMs); + maybeLogSlowReducer((action: Action), key, startMs, endMs); } return nextState; } // Based on Redux upstream's combineReducers. -export default (state: void | GlobalState, action: Action): GlobalState => { +export default (globalState: void | GlobalState, origAction: Action): GlobalState => { + let nextPerAccountState = globalState; + if (!nextPerAccountState || isPerAccountApplicableAction(origAction)) { + // Update the per-account state. We do this when the action is a + // PerAccountApplicableAction... and also when it's the store + // initialization action, signalled by the previous state being void. + + /* $FlowFixMe[incompatible-type]: TODO teach Flow that + isPerAccountApplicableAction checks this */ + const action: PerAccountApplicableAction = origAction; + const state: void | PerAccountState = globalState ? dubPerAccountState(globalState) : undefined; + + // prettier-ignore + nextPerAccountState = { + alertWords: applyReducer('alertWords', alertWords, state?.alertWords, action, state), + caughtUp: applyReducer('caughtUp', caughtUp, state?.caughtUp, action, state), + drafts: applyReducer('drafts', drafts, state?.drafts, action, state), + fetching: applyReducer('fetching', fetching, state?.fetching, action, state), + flags: applyReducer('flags', flags, state?.flags, action, state), + messages: applyReducer('messages', messages, state?.messages, action, state), + narrows: applyReducer('narrows', narrows, state?.narrows, action, state), + mute: applyReducer('mute', mute, state?.mute, action, state), + mutedUsers: applyReducer('mutedUsers', mutedUsers, state?.mutedUsers, action, state), + outbox: applyReducer('outbox', outbox, state?.outbox, action, state), + pmConversations: applyReducer('pmConversations', pmConversations, state?.pmConversations, action, state), + presence: applyReducer('presence', presence, state?.presence, action, state), + realm: applyReducer('realm', realm, state?.realm, action, state), + streams: applyReducer('streams', streams, state?.streams, action, state), + subscriptions: applyReducer('subscriptions', subscriptions, state?.subscriptions, action, state), + topics: applyReducer('topics', topics, state?.topics, action, state), + typing: applyReducer('typing', typing, state?.typing, action, state), + unread: applyReducer('unread', unread, state?.unread, action, state), + userGroups: applyReducer('userGroups', userGroups, state?.userGroups, action, state), + userStatus: applyReducer('userStatus', userStatus, state?.userStatus, action, state), + users: applyReducer('users', users, state?.users, action, state), + }; + } + + const state = globalState; + const action = origAction; + // prettier-ignore const nextState = { - migrations: applyReducer('migrations', migrations, state?.migrations, action, state), - accounts: applyReducer('accounts', accounts, state?.accounts, action, state), - alertWords: applyReducer('alertWords', alertWords, state?.alertWords, action, state), - caughtUp: applyReducer('caughtUp', caughtUp, state?.caughtUp, action, state), - drafts: applyReducer('drafts', drafts, state?.drafts, action, state), - fetching: applyReducer('fetching', fetching, state?.fetching, action, state), - flags: applyReducer('flags', flags, state?.flags, action, state), - messages: applyReducer('messages', messages, state?.messages, action, state), - narrows: applyReducer('narrows', narrows, state?.narrows, action, state), - mute: applyReducer('mute', mute, state?.mute, action, state), - mutedUsers: applyReducer('mutedUsers', mutedUsers, state?.mutedUsers, action, state), - outbox: applyReducer('outbox', outbox, state?.outbox, action, state), - pmConversations: applyReducer('pmConversations', pmConversations, state?.pmConversations, action, state), - presence: applyReducer('presence', presence, state?.presence, action, state), - realm: applyReducer('realm', realm, state?.realm, action, state), + ...nextPerAccountState, + + // TODO(#5006): These mix together per-account and global state. session: applyReducer('session', session, state?.session, action, state), settings: applyReducer('settings', settings, state?.settings, action, state), - streams: applyReducer('streams', streams, state?.streams, action, state), - subscriptions: applyReducer('subscriptions', subscriptions, state?.subscriptions, action, state), - topics: applyReducer('topics', topics, state?.topics, action, state), - typing: applyReducer('typing', typing, state?.typing, action, state), - // $FlowFixMe[incompatible-call] TODO(#5006) - unread: applyReducer('unread', unread, state?.unread, action, state), - userGroups: applyReducer('userGroups', userGroups, state?.userGroups, action, state), - userStatus: applyReducer('userStatus', userStatus, state?.userStatus, action, state), - users: applyReducer('users', users, state?.users, action, state), + + // All other state. + migrations: applyReducer('migrations', migrations, state?.migrations, action, state), + accounts: applyReducer('accounts', accounts, state?.accounts, action, state), }; if (state && Object.keys(nextState).every(key => nextState[key] === state[key])) { diff --git a/src/caughtup/caughtUpReducer.js b/src/caughtup/caughtUpReducer.js index 07b6a107bc8..83f2f96546d 100644 --- a/src/caughtup/caughtUpReducer.js +++ b/src/caughtup/caughtUpReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { CaughtUpState, Action } from '../types'; +import type { CaughtUpState, PerAccountApplicableAction } from '../types'; import { REGISTER_COMPLETE, LOGOUT, @@ -15,7 +15,10 @@ import { isSearchNarrow, keyFromNarrow } from '../utils/narrow'; const initialState: CaughtUpState = NULL_OBJECT; -export default (state: CaughtUpState = initialState, action: Action): CaughtUpState => { +export default ( + state: CaughtUpState = initialState, + action: PerAccountApplicableAction, +): CaughtUpState => { switch (action.type) { case REGISTER_COMPLETE: case LOGOUT: diff --git a/src/chat/fetchingReducer.js b/src/chat/fetchingReducer.js index 76fdcb4fe38..248cd872e69 100644 --- a/src/chat/fetchingReducer.js +++ b/src/chat/fetchingReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { FetchingState, Action } from '../types'; +import type { FetchingState, PerAccountApplicableAction } from '../types'; import { LOGOUT, LOGIN_SUCCESS, @@ -63,7 +63,10 @@ const messageFetchComplete = (state, action) => { }; }; -export default (state: FetchingState = initialState, action: Action): FetchingState => { +export default ( + state: FetchingState = initialState, + action: PerAccountApplicableAction, +): FetchingState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: diff --git a/src/chat/flagsReducer.js b/src/chat/flagsReducer.js index f016ae16d28..67416a26f9f 100644 --- a/src/chat/flagsReducer.js +++ b/src/chat/flagsReducer.js @@ -1,7 +1,7 @@ /* @flow strict-local */ import invariant from 'invariant'; -import type { Action, FlagsState, Message } from '../types'; +import type { PerAccountApplicableAction, FlagsState, Message } from '../types'; import { REGISTER_COMPLETE, MESSAGE_FETCH_COMPLETE, @@ -110,7 +110,10 @@ const eventUpdateMessageFlags = (state, action) => { return state; }; -export default (state: FlagsState = initialState, action: Action): FlagsState => { +export default ( + state: FlagsState = initialState, + action: PerAccountApplicableAction, +): FlagsState => { switch (action.type) { case REGISTER_COMPLETE: case LOGOUT: diff --git a/src/chat/narrowsReducer.js b/src/chat/narrowsReducer.js index dc8a3557499..0ee59dde570 100644 --- a/src/chat/narrowsReducer.js +++ b/src/chat/narrowsReducer.js @@ -3,7 +3,7 @@ import union from 'lodash.union'; import Immutable from 'immutable'; -import type { NarrowsState, Action } from '../types'; +import type { NarrowsState, PerAccountApplicableAction } from '../types'; import { ensureUnreachable } from '../types'; import { REGISTER_COMPLETE, @@ -150,7 +150,10 @@ const eventUpdateMessageFlags = (state, action) => { return state; }; -export default (state: NarrowsState = initialState, action: Action): NarrowsState => { +export default ( + state: NarrowsState = initialState, + action: PerAccountApplicableAction, +): NarrowsState => { switch (action.type) { case REGISTER_COMPLETE: case LOGOUT: diff --git a/src/drafts/draftsActions.js b/src/drafts/draftsActions.js index 27e41a55e93..9b2a094b8e9 100644 --- a/src/drafts/draftsActions.js +++ b/src/drafts/draftsActions.js @@ -1,8 +1,8 @@ /* @flow strict-local */ -import type { Narrow, Action } from '../types'; +import type { Narrow, PerAccountAction } from '../types'; import { DRAFT_UPDATE } from '../actionConstants'; -export const draftUpdate = (narrow: Narrow, content: string): Action => ({ +export const draftUpdate = (narrow: Narrow, content: string): PerAccountAction => ({ type: DRAFT_UPDATE, narrow, content, diff --git a/src/drafts/draftsReducer.js b/src/drafts/draftsReducer.js index cb6675b3fc3..9f3110ee872 100644 --- a/src/drafts/draftsReducer.js +++ b/src/drafts/draftsReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { DraftsState, Action } from '../types'; +import type { DraftsState, PerAccountApplicableAction } from '../types'; import { DRAFT_UPDATE, LOGOUT, ACCOUNT_SWITCH } from '../actionConstants'; import { NULL_OBJECT } from '../nullObjects'; import { keyFromNarrow } from '../utils/narrow'; @@ -22,7 +22,10 @@ const draftUpdate = (state, action) => { return state[narrowStr] === action.content ? state : { ...state, [narrowStr]: action.content }; }; -export default (state: DraftsState = initialState, action: Action): DraftsState => { +export default ( + state: DraftsState = initialState, + action: PerAccountApplicableAction, +): DraftsState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/message/fetchActions.js b/src/message/fetchActions.js index f56d77d3de9..a5226de8509 100644 --- a/src/message/fetchActions.js +++ b/src/message/fetchActions.js @@ -1,7 +1,14 @@ /* @flow strict-local */ import * as logging from '../utils/logging'; import * as NavigationService from '../nav/NavigationService'; -import type { Narrow, PerAccountState, Message, Action, ThunkAction, UserId } from '../types'; +import type { + Narrow, + PerAccountState, + Message, + PerAccountAction, + ThunkAction, + UserId, +} from '../types'; import { ensureUnreachable } from '../types'; import type { RegisterAbortReason } from '../actionTypes'; import type { InitialData } from '../api/initialDataTypes'; @@ -39,14 +46,18 @@ import { ZulipVersion } from '../utils/zulipVersion'; import { getAllUsersById, getHaveServerData, getOwnUserId } from '../users/userSelectors'; import { MIN_RECENTPMS_SERVER_VERSION } from '../pm-conversations/pmConversationsModel'; -const messageFetchStart = (narrow: Narrow, numBefore: number, numAfter: number): Action => ({ +const messageFetchStart = ( + narrow: Narrow, + numBefore: number, + numAfter: number, +): PerAccountAction => ({ type: MESSAGE_FETCH_START, narrow, numBefore, numAfter, }); -const messageFetchError = (args: {| narrow: Narrow, error: Error |}): Action => { +const messageFetchError = (args: {| narrow: Narrow, error: Error |}): PerAccountAction => { const { narrow, error } = args; return { type: MESSAGE_FETCH_ERROR, @@ -64,7 +75,7 @@ const messageFetchComplete = (args: {| foundNewest: boolean, foundOldest: boolean, ownUserId: UserId, -|}): Action => { +|}): PerAccountAction => { const { messages, narrow, @@ -192,11 +203,11 @@ export const fetchNewer = (narrow: Narrow): ThunkAction => (dispatch, getS } }; -const registerStart = (): Action => ({ +const registerStart = (): PerAccountAction => ({ type: REGISTER_START, }); -const registerAbortPlain = (reason: RegisterAbortReason): Action => ({ +const registerAbortPlain = (reason: RegisterAbortReason): PerAccountAction => ({ type: REGISTER_ABORT, reason, }); @@ -256,7 +267,7 @@ export const registerAbort = (reason: RegisterAbortReason): ThunkAction ({ +const registerComplete = (data: InitialData): PerAccountAction => ({ type: REGISTER_COMPLETE, data, }); diff --git a/src/message/messagesReducer.js b/src/message/messagesReducer.js index 2b480e272a0..5877a0520a9 100644 --- a/src/message/messagesReducer.js +++ b/src/message/messagesReducer.js @@ -3,7 +3,7 @@ import omit from 'lodash.omit'; import Immutable from 'immutable'; -import type { MessagesState, Message, Action } from '../types'; +import type { MessagesState, Message, PerAccountApplicableAction } from '../types'; import { REGISTER_COMPLETE, LOGOUT, @@ -63,7 +63,10 @@ const eventNewMessage = (state, action) => { return state.set(action.message.id, omit(action.message, 'flags')); }; -export default (state: MessagesState = initialState, action: Action): MessagesState => { +export default ( + state: MessagesState = initialState, + action: PerAccountApplicableAction, +): MessagesState => { switch (action.type) { case REGISTER_COMPLETE: case LOGOUT: diff --git a/src/mute/muteReducer.js b/src/mute/muteReducer.js index 79d5e15e68f..0e2ecab87ad 100644 --- a/src/mute/muteReducer.js +++ b/src/mute/muteReducer.js @@ -1,11 +1,11 @@ /* @flow strict-local */ -import type { MuteState, Action } from '../types'; +import type { MuteState, PerAccountApplicableAction } from '../types'; import { REGISTER_COMPLETE, LOGOUT, ACCOUNT_SWITCH, EVENT_MUTED_TOPICS } from '../actionConstants'; import { NULL_ARRAY } from '../nullObjects'; const initialState: MuteState = NULL_ARRAY; -export default (state: MuteState = initialState, action: Action): MuteState => { +export default (state: MuteState = initialState, action: PerAccountApplicableAction): MuteState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/mute/mutedUsersReducer.js b/src/mute/mutedUsersReducer.js index 0b8a645edfa..d84ef4cf61b 100644 --- a/src/mute/mutedUsersReducer.js +++ b/src/mute/mutedUsersReducer.js @@ -1,7 +1,7 @@ /* @flow strict-local */ import Immutable from 'immutable'; -import type { MutedUsersState, Action, UserId } from '../types'; +import type { MutedUsersState, PerAccountApplicableAction, UserId } from '../types'; import { REGISTER_COMPLETE, LOGIN_SUCCESS, @@ -17,7 +17,10 @@ function mutedUsersToMap(muted_users: $ReadOnlyArray): Immutable.Map< return Immutable.Map(muted_users.map(muted_user => [muted_user.id, muted_user.timestamp])); } -export default (state: MutedUsersState = initialState, action: Action): MutedUsersState => { +export default ( + state: MutedUsersState = initialState, + action: PerAccountApplicableAction, +): MutedUsersState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/notification/notificationActions.js b/src/notification/notificationActions.js index 8d7559ad659..e1428d72f0e 100644 --- a/src/notification/notificationActions.js +++ b/src/notification/notificationActions.js @@ -6,7 +6,8 @@ import type { Dispatch, GlobalDispatch, Identity, - Action, + AccountIndependentAction, + AllAccountsAction, ThunkAction, GlobalThunkAction, } from '../types'; @@ -27,17 +28,17 @@ import { doNarrow } from '../message/messagesActions'; import { accountSwitch } from '../account/accountActions'; import { getIdentities, getAccount, tryGetActiveAccountState } from '../account/accountsSelectors'; -export const gotPushToken = (pushToken: string | null): Action => ({ +export const gotPushToken = (pushToken: string | null): AccountIndependentAction => ({ type: GOT_PUSH_TOKEN, pushToken, }); -export const unackPushToken = (identity: Identity): Action => ({ +export const unackPushToken = (identity: Identity): AllAccountsAction => ({ type: UNACK_PUSH_TOKEN, identity, }); -const ackPushToken = (pushToken: string, identity: Identity): Action => ({ +const ackPushToken = (pushToken: string, identity: Identity): AllAccountsAction => ({ type: ACK_PUSH_TOKEN, identity, pushToken, diff --git a/src/outbox/outboxActions.js b/src/outbox/outboxActions.js index 9f2db4795ab..818f0ccce79 100644 --- a/src/outbox/outboxActions.js +++ b/src/outbox/outboxActions.js @@ -13,7 +13,7 @@ import type { Stream, UserOrBot, UserId, - Action, + PerAccountAction, ThunkAction, } from '../types'; import type { SubsetProperties } from '../generics'; @@ -31,22 +31,22 @@ import { caseNarrowPartial, isConversationNarrow } from '../utils/narrow'; import { BackoffMachine } from '../utils/async'; import { recipientsOfPrivateMessage, streamNameOfStreamMessage } from '../utils/recipient'; -export const messageSendStart = (outbox: Outbox): Action => ({ +export const messageSendStart = (outbox: Outbox): PerAccountAction => ({ type: MESSAGE_SEND_START, outbox, }); -export const toggleOutboxSending = (sending: boolean): Action => ({ +export const toggleOutboxSending = (sending: boolean): PerAccountAction => ({ type: TOGGLE_OUTBOX_SENDING, sending, }); -export const deleteOutboxMessage = (localMessageId: number): Action => ({ +export const deleteOutboxMessage = (localMessageId: number): PerAccountAction => ({ type: DELETE_OUTBOX_MESSAGE, local_message_id: localMessageId, }); -export const messageSendComplete = (localMessageId: number): Action => ({ +export const messageSendComplete = (localMessageId: number): PerAccountAction => ({ type: MESSAGE_SEND_COMPLETE, local_message_id: localMessageId, }); diff --git a/src/outbox/outboxReducer.js b/src/outbox/outboxReducer.js index a9b9f3802de..b5c0a0f31ce 100644 --- a/src/outbox/outboxReducer.js +++ b/src/outbox/outboxReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { OutboxState, Action, Outbox } from '../types'; +import type { OutboxState, PerAccountApplicableAction, Outbox } from '../types'; import { REGISTER_COMPLETE, MESSAGE_SEND_START, @@ -22,7 +22,10 @@ const messageSendStart = (state, action) => { return [...state, { ...action.outbox }]; }; -export default (state: OutboxState = initialState, action: Action): OutboxState => { +export default ( + state: OutboxState = initialState, + action: PerAccountApplicableAction, +): OutboxState => { switch (action.type) { case REGISTER_COMPLETE: return filterArray(state, (outbox: Outbox) => !outbox.isSent); diff --git a/src/pm-conversations/pmConversationsModel.js b/src/pm-conversations/pmConversationsModel.js index 08cfebae8e3..ceeb4acc77a 100644 --- a/src/pm-conversations/pmConversationsModel.js +++ b/src/pm-conversations/pmConversationsModel.js @@ -11,7 +11,7 @@ import { } from '../actionConstants'; import { makeUserId } from '../api/idTypes'; -import type { Action, PmMessage, PmOutbox, UserId } from '../types'; +import type { PerAccountApplicableAction, PmMessage, PmOutbox, UserId } from '../types'; import { recipientsOfPrivateMessage } from '../utils/recipient'; import { ZulipVersion } from '../utils/zulipVersion'; @@ -181,7 +181,7 @@ function insertMessage(state, message, ownUserId) { export function reducer( state: PmConversationsState = initialState, - action: Action, + action: PerAccountApplicableAction, ): PmConversationsState { switch (action.type) { case LOGOUT: diff --git a/src/presence/presenceReducer.js b/src/presence/presenceReducer.js index 87ad19b2520..4f203e6def1 100644 --- a/src/presence/presenceReducer.js +++ b/src/presence/presenceReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { PresenceState, Action } from '../types'; +import type { PresenceState, PerAccountApplicableAction } from '../types'; import { LOGOUT, LOGIN_SUCCESS, @@ -14,7 +14,10 @@ import objectEntries from '../utils/objectEntries'; const initialState: PresenceState = NULL_OBJECT; -export default (state: PresenceState = initialState, action: Action): PresenceState => { +export default ( + state: PresenceState = initialState, + action: PerAccountApplicableAction, +): PresenceState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: diff --git a/src/realm/realmReducer.js b/src/realm/realmReducer.js index 639bd32b914..a2159d24dcb 100644 --- a/src/realm/realmReducer.js +++ b/src/realm/realmReducer.js @@ -1,5 +1,10 @@ /* @flow strict-local */ -import type { RealmState, Action, RealmEmojiById, VideoChatProvider } from '../types'; +import type { + RealmState, + PerAccountApplicableAction, + RealmEmojiById, + VideoChatProvider, +} from '../types'; import { REGISTER_COMPLETE, EVENT_REALM_EMOJI_UPDATE, @@ -50,7 +55,10 @@ function getVideoChatProvider({ } } -export default (state: RealmState = initialState, action: Action): RealmState => { +export default ( + state: RealmState = initialState, + action: PerAccountApplicableAction, +): RealmState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: diff --git a/src/reduxTypes.js b/src/reduxTypes.js index 7edf0a2cc1b..cb771dc0d25 100644 --- a/src/reduxTypes.js +++ b/src/reduxTypes.js @@ -11,7 +11,7 @@ import type Immutable from 'immutable'; import type { InputSelector } from 'reselect'; import type { Account, Outbox } from './types'; -import type { Action } from './actionTypes'; +import type { Action, DispatchableWithoutAccountAction } from './actionTypes'; import type { Topic, Message, @@ -556,7 +556,7 @@ export type ThunkAction = (Dispatch, () => PerAccountState, ThunkExtras) => T /** The Redux `dispatch` for a global context. */ export interface GlobalDispatch { - (action: A): A; + (action: A): A; (GlobalThunkAction): T; } @@ -576,4 +576,5 @@ export type GlobalThunkAction = (GlobalDispatch, () => GlobalState) => T; (d: Dispatch): GlobalDispatch => d; // $FlowExpectedError[incompatible-exact] // $FlowExpectedError[prop-missing] +// $FlowExpectedError[incompatible-return] (a: GlobalThunkAction): ThunkAction => a; diff --git a/src/session/sessionActions.js b/src/session/sessionActions.js index 93986f7308c..4b2b7c150d6 100644 --- a/src/session/sessionActions.js +++ b/src/session/sessionActions.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { Action, Orientation } from '../types'; +import type { PerAccountAction, AccountIndependentAction, Orientation } from '../types'; import { APP_ONLINE, APP_ORIENTATION, @@ -8,26 +8,26 @@ import { DISMISS_SERVER_COMPAT_NOTICE, } from '../actionConstants'; -export const appOnline = (isOnline: boolean | null): Action => ({ +export const appOnline = (isOnline: boolean | null): AccountIndependentAction => ({ type: APP_ONLINE, isOnline, }); -export const deadQueue = (): Action => ({ +export const deadQueue = (): PerAccountAction => ({ type: DEAD_QUEUE, }); -export const appOrientation = (orientation: Orientation): Action => ({ +export const appOrientation = (orientation: Orientation): AccountIndependentAction => ({ type: APP_ORIENTATION, orientation, }); -export const debugFlagToggle = (key: string, value: boolean): Action => ({ +export const debugFlagToggle = (key: string, value: boolean): AccountIndependentAction => ({ type: DEBUG_FLAG_TOGGLE, key, value, }); -export const dismissCompatNotice = (): Action => ({ +export const dismissCompatNotice = (): PerAccountAction => ({ type: DISMISS_SERVER_COMPAT_NOTICE, }); diff --git a/src/settings/LanguageScreen.js b/src/settings/LanguageScreen.js index 79196162459..d7b4d6aca47 100644 --- a/src/settings/LanguageScreen.js +++ b/src/settings/LanguageScreen.js @@ -9,7 +9,7 @@ import { useGlobalSelector, useDispatch } from '../react-redux'; import { Screen } from '../common'; import LanguagePicker from './LanguagePicker'; import { getGlobalSettings } from '../selectors'; -import { settingsChange } from '../actions'; +import { setGlobalSettings } from '../actions'; type Props = $ReadOnly<{| navigation: AppNavigationProp<'language'>, @@ -24,7 +24,7 @@ export default function LanguageScreen(props: Props): Node { const handleLocaleChange = useCallback( (value: string) => { - dispatch(settingsChange({ language: value })); + dispatch(setGlobalSettings({ language: value })); }, [dispatch], ); diff --git a/src/settings/NotificationsScreen.js b/src/settings/NotificationsScreen.js index 15aa9705b14..e77cd2f800a 100644 --- a/src/settings/NotificationsScreen.js +++ b/src/settings/NotificationsScreen.js @@ -5,11 +5,10 @@ import type { Node } from 'react'; import type { RouteProp } from '../react-navigation'; import type { AppNavigationProp } from '../nav/AppNavigator'; -import { useSelector, useDispatch } from '../react-redux'; +import { useSelector } from '../react-redux'; import { getAuth, getSettings } from '../selectors'; import { SwitchRow, Screen } from '../common'; import * as api from '../api'; -import { settingsChange } from '../actions'; type Props = $ReadOnly<{| navigation: AppNavigationProp<'notifications'>, @@ -22,7 +21,9 @@ export default function NotificationsScreen(props: Props): Node { const offlineNotification = useSelector(state => getSettings(state).offlineNotification); const onlineNotification = useSelector(state => getSettings(state).onlineNotification); const streamNotification = useSelector(state => getSettings(state).streamNotification); - const dispatch = useDispatch(); + + // TODO(#3999): It'd be good to show "working on it" UI feedback while a + // request is pending, after the user touches a switch. const handleOfflineNotificationChange = useCallback(() => { api.toggleMobilePushSettings({ @@ -30,8 +31,7 @@ export default function NotificationsScreen(props: Props): Node { opp: 'offline_notification_change', value: !offlineNotification, }); - dispatch(settingsChange({ offlineNotification: !offlineNotification })); - }, [offlineNotification, auth, dispatch]); + }, [offlineNotification, auth]); const handleOnlineNotificationChange = useCallback(() => { api.toggleMobilePushSettings({ @@ -39,8 +39,7 @@ export default function NotificationsScreen(props: Props): Node { opp: 'online_notification_change', value: !onlineNotification, }); - dispatch(settingsChange({ onlineNotification: !onlineNotification })); - }, [onlineNotification, auth, dispatch]); + }, [onlineNotification, auth]); const handleStreamNotificationChange = useCallback(() => { api.toggleMobilePushSettings({ @@ -48,8 +47,7 @@ export default function NotificationsScreen(props: Props): Node { opp: 'stream_notification_change', value: !streamNotification, }); - dispatch(settingsChange({ streamNotification: !streamNotification })); - }, [streamNotification, auth, dispatch]); + }, [streamNotification, auth]); return ( diff --git a/src/settings/SettingsScreen.js b/src/settings/SettingsScreen.js index 266efff4550..472d3b106f4 100644 --- a/src/settings/SettingsScreen.js +++ b/src/settings/SettingsScreen.js @@ -16,7 +16,7 @@ import { IconMoreHorizontal, } from '../common/Icons'; import { - settingsChange, + setGlobalSettings, navigateToNotifications, navigateToLanguage, navigateToDiagnostics, @@ -38,7 +38,7 @@ export default function SettingsScreen(props: Props): Node { const dispatch = useDispatch(); const handleThemeChange = useCallback(() => { - dispatch(settingsChange({ theme: theme === 'default' ? 'night' : 'default' })); + dispatch(setGlobalSettings({ theme: theme === 'default' ? 'night' : 'default' })); }, [theme, dispatch]); return ( @@ -48,14 +48,14 @@ export default function SettingsScreen(props: Props): Node { label="Open links with in-app browser" value={shouldUseInAppBrowser(browser)} onValueChange={value => { - dispatch(settingsChange({ browser: value ? 'embedded' : 'external' })); + dispatch(setGlobalSettings({ browser: value ? 'embedded' : 'external' })); }} /> { - dispatch(settingsChange({ doNotMarkMessagesAsRead: value })); + dispatch(setGlobalSettings({ doNotMarkMessagesAsRead: value })); }} /> { }); }); - describe('SETTINGS_CHANGE', () => { + describe('SET_GLOBAL_SETTINGS', () => { test('changes value of a key', () => { const action = deepFreeze({ - type: SETTINGS_CHANGE, + type: SET_GLOBAL_SETTINGS, update: { theme: 'night' }, }); diff --git a/src/settings/settingsActions.js b/src/settings/settingsActions.js index 731e58a1e71..94de7553180 100644 --- a/src/settings/settingsActions.js +++ b/src/settings/settingsActions.js @@ -1,8 +1,10 @@ /* @flow strict-local */ -import type { Action, SettingsState } from '../types'; -import { SETTINGS_CHANGE } from '../actionConstants'; +import type { AccountIndependentAction, GlobalSettingsState } from '../types'; +import { SET_GLOBAL_SETTINGS } from '../actionConstants'; -export const settingsChange = (update: $Shape): Action => ({ - type: SETTINGS_CHANGE, +export const setGlobalSettings = ( + update: $Shape<$Exact>, +): AccountIndependentAction => ({ + type: SET_GLOBAL_SETTINGS, update, }); diff --git a/src/settings/settingsReducer.js b/src/settings/settingsReducer.js index e1aada75c52..0cb43a1d48b 100644 --- a/src/settings/settingsReducer.js +++ b/src/settings/settingsReducer.js @@ -1,7 +1,7 @@ /* @flow strict-local */ import type { SettingsState, Action } from '../types'; import { - SETTINGS_CHANGE, + SET_GLOBAL_SETTINGS, REGISTER_COMPLETE, EVENT_UPDATE_GLOBAL_NOTIFICATIONS_SETTINGS, } from '../actionConstants'; @@ -28,7 +28,7 @@ export default (state: SettingsState = initialState, action: Action): SettingsSt streamNotification: action.data.enable_stream_push_notifications, }; - case SETTINGS_CHANGE: + case SET_GLOBAL_SETTINGS: return { ...state, ...action.update, diff --git a/src/streams/streamsReducer.js b/src/streams/streamsReducer.js index fd73adc2cb4..3473809b83a 100644 --- a/src/streams/streamsReducer.js +++ b/src/streams/streamsReducer.js @@ -1,6 +1,6 @@ /* @flow strict-local */ import { EventTypes } from '../api/eventTypes'; -import type { Action, StreamsState } from '../types'; +import type { PerAccountApplicableAction, StreamsState } from '../types'; import { ensureUnreachable } from '../types'; import { LOGOUT, ACCOUNT_SWITCH, EVENT, REGISTER_COMPLETE } from '../actionConstants'; import { NULL_ARRAY } from '../nullObjects'; @@ -8,7 +8,10 @@ import { filterArray } from '../utils/immutability'; const initialState: StreamsState = NULL_ARRAY; -export default (state: StreamsState = initialState, action: Action): StreamsState => { +export default ( + state: StreamsState = initialState, + action: PerAccountApplicableAction, +): StreamsState => { switch (action.type) { case REGISTER_COMPLETE: return action.data.streams || initialState; diff --git a/src/subscriptions/subscriptionsReducer.js b/src/subscriptions/subscriptionsReducer.js index c61ca6883a2..3b28221f08d 100644 --- a/src/subscriptions/subscriptionsReducer.js +++ b/src/subscriptions/subscriptionsReducer.js @@ -1,6 +1,6 @@ /* @flow strict-local */ import { EventTypes } from '../api/eventTypes'; -import type { SubscriptionsState, Action } from '../types'; +import type { SubscriptionsState, PerAccountApplicableAction } from '../types'; import { ensureUnreachable } from '../types'; import { LOGOUT, @@ -20,7 +20,10 @@ const updateSubscription = (state, event) => sub.stream_id === event.stream_id ? { ...sub, [event.property]: event.value } : sub, ); -export default (state: SubscriptionsState = initialState, action: Action): SubscriptionsState => { +export default ( + state: SubscriptionsState = initialState, + action: PerAccountApplicableAction, +): SubscriptionsState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: diff --git a/src/topics/topicActions.js b/src/topics/topicActions.js index e45c04771c0..6e653368e94 100644 --- a/src/topics/topicActions.js +++ b/src/topics/topicActions.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { Narrow, Topic, Action, ThunkAction, Outbox } from '../types'; +import type { Narrow, Topic, PerAccountAction, ThunkAction, Outbox } from '../types'; import * as api from '../api'; import { INIT_TOPICS } from '../actionConstants'; import { isStreamNarrow, streamNameOfNarrow } from '../utils/narrow'; @@ -7,7 +7,7 @@ import { getAuth, getStreams } from '../selectors'; import { deleteOutboxMessage } from '../actions'; import { getOutbox } from '../directSelectors'; -export const initTopics = (topics: Topic[], streamId: number): Action => ({ +export const initTopics = (topics: Topic[], streamId: number): PerAccountAction => ({ type: INIT_TOPICS, topics, streamId, diff --git a/src/topics/topicsReducer.js b/src/topics/topicsReducer.js index 8695a2065b5..6a264215614 100644 --- a/src/topics/topicsReducer.js +++ b/src/topics/topicsReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { TopicsState, Action } from '../types'; +import type { TopicsState, PerAccountApplicableAction } from '../types'; import { LOGOUT, ACCOUNT_SWITCH, INIT_TOPICS, EVENT_NEW_MESSAGE } from '../actionConstants'; import { NULL_OBJECT } from '../nullObjects'; import { replaceItemInArray } from '../utils/immutability'; @@ -36,7 +36,10 @@ const eventNewMessage = (state, action) => { }; }; -export default (state: TopicsState = initialState, action: Action): TopicsState => { +export default ( + state: TopicsState = initialState, + action: PerAccountApplicableAction, +): TopicsState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/typing/__tests__/typingReducer-test.js b/src/typing/__tests__/typingReducer-test.js index 53ee68b6b30..ff07e217309 100644 --- a/src/typing/__tests__/typingReducer-test.js +++ b/src/typing/__tests__/typingReducer-test.js @@ -2,7 +2,7 @@ import deepFreeze from 'deep-freeze'; -import type { Action, User } from '../../types'; +import type { PerAccountAction, User } from '../../types'; import { EVENT_TYPING_START, EVENT_TYPING_STOP } from '../../actionConstants'; import typingReducer from '../typingReducer'; import { NULL_OBJECT } from '../../nullObjects'; @@ -18,7 +18,7 @@ describe('typingReducer', () => { sender: User, recipients: $ReadOnlyArray, time: number, - |}): Action => { + |}): PerAccountAction => { const { op, sender, recipients, time } = args; const base = { id: 123, diff --git a/src/typing/typingActions.js b/src/typing/typingActions.js index ebb5f725865..d5676b31220 100644 --- a/src/typing/typingActions.js +++ b/src/typing/typingActions.js @@ -1,10 +1,10 @@ /* @flow strict-local */ -import type { Action, ThunkAction } from '../types'; +import type { PerAccountAction, ThunkAction } from '../types'; import { sleep } from '../utils/async'; import { getTyping } from '../directSelectors'; -export const clearTyping = (outdatedNotifications: string[]): Action => ({ +export const clearTyping = (outdatedNotifications: string[]): PerAccountAction => ({ type: 'CLEAR_TYPING', outdatedNotifications, }); diff --git a/src/typing/typingReducer.js b/src/typing/typingReducer.js index f85d3af58da..196dfb96b85 100644 --- a/src/typing/typingReducer.js +++ b/src/typing/typingReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { Action, TypingState } from '../types'; +import type { PerAccountApplicableAction, TypingState } from '../types'; import { EVENT_TYPING_START, EVENT_TYPING_STOP, @@ -83,7 +83,10 @@ const clearTyping = (state, action) => { return newState; }; -export default (state: TypingState = initialState, action: Action): TypingState => { +export default ( + state: TypingState = initialState, + action: PerAccountApplicableAction, +): TypingState => { switch (action.type) { case EVENT_TYPING_START: return eventTypingStart(state, action); diff --git a/src/unread/unreadHuddlesReducer.js b/src/unread/unreadHuddlesReducer.js index 0fd4c357a48..c24b40cfda3 100644 --- a/src/unread/unreadHuddlesReducer.js +++ b/src/unread/unreadHuddlesReducer.js @@ -1,7 +1,7 @@ /* @flow strict-local */ import invariant from 'invariant'; -import type { Action } from '../types'; +import type { PerAccountApplicableAction } from '../types'; import type { UnreadHuddlesState } from './unreadModelTypes'; import { REGISTER_COMPLETE, @@ -54,7 +54,10 @@ const eventUpdateMessageFlags = (state, action) => { return state; }; -export default (state: UnreadHuddlesState = initialState, action: Action): UnreadHuddlesState => { +export default ( + state: UnreadHuddlesState = initialState, + action: PerAccountApplicableAction, +): UnreadHuddlesState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/unread/unreadMentionsReducer.js b/src/unread/unreadMentionsReducer.js index 30b69f4e2db..cca0fd1cd68 100644 --- a/src/unread/unreadMentionsReducer.js +++ b/src/unread/unreadMentionsReducer.js @@ -1,7 +1,7 @@ /* @flow strict-local */ import invariant from 'invariant'; -import type { Action } from '../types'; +import type { PerAccountApplicableAction } from '../types'; import type { UnreadMentionsState } from './unreadModelTypes'; import { REGISTER_COMPLETE, @@ -34,7 +34,10 @@ const eventUpdateMessageFlags = (state, action) => { return state; }; -export default (state: UnreadMentionsState = initialState, action: Action): UnreadMentionsState => { +export default ( + state: UnreadMentionsState = initialState, + action: PerAccountApplicableAction, +): UnreadMentionsState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/unread/unreadModel.js b/src/unread/unreadModel.js index 1a3e6e83e6f..1218b5da2c3 100644 --- a/src/unread/unreadModel.js +++ b/src/unread/unreadModel.js @@ -2,7 +2,7 @@ import Immutable from 'immutable'; import invariant from 'invariant'; -import type { Action } from '../actionTypes'; +import type { PerAccountApplicableAction } from '../actionTypes'; import type { UnreadState, UnreadStreamsState, @@ -118,7 +118,7 @@ function deleteMessages( function streamsReducer( state: UnreadStreamsState = initialStreamsState, - action: Action, + action: PerAccountApplicableAction, globalState: PerAccountState, ): UnreadStreamsState { switch (action.type) { @@ -210,7 +210,7 @@ function streamsReducer( export const reducer = ( state: void | UnreadState, - action: Action, + action: PerAccountApplicableAction, globalState: PerAccountState, ): UnreadState => { const nextState = { diff --git a/src/unread/unreadPmsReducer.js b/src/unread/unreadPmsReducer.js index 232c9edeb09..0724ce1987b 100644 --- a/src/unread/unreadPmsReducer.js +++ b/src/unread/unreadPmsReducer.js @@ -1,7 +1,7 @@ /* @flow strict-local */ import invariant from 'invariant'; -import type { Action } from '../types'; +import type { PerAccountApplicableAction } from '../types'; import type { UnreadPmsState } from './unreadModelTypes'; import { REGISTER_COMPLETE, @@ -52,7 +52,10 @@ const eventUpdateMessageFlags = (state, action) => { return state; }; -export default (state: UnreadPmsState = initialState, action: Action): UnreadPmsState => { +export default ( + state: UnreadPmsState = initialState, + action: PerAccountApplicableAction, +): UnreadPmsState => { switch (action.type) { case LOGOUT: case ACCOUNT_SWITCH: diff --git a/src/user-groups/userGroupsReducer.js b/src/user-groups/userGroupsReducer.js index 07c97a5b6bb..c13741dffb5 100644 --- a/src/user-groups/userGroupsReducer.js +++ b/src/user-groups/userGroupsReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { UserGroupsState, Action } from '../types'; +import type { UserGroupsState, PerAccountApplicableAction } from '../types'; import { LOGOUT, LOGIN_SUCCESS, @@ -45,7 +45,10 @@ const eventUserGroupRemoveMembers = (state, action) => }, ); -export default (state: UserGroupsState = initialState, action: Action): UserGroupsState => { +export default ( + state: UserGroupsState = initialState, + action: PerAccountApplicableAction, +): UserGroupsState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: diff --git a/src/user-status/userStatusReducer.js b/src/user-status/userStatusReducer.js index 93da107a890..f72041c1b45 100644 --- a/src/user-status/userStatusReducer.js +++ b/src/user-status/userStatusReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { UserStatusState, Action } from '../types'; +import type { UserStatusState, PerAccountApplicableAction } from '../types'; import { LOGOUT, LOGIN_SUCCESS, @@ -11,7 +11,10 @@ import { NULL_OBJECT } from '../nullObjects'; const initialState: UserStatusState = NULL_OBJECT; -export default (state: UserStatusState = initialState, action: Action): UserStatusState => { +export default ( + state: UserStatusState = initialState, + action: PerAccountApplicableAction, +): UserStatusState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: diff --git a/src/users/usersReducer.js b/src/users/usersReducer.js index b79332d692d..5343a293c8a 100644 --- a/src/users/usersReducer.js +++ b/src/users/usersReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { UsersState, Action } from '../types'; +import type { UsersState, PerAccountApplicableAction } from '../types'; import { LOGOUT, LOGIN_SUCCESS, @@ -13,7 +13,10 @@ import { NULL_ARRAY } from '../nullObjects'; const initialState: UsersState = NULL_ARRAY; -export default (state: UsersState = initialState, action: Action): UsersState => { +export default ( + state: UsersState = initialState, + action: PerAccountApplicableAction, +): UsersState => { switch (action.type) { case LOGOUT: case LOGIN_SUCCESS: