Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ import type { ZulipVersion } from './utils/zulipVersion';
*/
type RehydrateAction = {|
type: typeof REHYDRATE,
payload: GlobalState | { accounts: null } | {||} | void,
payload: $ReadOnly<$ObjMap<$Rest<GlobalState, { ... }>, <V>(V) => V | null>> | void,
error: mixed,
|};

Expand Down
15 changes: 9 additions & 6 deletions src/boot/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -88,13 +89,15 @@ export const cacheKeys: Array<$Keys<GlobalState>> = [
* that happens.
*/
function dropCache(state: GlobalState): $Shape<GlobalState> {
const result: $Shape<GlobalState> = {};
const result: $Shape<ReadWrite<GlobalState>> = {};
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;
Expand Down
2 changes: 1 addition & 1 deletion src/chat/narrowsSelectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<Subscription | {| ...Stream, in_home_view: boolean |}, Narrow> = createSelector(
(state, narrow) => narrow,
state => getSubscriptions(state),
Expand Down
11 changes: 11 additions & 0 deletions src/generics.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ export type BoundedDiff<-U, -L> = $Diff<
$ObjMap<L, () => 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<T: $ReadOnly<{ ... }>> = $Diff<T, {||}>;

/**
* An object type with a subset of T's properties, namely those in U.
*
Expand Down
2 changes: 1 addition & 1 deletion src/message/NotSubscribed.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import styles from '../styles';

type SelectorProps = $ReadOnly<{|
auth: Auth,
stream: { ...Stream },
stream: { ...Stream, ... },
|}>;

type Props = $ReadOnly<{|
Expand Down
2 changes: 1 addition & 1 deletion src/notification/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 0 additions & 2 deletions src/redux-persist-migrate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 15 additions & 4 deletions src/session/sessionReducer.js
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
};
};
Expand Down