diff --git a/src/account-info/ProfileCard.js b/src/account-info/ProfileCard.js index b8b9e8a1728..10fc70adb37 100644 --- a/src/account-info/ProfileCard.js +++ b/src/account-info/ProfileCard.js @@ -3,6 +3,7 @@ import React, { PureComponent } from 'react'; import { ScrollView, View } from 'react-native'; import type { NavigationTabProp, NavigationStateRoute } from 'react-navigation-tabs'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, User } from '../types'; import { createStyleSheet } from '../styles'; import { connect } from '../react-redux'; @@ -28,10 +29,9 @@ const styles = createStyleSheet({ }, }); -class SetStatusButton extends PureComponent<{| +dispatch: Dispatch |}> { +class SetStatusButton extends PureComponent<{||}> { onPress = () => { - const { dispatch } = this.props; - dispatch(navigateToUserStatus()); + NavigationService.dispatch(navigateToUserStatus()); }; render() { @@ -41,9 +41,9 @@ class SetStatusButton extends PureComponent<{| +dispatch: Dispatch |}> { } } -class SwitchAccountButton extends PureComponent<{| +dispatch: Dispatch |}> { +class SwitchAccountButton extends PureComponent<{||}> { onPress = () => { - this.props.dispatch(navigateToAccountPicker()); + NavigationService.dispatch(navigateToAccountPicker()); }; render() { @@ -91,10 +91,10 @@ class ProfileCard extends PureComponent { - + - + diff --git a/src/account/AccountPickScreen.js b/src/account/AccountPickScreen.js index 1f2a4525f85..21f143ab894 100644 --- a/src/account/AccountPickScreen.js +++ b/src/account/AccountPickScreen.js @@ -3,6 +3,7 @@ import React, { PureComponent } from 'react'; import type { NavigationStackProp, NavigationStateRoute } from 'react-navigation-stack'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch } from '../types'; import { connect } from '../react-redux'; import { hasAuth, getAccountStatuses } from '../selectors'; @@ -32,7 +33,7 @@ class AccountPickScreen extends PureComponent { dispatch(accountSwitch(index)); }); } else { - dispatch(navigateToRealmScreen({ realm })); + NavigationService.dispatch(navigateToRealmScreen({ realm })); } }; @@ -53,7 +54,7 @@ class AccountPickScreen extends PureComponent { canGoBack = this.props.hasAuth; render() { - const { accounts, dispatch } = this.props; + const { accounts } = this.props; return ( { { - dispatch(navigateToRealmScreen()); + NavigationService.dispatch(navigateToRealmScreen()); }} /> diff --git a/src/account/accountActions.js b/src/account/accountActions.js index 52fbcf057d6..e8381301d5d 100644 --- a/src/account/accountActions.js +++ b/src/account/accountActions.js @@ -1,4 +1,5 @@ /* @flow strict-local */ +import NavigationService from '../nav/NavigationService'; import type { Action, Dispatch, GetState } from '../types'; import { ACCOUNT_SWITCH, @@ -16,7 +17,7 @@ const accountSwitchPlain = (index: number): Action => ({ }); export const accountSwitch = (index: number) => (dispatch: Dispatch, getState: GetState) => { - dispatch(resetToLoading()); + NavigationService.dispatch(resetToLoading()); dispatch(accountSwitchPlain(index)); }; @@ -47,7 +48,7 @@ export const loginSuccess = (realm: URL, email: string, apiKey: string) => ( dispatch: Dispatch, getState: GetState, ) => { - dispatch(resetToLoading()); + NavigationService.dispatch(resetToLoading()); dispatch(loginSuccessPlain(realm, email, apiKey)); }; @@ -56,6 +57,6 @@ const logoutPlain = (): Action => ({ }); export const logout = () => async (dispatch: Dispatch, getState: GetState) => { - dispatch(resetToAccountPicker()); + NavigationService.dispatch(resetToAccountPicker()); dispatch(logoutPlain()); }; diff --git a/src/boot/AppEventHandlers.js b/src/boot/AppEventHandlers.js index dd5c5d05806..8f5e89f14ad 100644 --- a/src/boot/AppEventHandlers.js +++ b/src/boot/AppEventHandlers.js @@ -115,7 +115,7 @@ class AppEventHandlers extends PureComponent { }; notificationListener = new NotificationListener(this.props.dispatch); - shareListener = new ShareReceivedListener(this.props.dispatch); + shareListener = new ShareReceivedListener(); handleMemoryWarning = () => { // Release memory here @@ -124,7 +124,7 @@ class AppEventHandlers extends PureComponent { componentDidMount() { const { dispatch } = this.props; handleInitialNotification(dispatch); - handleInitialShare(dispatch); + handleInitialShare(); this.netInfoDisconnectCallback = NetInfo.addEventListener(this.handleConnectivityChange); AppState.addEventListener('change', this.handleAppStateChange); diff --git a/src/compose/ComposeMenu.js b/src/compose/ComposeMenu.js index 0ab3d6d3b1b..dcc9109100c 100644 --- a/src/compose/ComposeMenu.js +++ b/src/compose/ComposeMenu.js @@ -4,6 +4,7 @@ import { Platform, View } from 'react-native'; import type { DocumentPickerResponse } from 'react-native-document-picker'; import ImagePicker from 'react-native-image-picker'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, Narrow } from '../types'; import { connect } from '../react-redux'; import { showErrorAlert } from '../utils/info'; @@ -154,7 +155,7 @@ class ComposeMenu extends PureComponent { }); render() { - const { dispatch, expanded, insertVideoCallLink, onExpandContract } = this.props; + const { expanded, insertVideoCallLink, onExpandContract } = this.props; const numIcons = 3 + (Platform.OS === 'android' ? 1 : 0) + (insertVideoCallLink !== null ? 1 : 0); @@ -171,7 +172,7 @@ class ComposeMenu extends PureComponent { style={this.styles.composeMenuButton} size={24} onPress={() => { - dispatch(navigateToCreateGroup()); + NavigationService.dispatch(navigateToCreateGroup()); }} /> {Platform.OS === 'android' && ( diff --git a/src/diagnostics/DiagnosticsScreen.js b/src/diagnostics/DiagnosticsScreen.js index f3f1c121efb..04566c9cf66 100644 --- a/src/diagnostics/DiagnosticsScreen.js +++ b/src/diagnostics/DiagnosticsScreen.js @@ -4,9 +4,8 @@ import React, { PureComponent } from 'react'; import type { NavigationStackProp, NavigationStateRoute } from 'react-navigation-stack'; import { nativeApplicationVersion } from 'expo-application'; -import type { Dispatch } from '../types'; +import NavigationService from '../nav/NavigationService'; import { createStyleSheet } from '../styles'; -import { connect } from '../react-redux'; import { OptionButton, OptionDivider, Screen, RawLabel } from '../common'; import { navigateToDebug, @@ -28,14 +27,10 @@ type Props = $ReadOnly<{| // don't invoke it anywhere else at all), we know it gets the // `navigation` prop for free, with the stack-nav shape. navigation: NavigationStackProp, - - dispatch: Dispatch, |}>; -class DiagnosticsScreen extends PureComponent { +export default class DiagnosticsScreen extends PureComponent { render() { - const { dispatch } = this.props; - return ( @@ -43,30 +38,28 @@ class DiagnosticsScreen extends PureComponent { { - dispatch(navigateToVariables()); + NavigationService.dispatch(navigateToVariables()); }} /> { - dispatch(navigateToTiming()); + NavigationService.dispatch(navigateToTiming()); }} /> { - dispatch(navigateToStorage()); + NavigationService.dispatch(navigateToStorage()); }} /> { - dispatch(navigateToDebug()); + NavigationService.dispatch(navigateToDebug()); }} /> ); } } - -export default connect<{||}, _, _>()(DiagnosticsScreen); diff --git a/src/main/HomeTab.js b/src/main/HomeTab.js index 048eb4305fd..33609fe0592 100644 --- a/src/main/HomeTab.js +++ b/src/main/HomeTab.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import { View } from 'react-native'; import type { NavigationTabProp, NavigationStateRoute } from 'react-navigation-tabs'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch } from '../types'; import { connect } from '../react-redux'; import { HOME_NARROW, MENTIONED_NARROW, STARRED_NARROW } from '../utils/narrow'; @@ -65,7 +66,7 @@ class HomeTab extends PureComponent { { - dispatch(navigateToSearch()); + NavigationService.dispatch(navigateToSearch()); }} /> diff --git a/src/message/__tests__/messageActions-test.js b/src/message/__tests__/messageActions-test.js index 406109aa2e2..511225187e6 100644 --- a/src/message/__tests__/messageActions-test.js +++ b/src/message/__tests__/messageActions-test.js @@ -2,6 +2,8 @@ import configureStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import { navigateToChat } from '../../nav/navActions'; +import NavigationService from '../../nav/NavigationService'; import { doNarrow } from '../messagesActions'; import { streamNarrow } from '../../utils/narrow'; import * as eg from '../../__tests__/lib/exampleData'; @@ -15,6 +17,8 @@ const streamNarrowObj = streamNarrow('some stream'); describe('messageActions', () => { describe('doNarrow', () => { test('action to push to nav dispatched', () => { + NavigationService.dispatch = jest.fn(); + const store = mockStore( eg.reduxState({ accounts: [eg.selfAccount], @@ -24,11 +28,8 @@ describe('messageActions', () => { ); store.dispatch(doNarrow(streamNarrowObj)); - const actions = store.getActions(); - expect(actions).toHaveLength(1); - const [action0] = actions; - expect(action0.type).toBe('Navigation/PUSH'); + expect(NavigationService.dispatch).toHaveBeenCalledWith(navigateToChat(streamNarrowObj)); }); }); }); diff --git a/src/message/fetchActions.js b/src/message/fetchActions.js index 90b17f919ae..7df1c4e0da6 100644 --- a/src/message/fetchActions.js +++ b/src/message/fetchActions.js @@ -1,4 +1,5 @@ /* @flow strict-local */ +import NavigationService from '../nav/NavigationService'; import type { Narrow, Dispatch, @@ -173,7 +174,7 @@ export const initialFetchComplete = () => async (dispatch: Dispatch, getState: G // conditional accordingly, if we found out we're not depending on // the more general condition; see // https://github.com/zulip/zulip-mobile/pull/4274#discussion_r505941875 - dispatch(resetToMainTabs()); + NavigationService.dispatch(resetToMainTabs()); } dispatch(initialFetchCompletePlain()); }; diff --git a/src/message/messageActionSheet.js b/src/message/messageActionSheet.js index 06cb91b1c8d..6895042b056 100644 --- a/src/message/messageActionSheet.js +++ b/src/message/messageActionSheet.js @@ -1,5 +1,7 @@ /* @flow strict-local */ import { Clipboard, Share, Alert } from 'react-native'; + +import NavigationService from '../nav/NavigationService'; import type { Auth, Dispatch, @@ -185,13 +187,13 @@ shareMessage.title = 'Share'; shareMessage.errorMessage = 'Failed to share message'; const addReaction = ({ message, dispatch }) => { - dispatch(navigateToEmojiPicker(message.id)); + NavigationService.dispatch(navigateToEmojiPicker(message.id)); }; addReaction.title = 'Add a reaction'; addReaction.errorMessage = 'Failed to add reaction'; const showReactions = ({ message, dispatch }) => { - dispatch(navigateToMessageReactionScreen(message.id)); + NavigationService.dispatch(navigateToMessageReactionScreen(message.id)); }; showReactions.title = 'See who reacted'; showReactions.errorMessage = 'Failed to show reactions'; diff --git a/src/message/messagesActions.js b/src/message/messagesActions.js index 1febbe3f225..151dddeb0bd 100644 --- a/src/message/messagesActions.js +++ b/src/message/messagesActions.js @@ -1,4 +1,5 @@ /* @flow strict-local */ +import NavigationService from '../nav/NavigationService'; import type { Narrow, Dispatch, GetState } from '../types'; import { getAuth, getUsersById } from '../selectors'; import { getMessageIdFromLink, getNarrowFromLink } from '../utils/internalLinks'; @@ -17,7 +18,7 @@ export const doNarrow = (narrow: Narrow, anchor: number = FIRST_UNREAD_ANCHOR) = getState: GetState, ) => { // TODO: Use `anchor` to open the message list to a particular message. - dispatch(navigateToChat(narrow)); + NavigationService.dispatch(navigateToChat(narrow)); }; export const messageLinkPress = (href: string) => async ( diff --git a/src/nav/InitialNavigationDispatcher.js b/src/nav/InitialNavigationDispatcher.js index 9f2320f5c02..64488d0dc95 100644 --- a/src/nav/InitialNavigationDispatcher.js +++ b/src/nav/InitialNavigationDispatcher.js @@ -2,6 +2,7 @@ import type { Node as React$Node } from 'react'; import { PureComponent } from 'react'; +import NavigationService from './NavigationService'; import type { Dispatch, Account, User } from '../types'; import { resetToAccountPicker, resetToRealmScreen, resetToMainTabs } from '../actions'; import { connect } from '../react-redux'; @@ -24,12 +25,24 @@ type Props = $ReadOnly<{| class InitialNavigationDispatcher extends PureComponent { componentDidMount() { if (this.props.isHydrated) { + // `NavigationService` will be ready by the time this is run: a + // `ref` is set in the `ref`fed component's + // `componentDidMount` [1], and a parent's `componentDidMount` + // is run after a child's `componentDidMount` [2]. + // + // [1] https://reactjs.org/docs/refs-and-the-dom.html#adding-a-ref-to-a-dom-element + // [2] https://reactnavigation.org/docs/navigating-without-navigation-prop/#handling-initialization this.doInitialNavigation(); } } componentDidUpdate(prevProps) { if (!prevProps.isHydrated && this.props.isHydrated) { + // `NavigationService` will be ready here (as long as the + // `ref`fed component hasn't unmounted for some reason). + // `componentDidUpdate` won't run before `componentDidMount`, + // and we've established that it's ready by `componentDidMount` + // (see note there). this.doInitialNavigation(); } } @@ -37,20 +50,21 @@ class InitialNavigationDispatcher extends PureComponent { /** * Data has been loaded, so open the app to the right screen. * - * Not to be called before the REHYDRATE action, and not to be - * called more than once. + * Not to be called before the REHYDRATE action or before + * `NavigationService` is ready, and not to be called more than + * once. */ doInitialNavigation = () => { - const { hasAuth, accounts, users, dispatch } = this.props; + const { hasAuth, accounts, users } = this.props; // If there are accounts but the active account is not logged in, // show account screen. if (!hasAuth) { if (accounts.length > 1) { - dispatch(resetToAccountPicker()); + NavigationService.dispatch(resetToAccountPicker()); return; } else { - dispatch(resetToRealmScreen({ initial: true })); + NavigationService.dispatch(resetToRealmScreen({ initial: true })); return; } } @@ -67,7 +81,7 @@ class InitialNavigationDispatcher extends PureComponent { // Great: we have an active, logged-in account, and server data for it. // Show the main UI. - dispatch(resetToMainTabs()); + NavigationService.dispatch(resetToMainTabs()); }; render() { diff --git a/src/notification/notificationActions.js b/src/notification/notificationActions.js index 5cfc754263c..5884dae28bc 100644 --- a/src/notification/notificationActions.js +++ b/src/notification/notificationActions.js @@ -1,5 +1,6 @@ /* @flow strict-local */ import { Platform } from 'react-native'; + import type { Account, Dispatch, GetState, Identity, Action } from '../types'; import * as api from '../api'; import { diff --git a/src/pm-conversations/PmConversationsCard.js b/src/pm-conversations/PmConversationsCard.js index ee09d0740a7..7ad72cd7ced 100644 --- a/src/pm-conversations/PmConversationsCard.js +++ b/src/pm-conversations/PmConversationsCard.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import { View } from 'react-native'; import type { NavigationTabProp, NavigationStateRoute } from 'react-navigation-tabs'; +import NavigationService from '../nav/NavigationService'; import type { ThemeData } from '../styles'; import { ThemeContext, createStyleSheet } from '../styles'; import type { Dispatch, PmConversationData, UserOrBot } from '../types'; @@ -64,7 +65,7 @@ class PmConversationsCard extends PureComponent { style={styles.button} text="Create group" onPress={() => { - setTimeout(() => dispatch(navigateToCreateGroup())); + setTimeout(() => NavigationService.dispatch(navigateToCreateGroup())); }} /> { style={styles.button} text="Search" onPress={() => { - setTimeout(() => dispatch(navigateToUsersScreen())); + setTimeout(() => NavigationService.dispatch(navigateToUsersScreen())); }} /> diff --git a/src/settings/SettingsCard.js b/src/settings/SettingsCard.js index 16f29e5defb..737cedf055a 100644 --- a/src/settings/SettingsCard.js +++ b/src/settings/SettingsCard.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import { ScrollView } from 'react-native'; import type { NavigationTabProp, NavigationStateRoute } from 'react-navigation-tabs'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch } from '../types'; import { createStyleSheet } from '../styles'; import { connect } from '../react-redux'; @@ -49,7 +50,7 @@ class SettingsCard extends PureComponent { }; render() { - const { theme, dispatch } = this.props; + const { theme } = this.props; return ( @@ -64,28 +65,28 @@ class SettingsCard extends PureComponent { Icon={IconNotifications} label="Notifications" onPress={() => { - dispatch(navigateToNotifications()); + NavigationService.dispatch(navigateToNotifications()); }} /> { - dispatch(navigateToLanguage()); + NavigationService.dispatch(navigateToLanguage()); }} /> { - dispatch(navigateToDiagnostics()); + NavigationService.dispatch(navigateToDiagnostics()); }} /> { - dispatch(navigateToLegal()); + NavigationService.dispatch(navigateToLegal()); }} /> diff --git a/src/sharing/SharingScreen.js b/src/sharing/SharingScreen.js index 25b4d9888cf..196e0b59fea 100644 --- a/src/sharing/SharingScreen.js +++ b/src/sharing/SharingScreen.js @@ -5,6 +5,7 @@ import type { NavigationStackProp, NavigationStateRoute } from 'react-navigation import { createMaterialTopTabNavigator } from 'react-navigation-tabs'; import { FormattedMessage } from 'react-intl'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, SharedData, Auth, TabNavigationOptionsPropsType } from '../types'; import { createStyleSheet } from '../styles'; import { materialTopTabNavigatorConfig } from '../styles/tabs'; @@ -74,12 +75,12 @@ class SharingScreen extends PureComponent { static router = SharingTopTabNavigator.router; render() { - const { auth, dispatch, navigation } = this.props; + const { auth, navigation } = this.props; // If there is no active logged-in account, abandon the sharing attempt, // and present the account picker screen to the user. if (auth === undefined) { - dispatch(navigateToAccountPicker()); + NavigationService.dispatch(navigateToAccountPicker()); return null; } diff --git a/src/sharing/index.js b/src/sharing/index.js index 309307e7ef8..75e9f780fb4 100644 --- a/src/sharing/index.js +++ b/src/sharing/index.js @@ -1,6 +1,8 @@ /* @flow strict-local */ import { NativeModules, DeviceEventEmitter, Platform } from 'react-native'; -import type { Dispatch, SharedData, GetState } from '../types'; + +import NavigationService from '../nav/NavigationService'; +import type { SharedData } from '../types'; import { navigateToSharing } from '../actions'; const Sharing = NativeModules.Sharing ?? { @@ -9,25 +11,16 @@ const Sharing = NativeModules.Sharing ?? { null, }; -const goToSharing = (data: SharedData) => (dispatch: Dispatch, getState: GetState) => { - dispatch(navigateToSharing(data)); -}; - -export const handleInitialShare = async (dispatch: Dispatch) => { +export const handleInitialShare = async () => { const initialSharedData: SharedData | null = await Sharing.getInitialSharedContent(); if (initialSharedData !== null) { - dispatch(goToSharing(initialSharedData)); + NavigationService.dispatch(navigateToSharing(initialSharedData)); } }; export class ShareReceivedListener { - dispatch: Dispatch; unsubs: Array<() => void> = []; - constructor(dispatch: Dispatch) { - this.dispatch = dispatch; - } - /** Private. */ listen(name: string, handler: (...empty) => void | Promise) { if (Platform.OS === 'android') { @@ -44,7 +37,7 @@ export class ShareReceivedListener { } handleShareReceived = (data: SharedData) => { - this.dispatch(goToSharing(data)); + NavigationService.dispatch(navigateToSharing(data)); }; /** Start listening. Don't call twice without intervening `stop`. */ diff --git a/src/start/AuthScreen.js b/src/start/AuthScreen.js index b8267ef8721..21badef0958 100644 --- a/src/start/AuthScreen.js +++ b/src/start/AuthScreen.js @@ -6,6 +6,7 @@ import type { NavigationStackProp, NavigationStateRoute } from 'react-navigation import type { AppleAuthenticationCredential } from 'expo-apple-authentication'; import * as AppleAuthentication from 'expo-apple-authentication'; +import NavigationService from '../nav/NavigationService'; import config from '../config'; import type { AuthenticationMethods, @@ -239,12 +240,12 @@ class AuthScreen extends PureComponent { }; handleDevAuth = () => { - this.props.dispatch(navigateToDev()); + NavigationService.dispatch(navigateToDev()); }; handlePassword = () => { const { serverSettings } = this.props.navigation.state.params; - this.props.dispatch(navigateToPassword(serverSettings.require_email_format_usernames)); + NavigationService.dispatch(navigateToPassword(serverSettings.require_email_format_usernames)); }; handleNativeAppleAuth = async () => { diff --git a/src/start/RealmScreen.js b/src/start/RealmScreen.js index 254f0eafe3e..703bd996dd9 100644 --- a/src/start/RealmScreen.js +++ b/src/start/RealmScreen.js @@ -3,6 +3,7 @@ import React, { PureComponent } from 'react'; import { ScrollView, Keyboard } from 'react-native'; import type { NavigationStackProp, NavigationStateRoute } from 'react-navigation-stack'; +import NavigationService from '../nav/NavigationService'; import { ZulipVersion } from '../utils/zulipVersion'; import type { ApiResponseServerSettings, Dispatch } from '../types'; import { connect } from '../react-redux'; @@ -74,7 +75,7 @@ class RealmScreen extends PureComponent { new ZulipVersion(serverSettings.zulip_version), ), ); - dispatch(navigateToAuth(serverSettings)); + NavigationService.dispatch(navigateToAuth(serverSettings)); Keyboard.dismiss(); } catch (err) { this.setState({ error: 'Cannot connect to server' }); diff --git a/src/streams/StreamScreen.js b/src/streams/StreamScreen.js index eb8cad8c6dd..8a3e4ba4bbb 100644 --- a/src/streams/StreamScreen.js +++ b/src/streams/StreamScreen.js @@ -3,6 +3,7 @@ import React, { PureComponent } from 'react'; import { View } from 'react-native'; import type { NavigationStackProp, NavigationStateRoute } from 'react-navigation-stack'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, Stream, Subscription } from '../types'; import { connect } from '../react-redux'; import { delay } from '../utils/async'; @@ -55,13 +56,13 @@ class StreamScreen extends PureComponent { }; handleEdit = () => { - const { dispatch, stream } = this.props; - dispatch(navigateToEditStream(stream.stream_id)); + const { stream } = this.props; + NavigationService.dispatch(navigateToEditStream(stream.stream_id)); }; handleEditSubscribers = () => { - const { dispatch, stream } = this.props; - dispatch(navigateToStreamSubscribers(stream.stream_id)); + const { stream } = this.props; + NavigationService.dispatch(navigateToStreamSubscribers(stream.stream_id)); }; toggleStreamPushNotification = () => { diff --git a/src/subscriptions/StreamListCard.js b/src/subscriptions/StreamListCard.js index dafabab287d..f55106cdd42 100644 --- a/src/subscriptions/StreamListCard.js +++ b/src/subscriptions/StreamListCard.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import { View } from 'react-native'; import type { NavigationTabProp, NavigationStateRoute } from 'react-navigation-tabs'; +import NavigationService from '../nav/NavigationService'; import type { Auth, Dispatch, Stream, Subscription } from '../types'; import { createStyleSheet } from '../styles'; import { connect } from '../react-redux'; @@ -55,7 +56,7 @@ class StreamListCard extends PureComponent { }; render() { - const { dispatch, canCreateStreams, streams, subscriptions } = this.props; + const { canCreateStreams, streams, subscriptions } = this.props; const subsAndStreams = streams.map(x => ({ ...x, subscribed: subscriptions.some(s => s.stream_id === x.stream_id), @@ -71,7 +72,7 @@ class StreamListCard extends PureComponent { text="Create new stream" onPress={() => delay(() => { - dispatch(navigateToCreateStream()); + NavigationService.dispatch(navigateToCreateStream()); }) } /> diff --git a/src/title-buttons/ExtraNavButtonStream.js b/src/title-buttons/ExtraNavButtonStream.js index 3900dc00219..94ee3da0be9 100644 --- a/src/title-buttons/ExtraNavButtonStream.js +++ b/src/title-buttons/ExtraNavButtonStream.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, Narrow, Stream } from '../types'; import { connect } from '../react-redux'; import { getStreams } from '../selectors'; @@ -17,10 +18,10 @@ type Props = $ReadOnly<{| class ExtraNavButtonStream extends PureComponent { handlePress = () => { - const { dispatch, narrow, streams } = this.props; + const { narrow, streams } = this.props; const stream = streams.find(x => x.name === narrow[0].operand); if (stream) { - dispatch(navigateToTopicList(stream.stream_id)); + NavigationService.dispatch(navigateToTopicList(stream.stream_id)); } }; diff --git a/src/title-buttons/InfoNavButtonGroup.js b/src/title-buttons/InfoNavButtonGroup.js index 67ac6ef5a1a..31f802be2e4 100644 --- a/src/title-buttons/InfoNavButtonGroup.js +++ b/src/title-buttons/InfoNavButtonGroup.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, Narrow, UserOrBot } from '../types'; import { connect } from '../react-redux'; import { getRecipientsInGroupNarrow } from '../selectors'; @@ -22,8 +23,8 @@ type Props = $ReadOnly<{| class InfoNavButtonGroup extends PureComponent { handlePress = () => { - const { dispatch, recipients } = this.props; - dispatch(navigateToGroupDetails(recipients)); + const { recipients } = this.props; + NavigationService.dispatch(navigateToGroupDetails(recipients)); }; render() { diff --git a/src/title-buttons/InfoNavButtonStream.js b/src/title-buttons/InfoNavButtonStream.js index ba4611920db..7f5db4d6baa 100644 --- a/src/title-buttons/InfoNavButtonStream.js +++ b/src/title-buttons/InfoNavButtonStream.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; +import NavigationService from '../nav/NavigationService'; import type { Dispatch, Narrow, Stream } from '../types'; import { connect } from '../react-redux'; import { getStreams } from '../selectors'; @@ -17,10 +18,10 @@ type Props = $ReadOnly<{| class InfoNavButtonStream extends PureComponent { handlePress = () => { - const { dispatch, narrow, streams } = this.props; + const { narrow, streams } = this.props; const stream = streams.find(x => x.name === narrow[0].operand); if (stream) { - dispatch(navigateToStream(stream.stream_id)); + NavigationService.dispatch(navigateToStream(stream.stream_id)); } }; diff --git a/src/webview/webViewEventHandlers.js b/src/webview/webViewEventHandlers.js index 77f2df3a928..eb286bcba6f 100644 --- a/src/webview/webViewEventHandlers.js +++ b/src/webview/webViewEventHandlers.js @@ -181,7 +181,7 @@ const markRead = (props: Props, event: MessageListEventScroll) => { const handleImage = (props: Props, src: string, messageId: number) => { const message = props.messages.find(x => x.id === messageId); if (message && !message.isOutbox) { - props.dispatch(navigateToLightbox(src, message)); + NavigationService.dispatch(navigateToLightbox(src, message)); } }; @@ -263,8 +263,7 @@ export const handleMessageListEvent = (props: Props, _: GetText, event: MessageL case 'reactionDetails': { const { messageId, reactionName } = event; - const { dispatch } = props; - dispatch(navigateToMessageReactionScreen(messageId, reactionName)); + NavigationService.dispatch(navigateToMessageReactionScreen(messageId, reactionName)); } break;