Skip to content

Commit

Permalink
Merge pull request #4256 from tloncorp/po/tlon-3304-preserve-navigati…
Browse files Browse the repository at this point in the history
…on-state-when-resizing-window

desktop: preserve navigation state when resizing window
  • Loading branch information
patosullivan authored Dec 9, 2024
2 parents a0c6f99 + e24eaea commit b36d966
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 7 deletions.
109 changes: 108 additions & 1 deletion packages/app/navigation/BasePathNavigator.tsx
Original file line number Diff line number Diff line change
@@ -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<RootStackParamList>;
Expand All @@ -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 (
<Navigator.Navigator screenOptions={{ headerShown: false }}>
<Navigator.Screen
Expand Down
6 changes: 3 additions & 3 deletions packages/app/navigation/desktop/TopLevelDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ const DrawerContent = (props: DrawerContentComponentProps) => {
/>
<AvatarNavIcon
id={userId}
focused={isRouteActive('Profile')}
focused={isRouteActive('Contacts')}
onPress={() =>
props.navigation.reset({ index: 0, routes: [{ name: 'Profile' }] })
props.navigation.reset({ index: 0, routes: [{ name: 'Contacts' }] })
}
/>
{webAppNeedsUpdate && (
Expand Down Expand Up @@ -86,7 +86,7 @@ export const TopLevelDrawer = () => {
>
<Drawer.Screen name="Home" component={HomeNavigator} />
<Drawer.Screen name="Activity" component={ActivityScreen} />
<Drawer.Screen name="Profile" component={ProfileScreenNavigator} />
<Drawer.Screen name="Contacts" component={ProfileScreenNavigator} />
</Drawer.Navigator>
);
};
2 changes: 1 addition & 1 deletion packages/app/navigation/linking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const getDesktopLinkingConfig = (
initialRouteName: 'Home',
screens: {
Activity: 'activity',
Profile: 'profile',
Contacts: 'contacts',
Home: {
screens: {
ChatList: '',
Expand Down
2 changes: 1 addition & 1 deletion packages/app/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export type RootStackNavigationProp = NavigationProp<RootStackParamList>;

export type RootDrawerParamList = {
Home: NavigatorScreenParams<HomeDrawerParamList>;
} & Pick<RootStackParamList, 'Activity' | 'Profile'>;
} & Pick<RootStackParamList, 'Activity' | 'Contacts'>;

export type HomeDrawerParamList = Pick<
RootStackParamList,
Expand Down
2 changes: 1 addition & 1 deletion packages/app/navigation/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export async function getMainGroupRoute(
export function screenNameFromChannelId(channelId: string) {
return logic.isDmChannelId(channelId)
? 'DM'
: logic.isGroupChannelId(channelId)
: logic.isGroupDmChannelId(channelId)
? 'GroupDM'
: 'Channel';
}
20 changes: 20 additions & 0 deletions packages/app/utils/lastScreen.ts
Original file line number Diff line number Diff line change
@@ -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;
}
};

0 comments on commit b36d966

Please sign in to comment.