Skip to content

Commit

Permalink
flow: Annotate exported thunk action creators.
Browse files Browse the repository at this point in the history
Following Greg's recommendation at
  https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/Flow.20types-first/near/1237907.

Done by using the codemod [1] with the following command, then
removing irrelevant changes, then making the annotations a bit more
concise.

  yarn flow codemod annotate-exports --write --repeat \
    --log-level info src/ 2>out3.log

This will help move us along toward Flow's new "Types-First" mode;
switching entirely is zulip#4907.

[1] https://flow.org/en/docs/cli/annotate-exports/
  • Loading branch information
chrisbobbe committed Aug 4, 2021
1 parent 55859c6 commit 6ffe4c0
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 83 deletions.
12 changes: 6 additions & 6 deletions src/account/accountActions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @flow strict-local */
import * as NavigationService from '../nav/NavigationService';
import type { Action, Dispatch, GetState } from '../types';
import type { Action, ThunkAction } from '../types';
import { ACCOUNT_SWITCH, ACCOUNT_REMOVE, LOGIN_SUCCESS, LOGOUT } from '../actionConstants';
import { resetToAccountPicker, resetToMainTabs } from '../nav/navActions';

Expand All @@ -9,7 +9,7 @@ const accountSwitchPlain = (index: number): Action => ({
index,
});

export const accountSwitch = (index: number) => (dispatch: Dispatch, getState: GetState) => {
export const accountSwitch = (index: number): ThunkAction<void> => (dispatch, getState) => {
NavigationService.dispatch(resetToMainTabs());
dispatch(accountSwitchPlain(index));
};
Expand All @@ -26,9 +26,9 @@ const loginSuccessPlain = (realm: URL, email: string, apiKey: string): Action =>
apiKey,
});

export const loginSuccess = (realm: URL, email: string, apiKey: string) => (
dispatch: Dispatch,
getState: GetState,
export const loginSuccess = (realm: URL, email: string, apiKey: string): ThunkAction<void> => (
dispatch,
getState,
) => {
NavigationService.dispatch(resetToMainTabs());
dispatch(loginSuccessPlain(realm, email, apiKey));
Expand All @@ -38,7 +38,7 @@ const logoutPlain = (): Action => ({
type: LOGOUT,
});

export const logout = () => async (dispatch: Dispatch, getState: GetState) => {
export const logout = (): ThunkAction<Promise<void>> => async (dispatch, getState) => {
NavigationService.dispatch(resetToAccountPicker());
dispatch(logoutPlain());
};
4 changes: 2 additions & 2 deletions src/events/doEventActionSideEffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// import { Vibration } from 'react-native';

import { AppState } from 'react-native';
import type { GlobalState, GetState, Dispatch, Message } from '../types';
import type { GlobalState, Message, ThunkAction } from '../types';
import type { EventAction } from '../actionTypes';
import { EVENT_NEW_MESSAGE, EVENT_TYPING_START } from '../actionConstants';
import { isHomeNarrow, isMessageInNarrow } from '../utils/narrow';
Expand Down Expand Up @@ -48,7 +48,7 @@ const messageEvent = (state: GlobalState, message: Message): void => {
*
* To be dispatched before the event actions are dispatched.
*/
export default (action: EventAction) => async (dispatch: Dispatch, getState: GetState) => {
export default (action: EventAction): ThunkAction<Promise<void>> => async (dispatch, getState) => {
const state = getState();
switch (action.type) {
case EVENT_NEW_MESSAGE: {
Expand Down
10 changes: 5 additions & 5 deletions src/events/eventActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @flow strict-local */
import type { Dispatch, GeneralEvent, GetState } from '../types';
import type { Dispatch, GeneralEvent, GetState, ThunkAction } from '../types';
import * as api from '../api';
import { logout } from '../account/accountActions';
import { deadQueue } from '../session/sessionActions';
Expand Down Expand Up @@ -42,10 +42,10 @@ const handleEvent = (event: GeneralEvent, dispatch: Dispatch, getState: GetState
* This is part of our use of the Zulip events system; see `doInitialFetch`
* for discussion.
*/
export const startEventPolling = (queueId: number, eventId: number) => async (
dispatch: Dispatch,
getState: GetState,
) => {
export const startEventPolling = (
queueId: number,
eventId: number,
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
let lastEventId = eventId;

const backoffMachine = new BackoffMachine();
Expand Down
37 changes: 23 additions & 14 deletions src/message/fetchActions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
/* @flow strict-local */
import * as logging from '../utils/logging';
import * as NavigationService from '../nav/NavigationService';
import type { Narrow, Dispatch, GetState, GlobalState, Message, Action, UserId } from '../types';
import type {
Narrow,
Dispatch,
GetState,
GlobalState,
Message,
Action,
ThunkAction,
UserId,
} from '../types';
import { ensureUnreachable } from '../types';
import type { InitialFetchAbortReason } from '../actionTypes';
import type { ApiResponseServerSettings } from '../api/settings/getServerSettings';
Expand Down Expand Up @@ -102,7 +111,7 @@ export const fetchMessages = (fetchArgs: {|
anchor: number,
numBefore: number,
numAfter: number,
|}) => async (dispatch: Dispatch, getState: GetState): Promise<Message[]> => {
|}): ThunkAction<Promise<Message[]>> => async (dispatch, getState) => {
dispatch(messageFetchStart(fetchArgs.narrow, fetchArgs.numBefore, fetchArgs.numAfter));
try {
const { messages, found_newest, found_oldest } =
Expand Down Expand Up @@ -156,7 +165,7 @@ export const fetchMessages = (fetchArgs: {|
}
};

export const fetchOlder = (narrow: Narrow) => (dispatch: Dispatch, getState: GetState) => {
export const fetchOlder = (narrow: Narrow): ThunkAction<void> => (dispatch, getState) => {
const state = getState();
const firstMessageId = getFirstMessageId(state, narrow);
const caughtUp = getCaughtUpForNarrow(state, narrow);
Expand All @@ -175,7 +184,7 @@ export const fetchOlder = (narrow: Narrow) => (dispatch: Dispatch, getState: Get
}
};

export const fetchNewer = (narrow: Narrow) => (dispatch: Dispatch, getState: GetState) => {
export const fetchNewer = (narrow: Narrow): ThunkAction<void> => (dispatch, getState) => {
const state = getState();
const lastMessageId = getLastMessageId(state, narrow);
const caughtUp = getCaughtUpForNarrow(state, narrow);
Expand Down Expand Up @@ -207,10 +216,9 @@ const initialFetchAbortPlain = (reason: InitialFetchAbortReason): Action => ({
reason,
});

export const initialFetchAbort = (reason: InitialFetchAbortReason) => async (
dispatch: Dispatch,
getState: GetState,
) => {
export const initialFetchAbort = (
reason: InitialFetchAbortReason,
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
showErrorAlert(
// TODO: Set up these user-facing strings for translation once
// `initialFetchAbort`'s callers all have access to a `GetText`
Expand Down Expand Up @@ -277,7 +285,7 @@ export const isFetchNeededAtAnchor = (
export const fetchMessagesInNarrow = (
narrow: Narrow,
anchor: number = FIRST_UNREAD_ANCHOR,
) => async (dispatch: Dispatch, getState: GetState): Promise<Message[] | void> => {
): ThunkAction<Promise<Message[] | void>> => async (dispatch, getState) => {
if (!isFetchNeededAtAnchor(getState(), narrow, anchor)) {
return undefined;
}
Expand Down Expand Up @@ -399,7 +407,7 @@ export async function tryFetch<T>(
* (`fetchOlder` and `fetchNewer`), and to grab search results
* (`SearchMessagesScreen`).
*/
export const doInitialFetch = () => async (dispatch: Dispatch, getState: GetState) => {
export const doInitialFetch = (): ThunkAction<Promise<void>> => async (dispatch, getState) => {
dispatch(initialFetchStart());
const auth = getAuth(getState());

Expand Down Expand Up @@ -479,10 +487,11 @@ export const doInitialFetch = () => async (dispatch: Dispatch, getState: GetStat
dispatch(initNotifications());
};

export const uploadFile = (narrow: Narrow, uri: string, name: string) => async (
dispatch: Dispatch,
getState: GetState,
) => {
export const uploadFile = (
narrow: Narrow,
uri: string,
name: string,
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
const auth = getAuth(getState());
const response = await api.uploadFile(auth, uri, name);
const messageToSend = `[${name}](${response.uri})`;
Expand Down
16 changes: 8 additions & 8 deletions src/message/messagesActions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @flow strict-local */
import * as NavigationService from '../nav/NavigationService';
import type { Narrow, Dispatch, GetState } from '../types';
import type { Narrow, ThunkAction } from '../types';
import { getAuth } from '../selectors';
import { getMessageIdFromLink, getNarrowFromLink } from '../utils/internalLinks';
import { openLinkWithUserPreference } from '../utils/openLink';
Expand All @@ -14,17 +14,17 @@ import { getOwnUserId } from '../users/userSelectors';
/**
* Navigate to the given narrow.
*/
export const doNarrow = (narrow: Narrow, anchor: number = FIRST_UNREAD_ANCHOR) => (
dispatch: Dispatch,
getState: GetState,
) => {
export const doNarrow = (
narrow: Narrow,
anchor: number = FIRST_UNREAD_ANCHOR,
): ThunkAction<void> => (dispatch, getState) => {
// TODO: Use `anchor` to open the message list to a particular message.
NavigationService.dispatch(navigateToChat(narrow));
};

export const messageLinkPress = (href: string) => async (
dispatch: Dispatch,
getState: GetState,
export const messageLinkPress = (href: string): ThunkAction<Promise<void>> => async (
dispatch,
getState,
) => {
const state = getState();
const auth = getAuth(state);
Expand Down
17 changes: 10 additions & 7 deletions src/notification/notificationActions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* @flow strict-local */
import { Platform } from 'react-native';

import type { Account, Dispatch, GetState, Identity, Action } from '../types';
import type { Account, Dispatch, Identity, Action, ThunkAction } from '../types';
import * as api from '../api';
import {
getNotificationToken,
Expand Down Expand Up @@ -36,9 +36,9 @@ const ackPushToken = (pushToken: string, identity: Identity): Action => ({
pushToken,
});

export const narrowToNotification = (data: ?Notification) => (
dispatch: Dispatch,
getState: GetState,
export const narrowToNotification = (data: ?Notification): ThunkAction<void> => (
dispatch,
getState,
) => {
if (!data) {
return;
Expand Down Expand Up @@ -79,7 +79,7 @@ const sendPushToken = async (dispatch: Dispatch, account: Account | void, pushTo
};

/** Tell all logged-in accounts' servers about our device token, as needed. */
export const sendAllPushToken = () => async (dispatch: Dispatch, getState: GetState) => {
export const sendAllPushToken = (): ThunkAction<Promise<void>> => async (dispatch, getState) => {
const { pushToken } = getSession(getState());
if (pushToken === null) {
return;
Expand All @@ -89,7 +89,7 @@ export const sendAllPushToken = () => async (dispatch: Dispatch, getState: GetSt
};

/** Tell the active account's server about our device token, if needed. */
export const initNotifications = () => async (dispatch: Dispatch, getState: GetState) => {
export const initNotifications = (): ThunkAction<Promise<void>> => async (dispatch, getState) => {
const { pushToken } = getSession(getState());
if (pushToken === null) {
// Probably, we just don't have the token yet. When we learn it,
Expand All @@ -112,7 +112,10 @@ export const initNotifications = () => async (dispatch: Dispatch, getState: GetS
await sendPushToken(dispatch, account, pushToken);
};

export const tryStopNotifications = () => async (dispatch: Dispatch, getState: GetState) => {
export const tryStopNotifications = (): ThunkAction<Promise<void>> => async (
dispatch,
getState,
) => {
const auth = getAuth(getState());
const { ackedPushToken } = getActiveAccount(getState());
innerStopNotifications(auth, ackedPushToken, dispatch);
Expand Down
9 changes: 5 additions & 4 deletions src/outbox/outboxActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
UserOrBot,
UserId,
Action,
ThunkAction,
} from '../types';
import type { SubsetProperties } from '../generics';
import {
Expand Down Expand Up @@ -94,7 +95,7 @@ export const trySendMessages = (dispatch: Dispatch, getState: GetState): boolean
}
};

export const sendOutbox = () => async (dispatch: Dispatch, getState: GetState) => {
export const sendOutbox = (): ThunkAction<Promise<void>> => async (dispatch, getState) => {
const state = getState();
if (state.outbox.length === 0 || state.session.outboxSending) {
return;
Expand Down Expand Up @@ -166,9 +167,9 @@ const getContentPreview = (content: string, state: GlobalState): string => {
}
};

export const addToOutbox = (narrow: Narrow, content: string) => async (
dispatch: Dispatch,
getState: GetState,
export const addToOutbox = (narrow: Narrow, content: string): ThunkAction<Promise<void>> => async (
dispatch,
getState,
) => {
const state = getState();
const ownUser = getOwnUser(state);
Expand Down
4 changes: 3 additions & 1 deletion src/reduxTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,5 +390,7 @@ export type PlainDispatch = <A: Action>(action: A) => A;

export interface Dispatch {
<A: Action>(action: A): A;
<T>((Dispatch, GetState) => T): T;
<T>(ThunkAction<T>): T; // eslint-disable-line no-use-before-define
}

export type ThunkAction<T> = (Dispatch, GetState) => T;
6 changes: 3 additions & 3 deletions src/streams/streamsActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @flow strict-local */
import type { GetState, Dispatch, Stream } from '../types';
import type { Stream, ThunkAction } from '../types';
import * as api from '../api';
import { getAuth } from '../selectors';

Expand All @@ -8,15 +8,15 @@ export const createNewStream = (
description: string,
principals: string[],
isPrivate: boolean,
) => async (dispatch: Dispatch, getState: GetState) => {
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
await api.createStream(getAuth(getState()), name, description, principals, isPrivate);
};

export const updateExistingStream = (
id: number,
initialValues: Stream,
newValues: {| name: string, description: string, isPrivate: boolean |},
) => async (dispatch: Dispatch, getState: GetState) => {
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
if (initialValues.name !== newValues.name) {
// Stream names might contain unsafe characters so we must encode it first.
await api.updateStream(getAuth(getState()), id, 'new_name', JSON.stringify(newValues.name));
Expand Down
4 changes: 2 additions & 2 deletions src/subscriptions/subscriptionsActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @flow strict-local */
import type { GetState, Dispatch } from '../types';
import type { ThunkAction } from '../types';
import { getAuth } from '../selectors';
import type { SubscriptionProperty } from '../api/subscriptions/setSubscriptionProperty';
import * as api from '../api';
Expand All @@ -8,6 +8,6 @@ export const setSubscriptionProperty = (
streamId: number,
property: SubscriptionProperty,
value: boolean,
) => async (dispatch: Dispatch, getState: GetState) => {
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
await api.setSubscriptionProperty(getAuth(getState()), streamId, property, value);
};
21 changes: 12 additions & 9 deletions src/topics/topicActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @flow strict-local */
import type { GetState, Dispatch, Narrow, Topic, Action, Outbox } from '../types';
import type { Narrow, Topic, Action, ThunkAction, Outbox } from '../types';
import * as api from '../api';
import { INIT_TOPICS } from '../actionConstants';
import { isStreamNarrow, streamNameOfNarrow } from '../utils/narrow';
Expand All @@ -14,15 +14,18 @@ export const initTopics = (topics: Topic[], streamId: number): Action => ({
streamId,
});

export const fetchTopics = (streamId: number) => async (dispatch: Dispatch, getState: GetState) => {
export const fetchTopics = (streamId: number): ThunkAction<Promise<void>> => async (
dispatch,
getState,
) => {
const auth = getAuth(getState());
const { topics } = await api.getTopics(auth, streamId);
dispatch(initTopics(topics, streamId));
};

export const fetchTopicsForStream = (narrow: Narrow) => async (
dispatch: Dispatch,
getState: GetState,
export const fetchTopicsForStream = (narrow: Narrow): ThunkAction<Promise<void>> => async (
dispatch,
getState,
) => {
const state = getState();

Expand All @@ -40,10 +43,10 @@ export const fetchTopicsForStream = (narrow: Narrow) => async (
dispatch(fetchTopics(stream.stream_id));
};

export const deleteMessagesForTopic = (streamId: number, topic: string) => async (
dispatch: Dispatch,
getState: GetState,
) => {
export const deleteMessagesForTopic = (
streamId: number,
topic: string,
): ThunkAction<Promise<void>> => async (dispatch, getState) => {
const state = getState();
const outbox = getOutbox(state);
const stream = getStreamsById(state).get(streamId);
Expand Down
Loading

0 comments on commit 6ffe4c0

Please sign in to comment.