refactor(mobile): improve navigation header and layout components
- Updated `NavigationHeader` to enhance modal presentation handling and UI customization. - Adjusted `getDefaultHeaderHeight` function to simplify header height calculations for iOS devices. - Integrated `NativeNavigationHeader` in the terms screen for consistent header behavior. - Cleaned up layout files by removing unused imports and optimizing component structure. This refactor aims to streamline navigation and improve the overall user experience across mobile screens. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
bfec384151
commit
883ed75bd4
|
|
@ -1,6 +1,6 @@
|
|||
import { cn } from "@follow/utils"
|
||||
import { getDefaultHeaderHeight, HeaderTitle } from "@react-navigation/elements"
|
||||
import { router, useNavigation } from "expo-router"
|
||||
import { HeaderTitle } from "@react-navigation/elements"
|
||||
import { router, Stack, useNavigation } from "expo-router"
|
||||
import type { FC, PropsWithChildren, ReactNode } from "react"
|
||||
import { createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"
|
||||
import type { LayoutChangeEvent } from "react-native"
|
||||
|
|
@ -14,12 +14,14 @@ import Animated, {
|
|||
} from "react-native-reanimated"
|
||||
import type { DefaultStyle } from "react-native-reanimated/lib/typescript/hook/commonTypes"
|
||||
import { useSafeAreaFrame, useSafeAreaInsets } from "react-native-safe-area-context"
|
||||
import type { NativeStackNavigationOptions } from "react-native-screens/lib/typescript/native-stack/types"
|
||||
import type { ViewProps } from "react-native-svg/lib/typescript/fabric/utils"
|
||||
import { useColor } from "react-native-uikit-colors"
|
||||
|
||||
import { MingcuteLeftLineIcon } from "@/src/icons/mingcute_left_line"
|
||||
|
||||
import { ThemedBlurView } from "../../common/ThemedBlurView"
|
||||
import { getDefaultHeaderHeight } from "../utils"
|
||||
import { NavigationContext } from "../views/NavigationContext"
|
||||
import { SetNavigationHeaderHeightContext } from "../views/NavigationHeaderContext"
|
||||
|
||||
|
|
@ -137,9 +139,10 @@ export const InternalNavigationHeader = ({
|
|||
const insets = useSafeAreaInsets()
|
||||
const frame = useSafeAreaFrame()
|
||||
const defaultHeight = useMemo(
|
||||
() => getDefaultHeaderHeight(frame, modal, insets.top),
|
||||
() => getDefaultHeaderHeight(frame, modal, modal ? 0 : insets.top),
|
||||
[frame, insets.top, modal],
|
||||
)
|
||||
|
||||
const border = useColor("opaqueSeparator")
|
||||
const opacityAnimated = useSharedValue(0)
|
||||
const { scrollY } = useContext(NavigationContext)!
|
||||
|
|
@ -172,11 +175,8 @@ export const InternalNavigationHeader = ({
|
|||
)
|
||||
const rootTitleBarStyle = useAnimatedStyle(() => {
|
||||
const styles = {
|
||||
paddingTop: insets.top,
|
||||
|
||||
minHeight: defaultHeight,
|
||||
paddingTop: modal ? 0 : insets.top,
|
||||
position: "relative",
|
||||
|
||||
overflow: "hidden",
|
||||
} satisfies DefaultStyle
|
||||
if (hideableBottom) {
|
||||
|
|
@ -316,3 +316,24 @@ export const UINavigationHeaderActionButton = ({
|
|||
)
|
||||
}
|
||||
const Noop = () => null
|
||||
|
||||
/**
|
||||
* NativeNavigationHeader wrapped react navigation native-stack is universal in modal and stack, but there are significant limitations in UI customization.
|
||||
*/
|
||||
export interface NativeNavigationHeaderProps
|
||||
extends Pick<NativeStackNavigationOptions, "headerLeft" | "headerRight" | "headerTitle"> {}
|
||||
export const NativeNavigationHeader: FC<NativeNavigationHeaderProps> = (props) => {
|
||||
const navigation = useNavigation()
|
||||
return (
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerShown: true,
|
||||
|
||||
headerLeft: () => <DefaultHeaderBackButton canGoBack={navigation.canGoBack()} />,
|
||||
headerTransparent: true,
|
||||
headerBlurEffect: "systemChromeMaterial",
|
||||
...props,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function getDefaultHeaderHeight(
|
|||
|
||||
const isLandscape = layout.width > layout.height
|
||||
|
||||
if (Platform.OS === "ios" && (Platform.isPad || Platform.isTV)) {
|
||||
if (Platform.OS === "ios" && Platform.isPad) {
|
||||
if (modalPresentation) {
|
||||
headerHeight = 56
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ export const NavigationBlurEffectHeader = ({
|
|||
<NavigationContext.Provider value={navigationContext}>
|
||||
<SetNavigationHeaderHeightContext.Provider value={setHeaderHeight}>
|
||||
<InternalNavigationHeader
|
||||
{...options}
|
||||
modal={options.presentation === "modal" || options.presentation === "formSheet"}
|
||||
title={options.title}
|
||||
headerRight={options.headerRight}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export function EmailLogin() {
|
|||
className="mt-8"
|
||||
/>
|
||||
<TouchableOpacity className="mx-auto mt-10" onPress={() => router.push("/sign-up")}>
|
||||
<Text className="text-accent m-1 text-[15px]">Don't have an account? Sign Up</Text>
|
||||
<Text className="text-accent m-1 text-[15px]">Don't have an account?</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity className="mx-auto mt-4" onPress={() => router.push("/forget-password")}>
|
||||
<Text className="text-secondary-label m-[6] text-[15px]">Forgot password?</Text>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
import { Stack } from "expo-router"
|
||||
import { useColorScheme } from "react-native"
|
||||
|
||||
import { getSystemBackgroundColor } from "@/src/theme/utils"
|
||||
|
||||
export default function HeadlessLayout() {
|
||||
useColorScheme()
|
||||
const systemBackgroundColor = getSystemBackgroundColor()
|
||||
|
||||
return (
|
||||
<Stack
|
||||
screenOptions={{
|
||||
contentStyle: { backgroundColor: systemBackgroundColor },
|
||||
contentStyle: { backgroundColor: "#000" },
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
import { useMemo } from "react"
|
||||
import { useAnimatedValue } from "react-native"
|
||||
import { ScrollView } from "react-native"
|
||||
|
||||
import { NavigationContext } from "@/src/components/layouts/views/NavigationContext"
|
||||
import {
|
||||
NavigationBlurEffectHeader,
|
||||
SafeNavigationScrollView,
|
||||
} from "@/src/components/layouts/views/SafeNavigationScrollView"
|
||||
import { NativeNavigationHeader } from "@/src/components/layouts/header/NavigationHeader"
|
||||
import { Markdown } from "@/src/components/ui/typography/Markdown"
|
||||
|
||||
const txt = `# Terms of Service
|
||||
|
|
@ -96,15 +91,11 @@ export const TermsMarkdown = () => {
|
|||
}
|
||||
|
||||
export default function Teams() {
|
||||
const scrollY = useAnimatedValue(100)
|
||||
|
||||
return (
|
||||
<NavigationContext.Provider value={useMemo(() => ({ scrollY }), [scrollY])}>
|
||||
<SafeNavigationScrollView>
|
||||
<NavigationBlurEffectHeader headerShown title="Terms of Service" />
|
||||
<ScrollView className="bg-system-background" contentInsetAdjustmentBehavior="always">
|
||||
<NativeNavigationHeader headerTitle="Terms of Service" />
|
||||
|
||||
<TermsMarkdown />
|
||||
</SafeNavigationScrollView>
|
||||
</NavigationContext.Provider>
|
||||
<TermsMarkdown />
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,30 +9,35 @@ export default function ModalLayout() {
|
|||
name="add"
|
||||
options={{
|
||||
title: "Add Subscription",
|
||||
presentation: "modal",
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="rsshub-form"
|
||||
options={{
|
||||
title: "RSSHub Form",
|
||||
presentation: "modal",
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="follow"
|
||||
options={{
|
||||
title: "Follow",
|
||||
presentation: "modal",
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="list"
|
||||
options={{
|
||||
title: "List",
|
||||
presentation: "modal",
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="profile"
|
||||
options={{
|
||||
title: "Profile",
|
||||
presentation: "modal",
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -42,6 +47,7 @@ export default function ModalLayout() {
|
|||
headerShown: true,
|
||||
title: "",
|
||||
headerTransparent: true,
|
||||
presentation: "modal",
|
||||
headerLeft: ModalHeaderCloseButton,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -51,6 +57,7 @@ export default function ModalLayout() {
|
|||
headerShown: true,
|
||||
title: "",
|
||||
headerTransparent: true,
|
||||
presentation: "modal",
|
||||
headerLeft: ModalHeaderCloseButton,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -61,6 +68,7 @@ export default function ModalLayout() {
|
|||
title: "",
|
||||
headerTransparent: true,
|
||||
headerLeft: ModalHeaderCloseButton,
|
||||
presentation: "modal",
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
|
|
@ -70,6 +78,7 @@ export default function ModalLayout() {
|
|||
title: "",
|
||||
headerTransparent: true,
|
||||
headerLeft: ModalHeaderCloseButton,
|
||||
presentation: "modal",
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export default function RootLayout() {
|
|||
<View className="flex-1 bg-black">
|
||||
<RootProviders>
|
||||
<Session />
|
||||
|
||||
<AnimatedStack />
|
||||
|
||||
{__DEV__ && <DebugButton />}
|
||||
|
|
|
|||
Loading…
Reference in New Issue