feat(mobile): avoid direct expo-image

This commit is contained in:
DIYgod 2025-03-11 16:05:20 +08:00
parent 90cb12870c
commit 73fb08ed55
No known key found for this signature in database
9 changed files with 28 additions and 32 deletions

View File

@ -1,12 +1,13 @@
import { Image as ExpoImage } from "expo-image"
import { Animated, FlatList, Pressable, ScrollView, TouchableOpacity } from "react-native"
import Reanimated from "react-native-reanimated"
import { Image } from "@/src/components/ui/image/Image"
export const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView)
export const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
export const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity)
export const ReAnimatedExpoImage = Reanimated.createAnimatedComponent(ExpoImage)
export const ReAnimateImage = Reanimated.createAnimatedComponent(Image)
export const ReAnimatedPressable = Reanimated.createAnimatedComponent(Pressable)
export const ReAnimatedScrollView = Reanimated.createAnimatedComponent(ScrollView)
export const ReAnimatedTouchableOpacity = Reanimated.createAnimatedComponent(TouchableOpacity)

View File

@ -1,11 +1,12 @@
import { getBackgroundGradient, isCJKChar } from "@follow/utils"
import { Image } from "expo-image"
import { LinearGradient } from "expo-linear-gradient"
import { useMemo, useState } from "react"
import type { DimensionValue, StyleProp, TextStyle, ViewStyle } from "react-native"
import { StyleSheet, Text, View } from "react-native"
import { useColor } from "react-native-uikit-colors"
import { Image } from "@/src/components/ui/image/Image"
export const FallbackIcon = ({
title,
url,

View File

@ -1,11 +1,11 @@
import type { FeedViewType } from "@follow/constants"
import type { ImageProps } from "expo-image"
import type { ReactNode } from "react"
import { useMemo } from "react"
import type { FeedSchema } from "@/src/database/schemas/types"
import { getFeedIconSource } from "@/src/lib/image"
import type { ImageProps } from "../image/Image"
import { Image } from "../image/Image"
export type FeedIconRequiredFeed = Pick<

View File

@ -1,6 +1,6 @@
import { createBuildSafeHeaders } from "@follow/utils/src/headers"
import { getImageProxyUrl, IMAGE_PROXY_URL } from "@follow/utils/src/img-proxy"
import type { ImageProps as ExpoImageProps } from "expo-image"
import type { ImageErrorEventData, ImageProps as ExpoImageProps } from "expo-image"
import { Image as ExpoImage } from "expo-image"
import { forwardRef, useCallback, useMemo, useState } from "react"
@ -55,9 +55,16 @@ export const Image = forwardRef<ExpoImage, ImageProps>(
}, [proxy?.height, proxy?.width, safeSource])
const [isError, setIsError] = useState(false)
const onError = useCallback(() => {
setIsError(true)
}, [])
const onError = useCallback(
(e: ImageErrorEventData) => {
if (isError) {
rest.onError?.(e)
} else {
setIsError(true)
}
},
[isError, rest],
)
return (
<ExpoImage

View File

@ -1,8 +1,5 @@
import { FeedViewType } from "@follow/constants"
import { createBuildSafeHeaders } from "@follow/utils/src/headers"
import { IMAGE_PROXY_URL } from "@follow/utils/src/img-proxy"
import { useQuery } from "@tanstack/react-query"
import { Image } from "expo-image"
import { router } from "expo-router"
import { useAtomValue } from "jotai"
import type { FC } from "react"
@ -12,20 +9,15 @@ import { ScrollView } from "react-native-gesture-handler"
import Animated, { FadeInUp } from "react-native-reanimated"
import { FeedIcon } from "@/src/components/ui/icon/feed-icon"
import { Image } from "@/src/components/ui/image/Image"
import { ItemPressable } from "@/src/components/ui/pressable/ItemPressable"
import { apiClient } from "@/src/lib/api-fetch"
import { proxyEnv } from "@/src/lib/proxy-env"
import { useSubscriptionByFeedId } from "@/src/store/subscription/hooks"
import { useSearchPageContext } from "../ctx"
import { BaseSearchPageFlatList, ItemSeparator, RenderScrollComponent } from "./__base"
import { useDataSkeleton } from "./hooks"
const buildSafeHeaders = createBuildSafeHeaders(proxyEnv.VITE_WEB_URL, [
IMAGE_PROXY_URL,
proxyEnv.VITE_API_URL,
])
type SearchResultItem = Awaited<ReturnType<typeof apiClient.discover.$post>>["data"][number]
export const SearchFeed = () => {
@ -168,12 +160,11 @@ const PreviewItem = ({ entry }: { entry: NonNullable<SearchResultItem["entries"]
{!!firstMedia && (
<View className="bg-gray-6 ml-auto size-[52px] shrink-0 overflow-hidden rounded-lg">
<Image
source={{ uri: firstMedia.url, headers: buildSafeHeaders({ url: firstMedia.url }) }}
source={{ uri: firstMedia.url }}
className="size-full rounded-lg"
contentFit="cover"
transition={500}
placeholder={{
blurHash: firstMedia.blurhash,
blurhash: firstMedia.blurhash,
}}
/>
</View>

View File

@ -1,11 +1,11 @@
import { useQuery } from "@tanstack/react-query"
import { Image } from "expo-image"
import { router } from "expo-router"
import { useAtomValue } from "jotai"
import { memo } from "react"
import { Text, View } from "react-native"
import { FallbackIcon } from "@/src/components/ui/icon/fallback-icon"
import { Image } from "@/src/components/ui/image/Image"
import { ItemPressable } from "@/src/components/ui/pressable/ItemPressable"
import { apiClient } from "@/src/lib/api-fetch"
import { useSubscriptionByListId } from "@/src/store/subscription/hooks"
@ -71,12 +71,7 @@ const SearchListCard = memo(({ item }: { item: SearchResultItem }) => {
<View className="flex-row items-center gap-2 pl-4 pr-2">
<View className="size-[32px] overflow-hidden rounded-lg">
{item.list?.image ? (
<Image
source={item.list.image}
className="size-full"
contentFit="cover"
transition={1000}
/>
<Image source={{ uri: item.list.image }} className="size-full" contentFit="cover" />
) : (
!!item.list?.title && <FallbackIcon title={item.list.title} size={32} />
)}

View File

@ -1,5 +1,4 @@
import { cn } from "@follow/utils"
import { Image } from "expo-image"
import { router, usePathname } from "expo-router"
import { useEffect } from "react"
import { Pressable, Text, View } from "react-native"
@ -10,6 +9,7 @@ import Animated, {
withTiming,
} from "react-native-reanimated"
import { Image } from "@/src/components/ui/image/Image"
import { useActiveTrack } from "@/src/lib/player"
import { usePrefetchImageColors } from "@/src/store/image/hooks"
@ -46,7 +46,7 @@ export function PlayerTabBar({ className }: { className?: string }) {
}}
>
<View className="flex flex-row items-center gap-4 overflow-hidden rounded-2xl p-2">
<Image source={activeTrack?.artwork} className="size-12 rounded-lg" />
<Image source={{ uri: activeTrack?.artwork ?? "" }} className="size-12 rounded-lg" />
<View className="flex-1 overflow-hidden">
<Text className="text-label text-lg font-semibold" numberOfLines={1}>
{activeTrack?.title ?? ""}

View File

@ -1,5 +1,4 @@
import { cn } from "@follow/utils"
import { Image } from "expo-image"
import { LinearGradient } from "expo-linear-gradient"
import { router } from "expo-router"
import { useEffect, useMemo } from "react"
@ -12,6 +11,8 @@ import Reanimated, {
} from "react-native-reanimated"
import { SheetScreen } from "react-native-sheet-transitions"
import { Image } from "@/src/components/ui/image/Image"
import { gentleSpringPreset } from "../constants/spring"
import { useActiveTrack, useIsPlaying } from "../lib/player"
import { PlayerScreenContext, usePlayerScreenContext } from "../modules/player/context"
@ -36,7 +37,7 @@ function CoverArt({ cover }: { cover?: string }) {
return (
<Reanimated.View className="mx-auto my-12 aspect-square w-[87%] shadow" style={[animatedStyle]}>
<Image source={cover} className="size-full rounded-lg" />
<Image source={{ uri: cover ?? "" }} className="size-full rounded-lg" />
</Reanimated.View>
)
}

View File

@ -40,7 +40,7 @@ export const createBuildSafeHeaders =
headers.Referer = urlObj.origin
headers.Origin = urlObj.origin
} catch (error) {
console.error(error)
console.warn(error)
}
return headers