From 5784672118bb45f60c5eec5f40f88d57108dce99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Wed, 29 Jan 2025 12:04:46 +0100 Subject: [PATCH] fix: Hot reload in web-example, improve navigation (#6947) ## Summary This PR: - fixes hot reload on web, - improves navigation (uses proper screen title, better back button name (adds space between words)), - fixes web url to navigation state mapping (proper page is displayed on refresh or when navigating to a screen via url change) --- apps/common-app/src/App.tsx | 37 ++++++++++--------- .../apps/css/examples/animations/routes.ts | 2 +- .../src/apps/css/navigation/Navigator.tsx | 13 +++++-- .../src/apps/css/navigation/utils.ts | 3 +- .../src/components/navigation/BackButton.tsx | 16 ++++++-- apps/web-example/tsconfig.json | 4 +- .../src/css/platform/web/utils.ts | 2 +- 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/apps/common-app/src/App.tsx b/apps/common-app/src/App.tsx index 17a8dba4bfc..9cf2edd81cc 100644 --- a/apps/common-app/src/App.tsx +++ b/apps/common-app/src/App.tsx @@ -7,7 +7,7 @@ import { NavigationContainer, } from '@react-navigation/native'; import { useCallback, useEffect, useState } from 'react'; -import { ActivityIndicator, Linking, Platform, View } from 'react-native'; +import { ActivityIndicator, Linking, View } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; @@ -38,18 +38,23 @@ export default function App() { getPathFromState(state, options).replace(/%2F/g, '/'), getStateFromPath: (path) => { const chunks = path.split('/').filter(Boolean); - const routes = chunks.reduce>( - (result, chunk) => { - const lastRoute = result[result.length - 1]; - const route = { - name: lastRoute ? `${lastRoute.name}/${chunk}` : chunk, - }; - result.push(route); - return result; - }, - [] - ); - return { routes }; + if (chunks.length === 0) return { routes: [] }; + + const drawerRoute = chunks[0]; + const stackRoutes = chunks.slice(1).map((_, index, array) => ({ + name: array.slice(0, index + 1).join('/'), + })); + + return { + routes: [ + { + name: drawerRoute, + state: { + routes: stackRoutes, + }, + }, + ], + }; }, prefixes: [], }} @@ -124,11 +129,7 @@ function useNavigationState() { try { const initialUrl = await Linking.getInitialURL(); - if ( - Platform.OS !== 'web' && - Platform.OS !== 'macos' && - initialUrl === null - ) { + if (!IS_MACOS && !IS_WEB && initialUrl === null) { // Only restore state if there's no deep link and we're not on web const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY); // Erase the state immediately after fetching it. diff --git a/apps/common-app/src/apps/css/examples/animations/routes.ts b/apps/common-app/src/apps/css/examples/animations/routes.ts index a1057d84c50..d15150c9c65 100644 --- a/apps/common-app/src/apps/css/examples/animations/routes.ts +++ b/apps/common-app/src/apps/css/examples/animations/routes.ts @@ -159,7 +159,7 @@ const appearanceRoutes = { name: 'Color Formats', Component: animatedProperties.appearance.colors.ColorFormats, }, - ColorFunctions: { + ColorProperties: { name: 'Color Properties', Component: animatedProperties.appearance.colors.ColorProperties, }, diff --git a/apps/common-app/src/apps/css/navigation/Navigator.tsx b/apps/common-app/src/apps/css/navigation/Navigator.tsx index 3031739f076..df56aad2f29 100644 --- a/apps/common-app/src/apps/css/navigation/Navigator.tsx +++ b/apps/common-app/src/apps/css/navigation/Navigator.tsx @@ -126,6 +126,7 @@ type StackScreensOptions = { function createStackScreens( routes: Routes, + name: string, pathChunks: Array, reducedMotion: boolean, options?: StackScreensOptions @@ -150,7 +151,7 @@ function createStackScreens( options={{ ...sharedOptions, animation: reducedMotion || depth === 0 ? 'none' : 'default', - title: pathChunks[pathChunks.length - 1], + title: name, }} /> ), @@ -160,6 +161,7 @@ function createStackScreens( if (isRouteWithRoutes(value)) { return createStackScreens( value.routes, + value.name, [...pathChunks, key], reducedMotion, { @@ -177,7 +179,7 @@ function createStackScreens( options={{ ...sharedOptions, animation: 'slide_from_right', - title: key, + title: value.name, }} /> ); @@ -211,7 +213,12 @@ function Navigator() { statusBarStyle: 'dark', }}> {Object.entries(tabRoutes).flatMap(([key, value]) => - createStackScreens(value.routes, [key], shouldReduceMotion) + createStackScreens( + value.routes, + value.name, + [key], + shouldReduceMotion + ) )} diff --git a/apps/common-app/src/apps/css/navigation/utils.ts b/apps/common-app/src/apps/css/navigation/utils.ts index 010ef7f0650..3311e3afd33 100644 --- a/apps/common-app/src/apps/css/navigation/utils.ts +++ b/apps/common-app/src/apps/css/navigation/utils.ts @@ -6,5 +6,6 @@ export function isRouteWithRoutes(route: Route): route is RouteWithRoutes { export function getScreenTitle(path: string): string { const parts = path.split('/'); - return parts[parts.length - 1] ?? ''; + const lastPart = parts[parts.length - 1] ?? ''; + return lastPart.replace(/([A-Z])/g, ' $1').trim(); } diff --git a/apps/common-app/src/components/navigation/BackButton.tsx b/apps/common-app/src/components/navigation/BackButton.tsx index d394e4a9e75..33e277fb587 100644 --- a/apps/common-app/src/components/navigation/BackButton.tsx +++ b/apps/common-app/src/components/navigation/BackButton.tsx @@ -1,7 +1,7 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; -import { useNavigation } from '@react-navigation/native'; -import { memo } from 'react'; +import { useFocusEffect, useNavigation } from '@react-navigation/native'; +import { memo, useCallback, useState } from 'react'; import { StyleSheet } from 'react-native'; import { Pressable } from 'react-native-gesture-handler'; import Animated, { FadeInRight } from 'react-native-reanimated'; @@ -13,9 +13,17 @@ import { IS_WEB } from '@/utils'; function BackButton() { const navigation = useNavigation(); + const [prevRouteName, setPrevRouteName] = useState(() => { + const routes = navigation.getState()?.routes; + return routes?.[routes.length - 2]?.name; + }); - const routes = navigation.getState()?.routes; - const prevRouteName = routes?.[routes.length - 2]?.name; + useFocusEffect( + useCallback(() => { + const routes = navigation.getState()?.routes; + setPrevRouteName(routes?.[routes.length - 2]?.name); + }, [navigation]) + ); if (!prevRouteName || !navigation.canGoBack()) { return null; diff --git a/apps/web-example/tsconfig.json b/apps/web-example/tsconfig.json index a84af1214a7..3a57bfef626 100644 --- a/apps/web-example/tsconfig.json +++ b/apps/web-example/tsconfig.json @@ -4,8 +4,8 @@ "strict": true, "baseUrl": "../..", "paths": { - "@/*": ["./apps/common-app/src/*"] - // "react-native-reanimated": ["./packages/react-native-reanimated/src"] + "@/*": ["./apps/common-app/src/*"], + "react-native-reanimated": ["./packages/react-native-reanimated/src"] } }, "include": ["**/*.ts", "**/*.tsx"] diff --git a/packages/react-native-reanimated/src/css/platform/web/utils.ts b/packages/react-native-reanimated/src/css/platform/web/utils.ts index 341c46aea26..e92066ac5ca 100644 --- a/packages/react-native-reanimated/src/css/platform/web/utils.ts +++ b/packages/react-native-reanimated/src/css/platform/web/utils.ts @@ -41,7 +41,7 @@ export function kebabize(property: T) { function easingMapper(easing: CSSTimingFunction) { if (typeof easing === 'string') { - return kebabize(easing); + return easing; } if (easing instanceof StepsEasing) {