diff --git a/native/chat/chat-router.js b/native/chat/chat-router.js index ca294083a..f81b9701b 100644 --- a/native/chat/chat-router.js +++ b/native/chat/chat-router.js @@ -1,188 +1,192 @@ // @flow import type { StackAction, Route, Router, StackRouterOptions, StackNavigationState, RouterConfigOptions, GenericNavigationAction, } from '@react-navigation/core'; import { StackRouter, CommonActions } from '@react-navigation/native'; import type { MinimallyEncodedThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import { createNavigateToThreadAction } from './message-list-types.js'; import { clearScreensActionType, replaceWithThreadActionType, clearThreadsActionType, pushNewThreadActionType, } from '../navigation/action-types.js'; import { getRemoveEditMode } from '../navigation/nav-selectors.js'; import { removeScreensFromStack, getThreadIDFromRoute, } from '../navigation/navigation-utils.js'; import { ChatThreadListRouteName, ComposeSubchannelRouteName, } from '../navigation/route-names.js'; type ClearScreensAction = { +type: 'CLEAR_SCREENS', +payload: { +routeNames: $ReadOnlyArray, }, }; type ReplaceWithThreadAction = { +type: 'REPLACE_WITH_THREAD', +payload: { +threadInfo: ThreadInfo, }, }; type ClearThreadsAction = { +type: 'CLEAR_THREADS', +payload: { +threadIDs: $ReadOnlyArray, }, }; type PushNewThreadAction = { +type: 'PUSH_NEW_THREAD', +payload: { +threadInfo: ThreadInfo, }, }; export type ChatRouterNavigationAction = | StackAction | ClearScreensAction | ReplaceWithThreadAction | ClearThreadsAction | PushNewThreadAction; export type ChatRouterNavigationHelpers = { +clearScreens: (routeNames: $ReadOnlyArray) => void, +replaceWithThread: ( threadInfo: ThreadInfo | MinimallyEncodedThreadInfo, ) => void, +clearThreads: (threadIDs: $ReadOnlyArray) => void, +pushNewThread: (threadInfo: ThreadInfo | MinimallyEncodedThreadInfo) => void, }; function ChatRouter( routerOptions: StackRouterOptions, ): Router { const { getStateForAction: baseGetStateForAction, actionCreators: baseActionCreators, shouldActionChangeFocus: baseShouldActionChangeFocus, ...rest } = StackRouter(routerOptions); return { ...rest, getStateForAction: ( lastState: StackNavigationState, action: ChatRouterNavigationAction, options: RouterConfigOptions, ) => { if (action.type === clearScreensActionType) { const { routeNames } = action.payload; if (!lastState) { return lastState; } return removeScreensFromStack(lastState, (route: Route<>) => routeNames.includes(route.name) ? 'remove' : 'keep', ); } else if (action.type === replaceWithThreadActionType) { const { threadInfo } = action.payload; if (!lastState) { return lastState; } const clearedState = removeScreensFromStack( lastState, (route: Route<>) => route.name === ChatThreadListRouteName ? 'keep' : 'remove', ); const navigateAction = CommonActions.navigate( createNavigateToThreadAction({ threadInfo }), ); return baseGetStateForAction(clearedState, navigateAction, options); } else if (action.type === clearThreadsActionType) { const threadIDs = new Set(action.payload.threadIDs); if (!lastState) { return lastState; } - return removeScreensFromStack(lastState, (route: Route<>) => - threadIDs.has(getThreadIDFromRoute(route)) ? 'remove' : 'keep', - ); + return removeScreensFromStack(lastState, (route: Route<>) => { + const threadID = getThreadIDFromRoute(route); + if (!threadID) { + return 'keep'; + } + return threadIDs.has(threadID) ? 'remove' : 'keep'; + }); } else if (action.type === pushNewThreadActionType) { const { threadInfo } = action.payload; if (!lastState) { return lastState; } const clearedState = removeScreensFromStack( lastState, (route: Route<>) => route.name === ComposeSubchannelRouteName ? 'remove' : 'break', ); const navigateAction = CommonActions.navigate( createNavigateToThreadAction({ threadInfo }), ); return baseGetStateForAction(clearedState, navigateAction, options); } else { const result = baseGetStateForAction(lastState, action, options); const removeEditMode = getRemoveEditMode(lastState); // We prevent navigating if the user is in edit mode. We don't block // navigating back here because it is handled by the `beforeRemove` // listener in the `ChatInputBar` component. if ( result !== null && result?.index && result.index > lastState.index && removeEditMode && removeEditMode(action) === 'ignore_action' ) { return lastState; } return result; } }, actionCreators: { ...baseActionCreators, clearScreens: (routeNames: $ReadOnlyArray) => ({ type: clearScreensActionType, payload: { routeNames, }, }), replaceWithThread: (threadInfo: ThreadInfo) => ({ type: replaceWithThreadActionType, payload: { threadInfo }, }: ReplaceWithThreadAction), clearThreads: (threadIDs: $ReadOnlyArray) => ({ type: clearThreadsActionType, payload: { threadIDs }, }), pushNewThread: (threadInfo: ThreadInfo) => ({ type: pushNewThreadActionType, payload: { threadInfo }, }: PushNewThreadAction), }, shouldActionChangeFocus: (action: GenericNavigationAction) => { if (action.type === replaceWithThreadActionType) { return true; } else if (action.type === pushNewThreadActionType) { return true; } else { return baseShouldActionChangeFocus(action); } }, }; } export default ChatRouter;