feat(mobile): Enhance PreviewImage and EntryGridItem with interactive preview and animation

- Add onPreview callback to PreviewImage for custom actions during image preview
- Implement WebView entry setting when previewing an item in EntryGridItem
- Add fade-in animation to EntryGridItemAccessory for smoother UI transitions

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-02-19 20:12:43 +08:00
parent 9113c0a2e5
commit bdff613867
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
2 changed files with 23 additions and 6 deletions

View File

@ -11,6 +11,7 @@ interface PreviewImageProps {
aspectRatio: number
Accessory?: FC<any>
AccessoryProps?: any
onPreview?: () => void
}
export const PreviewImage = ({
@ -19,19 +20,21 @@ export const PreviewImage = ({
aspectRatio,
Accessory,
AccessoryProps,
onPreview,
}: PreviewImageProps) => {
const imageRef = useRef<View>(null)
const { openPreview } = usePreviewImage()
return (
<Pressable
onPress={() =>
onPress={() => {
onPreview?.()
openPreview({
imageRef,
images: [{ imageUrl, aspectRatio, blurhash, recyclingKey: imageUrl }],
accessoriesElement: Accessory ? <Accessory {...AccessoryProps} /> : undefined,
})
}
}}
>
<View ref={imageRef}>
<Image

View File

@ -1,12 +1,15 @@
import { FeedViewType } from "@follow/constants"
import { LinearGradient } from "expo-linear-gradient"
import { useMemo } from "react"
import { useEffect, useMemo } from "react"
import { ScrollView, Text, View } from "react-native"
import Animated from "react-native-reanimated"
import Animated, { useSharedValue, withTiming } from "react-native-reanimated"
import { useSafeAreaInsets } from "react-native-safe-area-context"
import { useUISettingKey } from "@/src/atoms/settings/ui"
import { EntryContentWebView } from "@/src/components/native/webview/EntryContentWebView"
import {
EntryContentWebView,
setWebViewEntry,
} from "@/src/components/native/webview/EntryContentWebView"
import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime"
import { FeedIcon } from "@/src/components/ui/icon/feed-icon"
import { ImageContextMenu } from "@/src/components/ui/image/ImageContextMenu"
@ -49,6 +52,11 @@ export function EntryGridItem(props: MasonryItem) {
return imageUrl ? (
<ImageContextMenu imageUrl={imageUrl}>
<PreviewImage
onPreview={() => {
if (item) {
setWebViewEntry(item)
}
}}
imageUrl={imageUrl}
blurhash={blurhash}
aspectRatio={aspectRatio}
@ -105,11 +113,17 @@ const EntryGridItemAccessory = ({ id }: { id: string }) => {
const entry = useEntry(id)
const feed = useFeed(entry?.feedId || "")
const insets = useSafeAreaInsets()
const opacityValue = useSharedValue(0)
useEffect(() => {
opacityValue.value = withTiming(1, { duration: 1000 })
}, [opacityValue])
if (!entry) {
return null
}
return (
<Animated.View className="absolute inset-x-0 bottom-0">
<Animated.View style={{ opacity: opacityValue }} className="absolute inset-x-0 bottom-0">
<LinearGradient colors={["transparent", "#000"]} locations={[0.1, 1]} className="flex-1">
<View className="flex-row items-center gap-2">
<View className="border-non-opaque-separator overflow-hidden rounded-full border">