From 73fb08ed55871c9e4c5b78d791d29de780bf9565 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Tue, 11 Mar 2025 16:05:20 +0800 Subject: [PATCH] feat(mobile): avoid direct expo-image --- .../src/components/common/AnimatedComponents.tsx | 5 +++-- .../src/components/ui/icon/fallback-icon.tsx | 3 ++- apps/mobile/src/components/ui/icon/feed-icon.tsx | 2 +- apps/mobile/src/components/ui/image/Image.tsx | 15 +++++++++++---- .../modules/discover/search-tabs/SearchFeed.tsx | 15 +++------------ .../modules/discover/search-tabs/SearchList.tsx | 9 ++------- apps/mobile/src/modules/player/PlayerTabBar.tsx | 4 ++-- apps/mobile/src/screens/player.tsx | 5 +++-- packages/utils/src/headers.ts | 2 +- 9 files changed, 28 insertions(+), 32 deletions(-) diff --git a/apps/mobile/src/components/common/AnimatedComponents.tsx b/apps/mobile/src/components/common/AnimatedComponents.tsx index 6da5b5c47..1acf969a5 100644 --- a/apps/mobile/src/components/common/AnimatedComponents.tsx +++ b/apps/mobile/src/components/common/AnimatedComponents.tsx @@ -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) diff --git a/apps/mobile/src/components/ui/icon/fallback-icon.tsx b/apps/mobile/src/components/ui/icon/fallback-icon.tsx index 7131e4fdc..27ea4331a 100644 --- a/apps/mobile/src/components/ui/icon/fallback-icon.tsx +++ b/apps/mobile/src/components/ui/icon/fallback-icon.tsx @@ -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, diff --git a/apps/mobile/src/components/ui/icon/feed-icon.tsx b/apps/mobile/src/components/ui/icon/feed-icon.tsx index 550a5bfa4..6e4ad2a1b 100644 --- a/apps/mobile/src/components/ui/icon/feed-icon.tsx +++ b/apps/mobile/src/components/ui/icon/feed-icon.tsx @@ -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< diff --git a/apps/mobile/src/components/ui/image/Image.tsx b/apps/mobile/src/components/ui/image/Image.tsx index 06cf35b95..ca54ea8f0 100644 --- a/apps/mobile/src/components/ui/image/Image.tsx +++ b/apps/mobile/src/components/ui/image/Image.tsx @@ -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( }, [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 ( >["data"][number] export const SearchFeed = () => { @@ -168,12 +160,11 @@ const PreviewItem = ({ entry }: { entry: NonNullable diff --git a/apps/mobile/src/modules/discover/search-tabs/SearchList.tsx b/apps/mobile/src/modules/discover/search-tabs/SearchList.tsx index e52814db1..16082abb2 100644 --- a/apps/mobile/src/modules/discover/search-tabs/SearchList.tsx +++ b/apps/mobile/src/modules/discover/search-tabs/SearchList.tsx @@ -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 }) => { {item.list?.image ? ( - + ) : ( !!item.list?.title && )} diff --git a/apps/mobile/src/modules/player/PlayerTabBar.tsx b/apps/mobile/src/modules/player/PlayerTabBar.tsx index 8da9fbfcd..697fa8a4c 100644 --- a/apps/mobile/src/modules/player/PlayerTabBar.tsx +++ b/apps/mobile/src/modules/player/PlayerTabBar.tsx @@ -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 }) { }} > - + {activeTrack?.title ?? ""} diff --git a/apps/mobile/src/screens/player.tsx b/apps/mobile/src/screens/player.tsx index 526a626ce..d368e8b28 100644 --- a/apps/mobile/src/screens/player.tsx +++ b/apps/mobile/src/screens/player.tsx @@ -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 ( - + ) } diff --git a/packages/utils/src/headers.ts b/packages/utils/src/headers.ts index fd939a21f..df2bab361 100644 --- a/packages/utils/src/headers.ts +++ b/packages/utils/src/headers.ts @@ -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