From a9bf03e71c8be011da1432bb9bec87404cf89688 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 5 Dec 2024 14:02:17 -0600 Subject: [PATCH 1/2] desktop: preserve navigation state when resizing window --- packages/app/navigation/BasePathNavigator.tsx | 109 +++++++++++++++++- .../app/navigation/desktop/TopLevelDrawer.tsx | 6 +- packages/app/navigation/types.ts | 2 +- packages/app/navigation/utils.ts | 2 +- packages/app/utils/lastScreen.ts | 20 ++++ 5 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 packages/app/utils/lastScreen.ts diff --git a/packages/app/navigation/BasePathNavigator.tsx b/packages/app/navigation/BasePathNavigator.tsx index 2ebf352ff9..6722067a38 100644 --- a/packages/app/navigation/BasePathNavigator.tsx +++ b/packages/app/navigation/BasePathNavigator.tsx @@ -1,9 +1,15 @@ -import { NavigatorScreenParams } from '@react-navigation/native'; +import { + NavigatorScreenParams, + useNavigationState, +} from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import { useEffect, useMemo, useRef } from 'react'; +import { getLastScreen, setLastScreen } from '../utils/lastScreen'; import { RootStack } from './RootStack'; import { TopLevelDrawer } from './desktop/TopLevelDrawer'; import { RootDrawerParamList, RootStackParamList } from './types'; +import { useResetToChannel, useTypedReset } from './utils'; export type MobileBasePathStackParamList = { Root: NavigatorScreenParams; @@ -27,6 +33,107 @@ export function BasePathNavigator({ isMobile }: { isMobile: boolean }) { ? MobileBasePathStackNavigator : DesktopBasePathStackNavigator; + const navState = useNavigationState((state) => state); + const currentRoute = navState?.routes[navState.index]; + const rootState = currentRoute?.state; + const lastWasMobile = useRef(isMobile); + + const currentScreenAndParams = useMemo(() => { + if (isMobile !== lastWasMobile.current) { + return undefined; + } + + const isHome = + rootState?.index === 0 && rootState?.routes[0].name === 'Home'; + const isContacts = + rootState?.index === 2 && rootState?.routes[2].name === 'Contacts'; + if (isHome) { + const homeState = rootState.routes[0].state; + if (!homeState || homeState.index === undefined) { + return { + name: 'Home', + params: {}, + }; + } + // capture the current screen and params within the home tab + return { + name: homeState.routes[homeState.index].name, + params: homeState.routes[homeState.index].params, + }; + } + + if (isContacts) { + // contacts tab is the third tab + const contactsState = rootState.routes[2].state; + if (!contactsState || contactsState.index === undefined) { + return { + name: 'Contacts', + params: {}, + }; + } + return { + name: contactsState.routes[contactsState.index].name, + params: contactsState.routes[contactsState.index].params, + }; + } + + if (rootState && rootState.routes && rootState.index !== undefined) { + const screen = rootState.routes[rootState.index]; + return { + name: screen.name, + params: screen.params, + }; + } + + return undefined; + }, [isMobile, rootState]); + + const resetToChannel = useResetToChannel(); + const reset = useTypedReset(); + + useEffect(() => { + const lastScreen = async () => getLastScreen(); + + // if we're switching between mobile and desktop, we want to reset the + // navigator to the last screen that was open in the other mode + if (lastWasMobile.current !== isMobile) { + setTimeout(() => { + lastScreen().then((lastScreen) => { + if (!lastScreen) { + return; + } + + if ( + lastScreen.name === 'Channel' || + lastScreen.name === 'GroupDM' || + lastScreen.name === 'DM' + ) { + resetToChannel(lastScreen.params.channelId, { + groupId: lastScreen.params.groupId, + }); + } else if (isMobile && lastScreen.name === 'Home') { + reset([{ name: 'ChatList' }]); + } + if (isMobile && lastScreen.name === 'Profile') { + // if we're on mobile and the last screen was profile, we want to + // be able to go back to the contacts screen when we press back + reset([{ name: 'Contacts' }, { name: 'Profile' }]); + } else { + reset([lastScreen]); + } + + lastWasMobile.current = isMobile; + }); + }, 1); // tiny delay to let navigator mount. otherwise it doesn't work. + } + }, [isMobile, resetToChannel, rootState, reset]); + + useEffect(() => { + if (currentScreenAndParams && isMobile === lastWasMobile.current) { + setLastScreen(currentScreenAndParams); + } + }, [currentScreenAndParams, isMobile]); + return ( { /> props.navigation.navigate('Profile')} + focused={isRouteActive('Contacts')} + onPress={() => props.navigation.navigate('Contacts')} /> {webAppNeedsUpdate && ( { > - + ); }; diff --git a/packages/app/navigation/types.ts b/packages/app/navigation/types.ts index 73bbc1aa16..f71d7c706a 100644 --- a/packages/app/navigation/types.ts +++ b/packages/app/navigation/types.ts @@ -73,7 +73,7 @@ export type RootStackNavigationProp = NavigationProp; export type RootDrawerParamList = { Home: NavigatorScreenParams; -} & Pick; +} & Pick; export type HomeDrawerParamList = Pick< RootStackParamList, diff --git a/packages/app/navigation/utils.ts b/packages/app/navigation/utils.ts index e6f0c493e0..ec7567ecd9 100644 --- a/packages/app/navigation/utils.ts +++ b/packages/app/navigation/utils.ts @@ -126,7 +126,7 @@ export async function getMainGroupRoute(groupId: string) { export function screenNameFromChannelId(channelId: string) { return logic.isDmChannelId(channelId) ? 'DM' - : logic.isGroupChannelId(channelId) + : logic.isGroupDmChannelId(channelId) ? 'GroupDM' : 'Channel'; } diff --git a/packages/app/utils/lastScreen.ts b/packages/app/utils/lastScreen.ts new file mode 100644 index 0000000000..770b215524 --- /dev/null +++ b/packages/app/utils/lastScreen.ts @@ -0,0 +1,20 @@ +import storage from '../lib/storage'; + +export const setLastScreen = async (screen: { name: string; params: any }) => { + await storage.save({ + key: 'lastScreen', + data: screen, + }); +}; + +export const getLastScreen = async () => { + try { + const result = await storage.load({ key: 'lastScreen' }); + return result; + } catch (err) { + if (err instanceof Error && err.name !== 'NotFoundError') { + console.error(err); + } + return null; + } +}; From eac8164bb0bda74f0f4fbba8a2d3a8bffd24270c Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Fri, 6 Dec 2024 06:04:23 -0600 Subject: [PATCH 2/2] fix type issue in linking module --- packages/app/navigation/linking.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/navigation/linking.ts b/packages/app/navigation/linking.ts index be68849eda..9b09d4e154 100644 --- a/packages/app/navigation/linking.ts +++ b/packages/app/navigation/linking.ts @@ -87,7 +87,7 @@ export const getDesktopLinkingConfig = ( initialRouteName: 'Home', screens: { Activity: 'activity', - Profile: 'profile', + Contacts: 'contacts', Home: { screens: { ChatList: '',