diff --git a/src/actionTypes.js b/src/actionTypes.js index 74f17b95153..1f03382c60d 100644 --- a/src/actionTypes.js +++ b/src/actionTypes.js @@ -110,7 +110,7 @@ import type { ZulipVersion } from './utils/zulipVersion'; */ type RehydrateAction = {| type: typeof REHYDRATE, - payload: GlobalState | { accounts: null } | {||} | void, + payload: $ReadOnly<$ObjMap<$Rest, (V) => V | null>> | void, error: mixed, |}; diff --git a/src/boot/store.js b/src/boot/store.js index a103f75859a..88896b0e794 100644 --- a/src/boot/store.js +++ b/src/boot/store.js @@ -8,6 +8,7 @@ import Immutable from 'immutable'; import { persistStore, autoRehydrate } from '../third/redux-persist'; import type { Config } from '../third/redux-persist'; +import type { ReadWrite } from '../generics'; import { ZulipVersion } from '../utils/zulipVersion'; import { stringify, parse } from './replaceRevive'; import type { Action, GlobalState } from '../types'; @@ -88,13 +89,15 @@ export const cacheKeys: Array<$Keys> = [ * that happens. */ function dropCache(state: GlobalState): $Shape { - const result: $Shape = {}; + const result: $Shape> = {}; storeKeys.forEach(key => { - /* $FlowFixMe[cannot-write] - This is well-typed only because it's the same `key` twice. It seems - like we should have to suppress an error about not having that - guarantee, in addition to the more minor-looking `cannot-write`. Not - sure why we don't. */ + // $FlowFixMe[incompatible-indexer] + // $FlowFixMe[incompatible-exact] + // $FlowFixMe[prop-missing] + // $FlowFixMe[incompatible-variance] + // $FlowFixMe[incompatible-type-arg] + /* $FlowFixMe[incompatible-type] + This is well-typed only because it's the same `key` twice. */ result[key] = state[key]; }); return result; diff --git a/src/chat/narrowsSelectors.js b/src/chat/narrowsSelectors.js index 270e244284d..e0f53550ac1 100644 --- a/src/chat/narrowsSelectors.js +++ b/src/chat/narrowsSelectors.js @@ -113,7 +113,7 @@ export const getLastMessageId = (state: GlobalState, narrow: Narrow): number | v // Prettier mishandles this Flow syntax. // prettier-ignore -// TODO: clean up what this returns. +// TODO: clean up what this returns; possibly to just `Stream` export const getStreamInNarrow: Selector = createSelector( (state, narrow) => narrow, state => getSubscriptions(state), diff --git a/src/generics.js b/src/generics.js index 5f9a0b73146..51b99914db0 100644 --- a/src/generics.js +++ b/src/generics.js @@ -47,6 +47,17 @@ export type BoundedDiff<-U, -L> = $Diff< $ObjMap mixed>, >; +/** + * The object type `T` with its readonly annotation stripped. + */ +// The implementation relies on facebook/flow#6225, so it will +// naturally have to change when that gets fixed. +// +// See discussion for an alternative, with `$ObjMap`, that seemed like +// it was going to work, but didn't: +// https://github.com/zulip/zulip-mobile/pull/4520#discussion_r593394451. +export type ReadWrite> = $Diff; + /** * An object type with a subset of T's properties, namely those in U. * diff --git a/src/message/NotSubscribed.js b/src/message/NotSubscribed.js index 7d6ad6820d1..ab0eea10aa0 100644 --- a/src/message/NotSubscribed.js +++ b/src/message/NotSubscribed.js @@ -12,7 +12,7 @@ import styles from '../styles'; type SelectorProps = $ReadOnly<{| auth: Auth, - stream: { ...Stream }, + stream: { ...Stream, ... }, |}>; type Props = $ReadOnly<{| diff --git a/src/notification/index.js b/src/notification/index.js index b9e29199903..a0cf4bed391 100644 --- a/src/notification/index.js +++ b/src/notification/index.js @@ -268,7 +268,7 @@ export class NotificationListener { // On iOS, `note` should be an IOSNotifications object. The notification // data it returns from `getData` is unvalidated -- it comes almost // straight off the wire from the server. - this.listen('notificationOpened', (note: { getData(): JSONableDict }) => { + this.listen('notificationOpened', (note: { getData(): JSONableDict, ... }) => { const data = fromAPNs(note.getData()); if (data) { this.handleNotificationOpen(data); diff --git a/src/redux-persist-migrate/index.js b/src/redux-persist-migrate/index.js index 25dcb183aed..69c1f3a6b25 100644 --- a/src/redux-persist-migrate/index.js +++ b/src/redux-persist-migrate/index.js @@ -75,8 +75,6 @@ export function createMigrationImpl( throw new Error('createMigration: bad arguments'); } if (action.type === REHYDRATE) { - // $FlowIgnore[prop-missing] - // $FlowIgnore[incompatible-exact] /* $FlowIgnore[incompatible-type] this really is a lie -- and kind of central to migration */ const incomingState: State = action.payload; diff --git a/src/session/sessionReducer.js b/src/session/sessionReducer.js index f49f990ee16..5590cbf794a 100644 --- a/src/session/sessionReducer.js +++ b/src/session/sessionReducer.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import type { Debug, Orientation, Action } from '../types'; +import type { GlobalState, Debug, Orientation, Action } from '../types'; import { REHYDRATE, DEAD_QUEUE, @@ -93,15 +93,26 @@ const initialState: SessionState = { const rehydrate = (state, action) => { const { payload } = action; - const haveApiKey = !!(payload && payload.accounts && hasAuth(payload)); + + /* $FlowIgnore: The actual type allows any property to be null; narrow + that to just the one that `hasAuth` will care about. (What we really + want here is what the value of `hasAuth` will be after the rehydrate + is complete. So even if some other property is null in the payload, + we still do want to ask `hasAuth` what it thinks.) */ + const payloadForHasAuth = (payload: GlobalState | { accounts: null, ... } | void); + const haveApiKey = !!( + payloadForHasAuth + && payloadForHasAuth.accounts + && hasAuth(payloadForHasAuth) + ); + return { ...state, isHydrated: true, // On rehydration, do an initial fetch if we have access to an account // (indicated by the presence of an api key). Otherwise, the initial fetch // will be initiated on loginSuccess. - // NB `InitialNavigationDispatcher`'s `doInitialNavigation` - // depends intimately on this behavior. + // NB `getInitialRouteInfo` depends intimately on this behavior. needsInitialFetch: haveApiKey, }; };