diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..3a6c8c6 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,37 @@ +#!/bin/sh + +if [ "$LEFTHOOK" = "0" ]; then + exit 0 +fi + +if [ -t 1 ] ; then + exec < /dev/tty ; # <- enables interactive shell +fi + +dir="$(git rev-parse --show-toplevel)" + +call_lefthook() +{ + if lefthook -h >/dev/null 2>&1 + then + eval lefthook $@ + elif test -f "$dir/node_modules/@arkweid/lefthook/bin/lefthook" + then + eval "$dir/node_modules/@arkweid/lefthook/bin/lefthook $@" + elif bundle exec lefthook -h >/dev/null 2>&1 + then + bundle exec lefthook $@ + elif npx @arkweid/lefthook -h >/dev/null 2>&1 + then + npx @arkweid/lefthook $@ + elif yarn lefthook -h >/dev/null 2>&1 + then + yarn lefthook $@ + else + echo "Can't find lefthook in PATH" + fi +} + + + +call_lefthook "run commit-msg $@" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..9ba5f64 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,37 @@ +#!/bin/sh + +if [ "$LEFTHOOK" = "0" ]; then + exit 0 +fi + +if [ -t 1 ] ; then + exec < /dev/tty ; # <- enables interactive shell +fi + +dir="$(git rev-parse --show-toplevel)" + +call_lefthook() +{ + if lefthook -h >/dev/null 2>&1 + then + eval lefthook $@ + elif test -f "$dir/node_modules/@arkweid/lefthook/bin/lefthook" + then + eval "$dir/node_modules/@arkweid/lefthook/bin/lefthook $@" + elif bundle exec lefthook -h >/dev/null 2>&1 + then + bundle exec lefthook $@ + elif npx @arkweid/lefthook -h >/dev/null 2>&1 + then + npx @arkweid/lefthook $@ + elif yarn lefthook -h >/dev/null 2>&1 + then + yarn lefthook $@ + else + echo "Can't find lefthook in PATH" + fi +} + + + +call_lefthook "run pre-commit $@" diff --git a/.husky/prepare-commit-msg b/.husky/prepare-commit-msg new file mode 100755 index 0000000..82a53ae --- /dev/null +++ b/.husky/prepare-commit-msg @@ -0,0 +1,39 @@ +#!/bin/sh + +if [ "$LEFTHOOK" = "0" ]; then + exit 0 +fi + +if [ -t 1 ] ; then + exec < /dev/tty ; # <- enables interactive shell +fi + +dir="$(git rev-parse --show-toplevel)" + +call_lefthook() +{ + if lefthook -h >/dev/null 2>&1 + then + eval lefthook $@ + elif test -f "$dir/node_modules/@arkweid/lefthook/bin/lefthook" + then + eval "$dir/node_modules/@arkweid/lefthook/bin/lefthook $@" + elif bundle exec lefthook -h >/dev/null 2>&1 + then + bundle exec lefthook $@ + elif npx @arkweid/lefthook -h >/dev/null 2>&1 + then + npx @arkweid/lefthook $@ + elif yarn lefthook -h >/dev/null 2>&1 + then + yarn lefthook $@ + else + echo "Can't find lefthook in PATH" + fi +} + +# lefthook_version: 9df6c1f1f0b607d16db43f25a405e842 + +call_lefthook "install" + +call_lefthook "run prepare-commit-msg $@" diff --git a/example/app.json b/example/app.json index e14d01d..6fb640b 100644 --- a/example/app.json +++ b/example/app.json @@ -31,7 +31,9 @@ "package": "com.webridge.navigation" }, "web": { - "favicon": "./assets/favicon.png" + "favicon": "./assets/favicon.png", + "name": "Navigation", + "description": "Navigation" }, "plugins": [ "./expo-plugin-android-material-you-bottom-bar", diff --git a/example/src/NavigatorScreens.tsx b/example/src/NavigatorScreens.tsx index ea2f6c7..97bff18 100644 --- a/example/src/NavigatorScreens.tsx +++ b/example/src/NavigatorScreens.tsx @@ -12,17 +12,29 @@ import { export const AuthScreen = registerScreen( '/auth', lazy(() => import('./AuthScreen')), - () => {} + () => {}, + { + title: 'Login', + description: 'Login to your account', + } ); export const HomeScreen = registerScreen( '/home', lazy(() => import('./HomeScreen')), - () => {} + () => {}, + { + title: 'Home', + description: 'Home is where the heart is', + } ); export const AccountScreen = registerScreen( '/account', RequireAuthHOC(lazy(() => import('./AccountScreen'))), - () => {} + () => {}, + { + title: 'Account', + description: 'Your account', + } ); export const PostsScreen = registerScreen( @@ -33,6 +45,10 @@ export const PostsScreen = registerScreen( staleTime: 3000, }); return 'testQueryReference'; + }, + { + title: 'Posts', + description: 'All posts', } ); @@ -47,5 +63,9 @@ export const PostScreen = registerScreen( { staleTime: 3000 } ); return 'testQueryReference'; + }, + { + title: 'Post', + description: 'A post', } ); diff --git a/package.json b/package.json index 0f1086c..929d7f5 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@types/color": "^3.0.3", "@types/jest": "^29.2.4", "@types/react": "^18.0.28", + "@types/react-helmet": "^6.1.6", "@types/react-native": "^0.71.3", "color": "^4.2.3", "commitlint": "^17.0.2", @@ -177,6 +178,6 @@ }, "dependencies": { "expo-status-bar": "^1.4.4", - "react-helmet-async": "^1.3.0" + "react-helmet": "^6.1.0" } } diff --git a/src/Head.tsx b/src/Head.tsx index 2dfc5cd..11971df 100644 --- a/src/Head.tsx +++ b/src/Head.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Helmet } from 'react-helmet-async'; +import { Helmet } from 'react-helmet'; export function Head({ children }: { children: any }) { return {children}; diff --git a/src/StatusBar.web.tsx b/src/StatusBar.web.tsx index 21b4ed3..b6664d0 100644 --- a/src/StatusBar.web.tsx +++ b/src/StatusBar.web.tsx @@ -1,12 +1,27 @@ import * as React from 'react'; import type { StatusBarProps } from 'expo-status-bar'; - +const getElement = () => document.querySelector('meta[name=theme-color]'); function StatusBar({ backgroundColor }: StatusBarProps) { + const originalColor = React.useRef( + getElement()?.getAttribute('content') || null + ); + React.useEffect(() => { if (backgroundColor && typeof backgroundColor === 'string') { - const themeColor = document.querySelector('meta[name=theme-color]'); + const themeColor = getElement(); themeColor?.setAttribute('content', backgroundColor); + + const orgColor = originalColor.current; + return () => { + if (orgColor) { + themeColor?.setAttribute('content', orgColor); + } else { + themeColor?.remove(); + } + }; } + return undefined; }, [backgroundColor]); + return null; } -export default StatusBar; +export default React.memo(StatusBar); diff --git a/src/navigation/NavigationStack.web.tsx b/src/navigation/NavigationStack.web.tsx index 3068960..618a222 100644 --- a/src/navigation/NavigationStack.web.tsx +++ b/src/navigation/NavigationStack.web.tsx @@ -4,34 +4,48 @@ import { NavigationMotion } from 'navigation-react-mobile'; import OptimizedContext, { OptimizedContextProvider, } from '../contexts/OptimizedContext'; +import { Head } from '../Head'; +import type { BaseScreen } from 'react-native-ridge-navigation'; function NavigationStack({ renderWeb }: { renderWeb?: (key: string) => any }) { const { theme } = React.useContext(OptimizedContext); return ( ( -
- - {renderWeb?.(state.key) || scene} - -
- )} + renderMotion={(_, scene, key, active, state, data) => { + const screen = state.screen as BaseScreen; + const screenOptions = screen?.options; + const title = screenOptions?.title; + const description = screenOptions?.description; + return ( +
+ {active && ( + + {title || ''} + + + )} + + {renderWeb?.(state.key) || scene} + +
+ ); + }} /> ); } diff --git a/src/navigationUtils.ts b/src/navigationUtils.ts index 1d7117f..9ca5f58 100644 --- a/src/navigationUtils.ts +++ b/src/navigationUtils.ts @@ -18,6 +18,7 @@ export interface BaseScreen { path: string; element: ComponentType; preload: (params: any) => any; + options?: ScreenOptions; } export interface FluentParams { @@ -101,15 +102,20 @@ export function createNormalRoot(child: BaseScreen): RootChildNormal { export type RootValue = RootChildNormal | RootChildBottomTabs; export type Root = Record; +export type ScreenOptions = { + title?: string; + description?: string; +}; export function registerScreen< Path extends string, E extends ComponentType, Preload extends (params: ExtractRouteParams) => void ->(path: Path, element: E, preload: Preload) { +>(path: Path, element: E, preload: Preload, options?: ScreenOptions) { return { path, element, preload, + options, }; } diff --git a/yarn.lock b/yarn.lock index 4dc37e3..f030cca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1120,13 +1120,6 @@ dependencies: regenerator-runtime "^0.13.10" -"@babel/runtime@^7.12.5": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== - dependencies: - regenerator-runtime "^0.13.11" - "@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3": version "7.18.10" resolved "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" @@ -2767,6 +2760,13 @@ resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/react-helmet@^6.1.6": + version "6.1.6" + resolved "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.6.tgz#7d1afd8cbf099616894e8240e9ef70e3c6d7506d" + integrity sha512-ZKcoOdW/Tg+kiUbkFCBtvDw0k3nD4HJ/h/B9yWxN4uDO8OkRksWTO+EL+z/Qu3aHTeTll3Ro0Cc/8UhwBCMG5A== + dependencies: + "@types/react" "*" + "@types/react-native@^0.71.3": version "0.71.3" resolved "https://registry.npmjs.org/@types/react-native/-/react-native-0.71.3.tgz#537f669ed6b38b5ae47444bd9d253c4cff23bed7" @@ -12397,21 +12397,20 @@ react-error-overlay@^6.0.9: resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-fast-compare@^3.2.0: +react-fast-compare@^3.1.1: version "3.2.2" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== -react-helmet-async@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" - integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== dependencies: - "@babel/runtime" "^7.12.5" - invariant "^2.2.4" + object-assign "^4.1.1" prop-types "^15.7.2" - react-fast-compare "^3.2.0" - shallowequal "^1.1.0" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: version "18.2.0" @@ -12521,6 +12520,11 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" +react-side-effect@^2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" + integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== + react@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -12703,7 +12707,7 @@ regenerate@^1.4.2: resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.11" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== @@ -13396,11 +13400,6 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallowequal@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" - integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"