diff --git a/apps/mobile/src/components/layouts/header/NavigationHeader.tsx b/apps/mobile/src/components/layouts/header/NavigationHeader.tsx index 693f06a64..d99cbdb74 100644 --- a/apps/mobile/src/components/layouts/header/NavigationHeader.tsx +++ b/apps/mobile/src/components/layouts/header/NavigationHeader.tsx @@ -20,7 +20,6 @@ import { useColor } from "react-native-uikit-colors" import { MingcuteLeftLineIcon } from "@/src/icons/mingcute_left_line" import { ThemedBlurView } from "../../common/ThemedBlurView" -import { PortalHost } from "../../ui/portal" import { NavigationContext } from "../views/NavigationContext" import { SetNavigationHeaderHeightContext } from "../views/NavigationHeaderContext" @@ -29,6 +28,7 @@ export interface NavigationHeaderRawProps { canGoBack: boolean }> headerTitle?: FC> | ReactNode + headerTitleAbsolute?: boolean headerRight?: FC<{ canGoBack: boolean }> @@ -131,6 +131,7 @@ export const NavigationHeader = ({ modal = false, hideableBottom, hideableBottomHeight, + headerTitleAbsolute, ...rest }: NavigationHeaderProps) => { const insets = useSafeAreaInsets() @@ -256,36 +257,42 @@ export const NavigationHeader = ({ }} pointerEvents={"box-none"} > - - {/* Left */} - { - setHeaderLeftWidth(e.nativeEvent.layout.width) - }, [])} - > - - - {/* Center */} - { - setTitleWidth(e.nativeEvent.layout.width) - }, [])} - className="flex-1 items-center justify-center" - pointerEvents={"box-none"} - style={{ - marginHorizontal: titleMarginHorizontal, - transform: [{ translateX: titleTransformX }], - }} - > - {headerTitle} - - {/* Right */} - - - - + {/* Left */} + { + setHeaderLeftWidth(e.nativeEvent.layout.width) + }, [])} + > + + + {/* Center */} + + { + setTitleWidth(e.nativeEvent.layout.width) + }} + className="flex-1 items-center justify-center" + pointerEvents={"box-none"} + style={{ + marginHorizontal: titleMarginHorizontal, + transform: [{ translateX: titleTransformX }], + }} + > + {headerTitleAbsolute ? : headerTitle} + + + {/* Right */} + + + + + {headerTitleAbsolute && headerTitle} + {!!hideableBottom && ( diff --git a/apps/mobile/src/components/layouts/views/SafeNavigationScrollView.tsx b/apps/mobile/src/components/layouts/views/SafeNavigationScrollView.tsx index 945ea098d..664221576 100644 --- a/apps/mobile/src/components/layouts/views/SafeNavigationScrollView.tsx +++ b/apps/mobile/src/components/layouts/views/SafeNavigationScrollView.tsx @@ -90,11 +90,13 @@ export const SafeNavigationScrollView: FC = ({ export const NavigationBlurEffectHeader = ({ headerHideableBottom, headerHideableBottomHeight, + headerTitleAbsolute, ...props }: NativeStackNavigationOptions & { blurThreshold?: number headerHideableBottomHeight?: number headerHideableBottom?: () => React.ReactNode + headerTitleAbsolute?: boolean }) => { const label = useColor("label") @@ -134,6 +136,7 @@ export const NavigationBlurEffectHeader = ({ headerLeft={options.headerLeft} hideableBottom={hideableBottom} hideableBottomHeight={headerHideableBottomHeight} + headerTitleAbsolute={headerTitleAbsolute} // @ts-expect-error headerTitle={options.headerTitle} /> diff --git a/apps/mobile/src/components/ui/avatar/UserAvatar.tsx b/apps/mobile/src/components/ui/avatar/UserAvatar.tsx index 199369337..188d3f880 100644 --- a/apps/mobile/src/components/ui/avatar/UserAvatar.tsx +++ b/apps/mobile/src/components/ui/avatar/UserAvatar.tsx @@ -13,7 +13,10 @@ export const UserAvatar = ({ image, size = 24, name, className }: UserAvatarProp if (!image) { return ( {name.slice(0, 2)} @@ -24,7 +27,7 @@ export const UserAvatar = ({ image, size = 24, name, className }: UserAvatarProp return ( ([]) const manager = useRef() const mount = useCallback((children: React.ReactNode, _key?: PortalKey) => { let key = _key if (!key) { - key = nextKey.current - nextKey.current = `${id}-${Number.parseInt(key) + 1}` + nextKey.current++ + key = `${id}-${nextKey.current}` } if (manager.current) { diff --git a/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx b/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx index 1b25b9511..6ebd9388f 100644 --- a/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx +++ b/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx @@ -110,6 +110,7 @@ const HeaderRightActionsImpl = ({ {!isHeaderTitleVisible && ( )} + { setExtraActionContainerWidth(e.nativeEvent.layout.width) diff --git a/apps/mobile/src/modules/entry-content/EntryTitle.tsx b/apps/mobile/src/modules/entry-content/EntryTitle.tsx index d97d70cc3..5cb249b4a 100644 --- a/apps/mobile/src/modules/entry-content/EntryTitle.tsx +++ b/apps/mobile/src/modules/entry-content/EntryTitle.tsx @@ -1,13 +1,25 @@ +import { useTypeScriptHappyCallback } from "@follow/hooks" import { useHeaderHeight } from "@react-navigation/elements" +import { useQuery } from "@tanstack/react-query" +import { router } from "expo-router" import { useCallback, useContext, useEffect, useState } from "react" -import { Text, View } from "react-native" -import Animated, { useSharedValue, withTiming } from "react-native-reanimated" +import { Text, TouchableOpacity, useWindowDimensions, View } from "react-native" +import type { SharedValue } from "react-native-reanimated" +import Animated, { + interpolate, + useAnimatedStyle, + useSharedValue, + withTiming, +} from "react-native-reanimated" +import { useColor } from "react-native-uikit-colors" +import { useUISettingKey } from "@/src/atoms/settings/ui" import { NavigationContext } from "@/src/components/layouts/views/NavigationContext" import { NavigationBlurEffectHeader } from "@/src/components/layouts/views/SafeNavigationScrollView" import { UserAvatar } from "@/src/components/ui/avatar/UserAvatar" import { FeedIcon } from "@/src/components/ui/icon/feed-icon" -import { Portal } from "@/src/components/ui/portal/Portal" +import { MingcuteLeftLineIcon } from "@/src/icons/mingcute_left_line" +import { apiClient } from "@/src/lib/api-fetch" import { EntryContentContext, useEntryContentContext } from "@/src/modules/entry-content/ctx" import { EntryContentHeaderRightActions } from "@/src/modules/entry-content/EntryContentHeaderRightActions" import { useEntry } from "@/src/store/entry/hooks" @@ -41,11 +53,23 @@ export const EntryTitle = ({ title, entryId }: { title: string; entryId: string }, [scrollY, title, titleHeight, headerHeight, opacityAnimatedValue]) const ctxValue = useEntryContentContext() + const headerBarWidth = useWindowDimensions().width return ( <> ( + + ), + [entryId], + )} headerRight={useCallback( () => ( @@ -58,9 +82,13 @@ export const EntryTitle = ({ title, entryId }: { title: string; entryId: string ), [ctxValue, entryId, opacityAnimatedValue, isHeaderTitleVisible], )} - headerTitle={() => ( - - + headerTitle={useCallback( + () => ( + - + ), + [headerBarWidth, opacityAnimatedValue, title], )} /> { ) } + +interface EntryLeftGroupProps { + canGoBack: boolean + entryId: string + titleOpacityShareValue: SharedValue +} + +const EntryLeftGroup = ({ canGoBack, entryId, titleOpacityShareValue }: EntryLeftGroupProps) => { + const label = useColor("label") + + const hideRecentReader = useUISettingKey("hideRecentReader") + const animatedOpacity = useAnimatedStyle(() => { + return { + opacity: interpolate(titleOpacityShareValue.value, [0, 1], [1, 0]), + } + }) + return ( + + router.back()}> + {canGoBack && } + + + {!hideRecentReader && ( + + + + )} + + ) +} + +const EntryReadHistory = ({ entryId }: { entryId: string }) => { + const { data } = useQuery({ + queryKey: ["entry-read-history", entryId], + queryFn: () => { + return apiClient.entries["read-histories"][":id"].$get({ + param: { + id: entryId, + }, + query: { + size: 6, + }, + }) + }, + staleTime: 1000 * 60 * 5, + }) + if (!data?.data.entryReadHistories) return null + return ( + + {data?.data.entryReadHistories.userIds.map((userId, index) => { + const user = data.data.users[userId] + if (!user) return null + return ( + + + + ) + })} + + ) +} diff --git a/apps/mobile/src/modules/entry-list/templates/EntryNormalItem.tsx b/apps/mobile/src/modules/entry-list/templates/EntryNormalItem.tsx index 1926c8719..a61e3839d 100644 --- a/apps/mobile/src/modules/entry-list/templates/EntryNormalItem.tsx +++ b/apps/mobile/src/modules/entry-list/templates/EntryNormalItem.tsx @@ -86,11 +86,11 @@ export function EntryNormalItem({ entryId, extraData }: { entryId: string; extra postfixText="ago" /> - + {title} {view !== FeedViewType.Notifications && ( - + {description} )}