diff --git a/app/(tabs)/challenges.tsx b/app/(tabs)/challenges.tsx index 5eab792..f63d357 100644 --- a/app/(tabs)/challenges.tsx +++ b/app/(tabs)/challenges.tsx @@ -2,6 +2,7 @@ import dayjs from 'dayjs'; import ChallengeProgressCard from '@/components/challenges/ChallengeProgressCard'; import { ConfirmationSheet } from '@/components/ui/ConfirmationSheet'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; @@ -23,7 +24,6 @@ import { import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/app/(tabs)/medications.tsx b/app/(tabs)/medications.tsx index d598f91..59b9b04 100644 --- a/app/(tabs)/medications.tsx +++ b/app/(tabs)/medications.tsx @@ -4,6 +4,7 @@ import { MedicationCard } from '@/components/medication/MedicationCard'; import { TakenMedicationsStack } from '@/components/medication/TakenMedicationsStack'; import { ThemedText } from '@/components/ThemedText'; import { IconSymbol } from '@/components/ui/IconSymbol'; +import { Image } from '@/components/ui/Image'; import { MedicalDisclaimerSheet } from '@/components/ui/MedicalDisclaimerSheet'; import { MedicationAiSummaryInfoSheet } from '@/components/ui/MedicationAiSummaryInfoSheet'; import { Colors } from '@/constants/Colors'; @@ -20,7 +21,6 @@ import { useFocusEffect } from '@react-navigation/native'; import dayjs, { Dayjs } from 'dayjs'; import 'dayjs/locale/zh-cn'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { router } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/app/(tabs)/personal.tsx b/app/(tabs)/personal.tsx index e825e20..98d4b64 100644 --- a/app/(tabs)/personal.tsx +++ b/app/(tabs)/personal.tsx @@ -11,6 +11,7 @@ import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; +import { Image } from '@/components/ui/Image'; import type { BadgeDto } from '@/services/badges'; import { reportBadgeShowcaseDisplayed } from '@/services/badges'; import { updateUser, type UserLanguage } from '@/services/users'; @@ -24,7 +25,6 @@ import { Ionicons } from '@expo/vector-icons'; import { useFocusEffect } from '@react-navigation/native'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/app/badges/index.tsx b/app/badges/index.tsx index d9ac244..ae3e998 100644 --- a/app/badges/index.tsx +++ b/app/badges/index.tsx @@ -1,14 +1,14 @@ +import { BadgeShowcaseModal } from '@/components/badges/BadgeShowcaseModal'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import type { BadgeDto } from '@/services/badges'; import { fetchAvailableBadges, selectBadgesLoading, selectSortedBadges } from '@/store/badgesSlice'; import { DEFAULT_MEMBER_NAME, selectUserProfile } from '@/store/userSlice'; -import { BadgeShowcaseModal } from '@/components/badges/BadgeShowcaseModal'; import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; import { useFocusEffect } from '@react-navigation/native'; import * as Haptics from 'expo-haptics'; -import { Image } from 'expo-image'; import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FlatList, Pressable, RefreshControl, StyleSheet, Text, View } from 'react-native'; diff --git a/app/challenges/[id]/index.tsx b/app/challenges/[id]/index.tsx index f72e5a8..cfac5ea 100644 --- a/app/challenges/[id]/index.tsx +++ b/app/challenges/[id]/index.tsx @@ -1,6 +1,7 @@ import ChallengeProgressCard from '@/components/challenges/ChallengeProgressCard'; import { ChallengeRankingItem } from '@/components/challenges/ChallengeRankingItem'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; @@ -33,7 +34,6 @@ import { BlurView } from 'expo-blur'; import * as Clipboard from 'expo-clipboard'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import * as Haptics from 'expo-haptics'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; import LottieView from 'lottie-react-native'; diff --git a/app/challenges/create-custom.tsx b/app/challenges/create-custom.tsx index 12c72de..d6126ff 100644 --- a/app/challenges/create-custom.tsx +++ b/app/challenges/create-custom.tsx @@ -1,8 +1,8 @@ +import { Image } from '@/components/ui/Image'; import i18n from '@/i18n'; import dayjs from 'dayjs'; import { BlurView } from 'expo-blur'; import * as Clipboard from 'expo-clipboard'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; diff --git a/app/coach.tsx b/app/coach.tsx index b4dcedb..3e09bde 100644 --- a/app/coach.tsx +++ b/app/coach.tsx @@ -18,6 +18,7 @@ import { import Animated, { FadeInDown, FadeInUp, Layout } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { getTabBarBottomPadding } from '@/constants/TabBar'; import { useAppSelector } from '@/hooks/redux'; @@ -29,7 +30,6 @@ import { loadAiCoachSessionCache, saveAiCoachSessionCache } from '@/services/aiC import { api, getAuthToken, postTextStream } from '@/services/api'; import { selectLatestMoodRecordByDate } from '@/store/moodSlice'; import { generateWelcomeMessage, hasRecordedMoodToday } from '@/utils/welcomeMessage'; -import { Image } from 'expo-image'; import { HistoryModal } from '../components/model/HistoryModal'; import { ActionSheet } from '../components/ui/ActionSheet'; diff --git a/app/food-library.tsx b/app/food-library.tsx index 217d68f..f31c2bb 100644 --- a/app/food-library.tsx +++ b/app/food-library.tsx @@ -1,6 +1,7 @@ import { CreateCustomFoodModal, type CustomFoodData } from '@/components/model/food/CreateCustomFoodModal'; import { FoodDetailModal } from '@/components/model/food/FoodDetailModal'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { DEFAULT_IMAGE_FOOD } from '@/constants/Image'; import { useAppDispatch } from '@/hooks/redux'; @@ -13,7 +14,6 @@ import { fetchDailyNutritionData } from '@/store/nutritionSlice'; import type { FoodItem, MealType, SelectedFoodItem } from '@/types/food'; import { saveNutritionToHealthKit } from '@/utils/health'; import { Ionicons } from '@expo/vector-icons'; -import { Image } from 'expo-image'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useEffect, useMemo, useState } from 'react'; import { diff --git a/app/food/analysis-result.tsx b/app/food/analysis-result.tsx index 7b8d559..4ab945c 100644 --- a/app/food/analysis-result.tsx +++ b/app/food/analysis-result.tsx @@ -1,5 +1,6 @@ import { CircularRing } from '@/components/CircularRing'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { ROUTES } from '@/constants/Routes'; import { useAppSelector } from '@/hooks/redux'; @@ -9,7 +10,6 @@ import { addDietRecord, type CreateDietRecordDto, type MealType } from '@/servic import { selectFoodRecognitionResult } from '@/store/foodRecognitionSlice'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useEffect, useState } from 'react'; diff --git a/app/food/camera.tsx b/app/food/camera.tsx index 462b240..a5605fa 100644 --- a/app/food/camera.tsx +++ b/app/food/camera.tsx @@ -1,4 +1,5 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; @@ -6,7 +7,6 @@ import { useI18n } from '@/hooks/useI18n'; import { Ionicons } from '@expo/vector-icons'; import { CameraType, CameraView, useCameraPermissions } from 'expo-camera'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; diff --git a/app/food/food-recognition.tsx b/app/food/food-recognition.tsx index 93f832c..f9b168c 100644 --- a/app/food/food-recognition.tsx +++ b/app/food/food-recognition.tsx @@ -1,4 +1,5 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useMembershipModal } from '@/contexts/MembershipModalContext'; import { useAppDispatch } from '@/hooks/redux'; @@ -11,7 +12,6 @@ import { recognizeFood } from '@/services/foodRecognition'; import { saveRecognitionResult, setError, setLoading } from '@/store/foodRecognitionSlice'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useEffect, useState } from 'react'; diff --git a/app/food/nutrition-analysis-history.tsx b/app/food/nutrition-analysis-history.tsx index b2db8fa..caf95f5 100644 --- a/app/food/nutrition-analysis-history.tsx +++ b/app/food/nutrition-analysis-history.tsx @@ -1,4 +1,5 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useI18n } from '@/hooks/useI18n'; import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding'; @@ -13,7 +14,6 @@ import { triggerLightHaptic } from '@/utils/haptics'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useCallback, useEffect, useState } from 'react'; diff --git a/app/food/nutrition-label-analysis.tsx b/app/food/nutrition-label-analysis.tsx index e3a60d5..afc0c97 100644 --- a/app/food/nutrition-label-analysis.tsx +++ b/app/food/nutrition-label-analysis.tsx @@ -1,4 +1,5 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useCosUpload } from '@/hooks/useCosUpload'; @@ -12,7 +13,6 @@ import { triggerLightHaptic } from '@/utils/haptics'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; diff --git a/app/gallery/index.tsx b/app/gallery/index.tsx index f9b7988..7b6c533 100644 --- a/app/gallery/index.tsx +++ b/app/gallery/index.tsx @@ -1,4 +1,5 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image as ExpoImage } from '@/components/ui/Image'; import { useMembershipModal } from '@/contexts/MembershipModalContext'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useVipService } from '@/hooks/useVipService'; @@ -9,7 +10,6 @@ import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import * as FileSystem from 'expo-file-system/legacy'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image as ExpoImage } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import * as MediaLibrary from 'expo-media-library'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/app/health/profile.tsx b/app/health/profile.tsx index 4a0f851..aeb3377 100644 --- a/app/health/profile.tsx +++ b/app/health/profile.tsx @@ -5,6 +5,7 @@ import { HealthHistoryTab } from '@/components/health/tabs/HealthHistoryTab'; import { MedicalRecordsTab } from '@/components/health/tabs/MedicalRecordsTab'; import { ConfirmationSheet } from '@/components/ui/ConfirmationSheet'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { ROUTES } from '@/constants/Routes'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; @@ -16,15 +17,14 @@ import { joinFamilyGroup, selectFamilyGroup, } from '@/store/familyHealthSlice'; -import { +import { fetchHealthHistory, - selectHealthHistoryProgress + selectHealthHistoryProgress } from '@/store/healthSlice'; import { DEFAULT_MEMBER_NAME } from '@/store/userSlice'; import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { Stack, useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; diff --git a/app/medications/[medicationId].tsx b/app/medications/[medicationId].tsx index 133ac8c..20a92ec 100644 --- a/app/medications/[medicationId].tsx +++ b/app/medications/[medicationId].tsx @@ -2,6 +2,7 @@ import { ExpiryDatePickerModal } from '@/components/medications/ExpiryDatePicker import { ThemedText } from '@/components/ThemedText'; import { ConfirmationSheet } from '@/components/ui/ConfirmationSheet'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import InfoCard from '@/components/ui/InfoCard'; import { Colors } from '@/constants/Colors'; import { DOSAGE_UNITS, DOSAGE_VALUES, FORM_OPTIONS } from '@/constants/Medication'; @@ -37,7 +38,6 @@ import { Picker } from '@react-native-picker/picker'; import Voice from '@react-native-voice/voice'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; diff --git a/app/medications/add-medication.tsx b/app/medications/add-medication.tsx index af46784..c3fa717 100644 --- a/app/medications/add-medication.tsx +++ b/app/medications/add-medication.tsx @@ -1,6 +1,7 @@ import { ThemedText } from '@/components/ThemedText'; import { HeaderBar } from '@/components/ui/HeaderBar'; import { IconSymbol } from '@/components/ui/IconSymbol'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { DOSAGE_UNITS, FORM_OPTIONS } from '@/constants/Medication'; import { useAppDispatch } from '@/hooks/redux'; @@ -15,7 +16,6 @@ import { Picker } from '@react-native-picker/picker'; import Voice from '@react-native-voice/voice'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { router } from 'expo-router'; diff --git a/app/medications/ai-camera.tsx b/app/medications/ai-camera.tsx index d711926..1fbac73 100644 --- a/app/medications/ai-camera.tsx +++ b/app/medications/ai-camera.tsx @@ -1,5 +1,6 @@ import { MedicationPhotoGuideModal } from '@/components/medications/MedicationPhotoGuideModal'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; @@ -10,7 +11,6 @@ import { getItem, setItem } from '@/utils/kvStore'; import { Ionicons } from '@expo/vector-icons'; import { CameraView, useCameraPermissions } from 'expo-camera'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { router } from 'expo-router'; diff --git a/app/medications/ai-progress.tsx b/app/medications/ai-progress.tsx index 59cc731..1c45e52 100644 --- a/app/medications/ai-progress.tsx +++ b/app/medications/ai-progress.tsx @@ -1,11 +1,11 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors, palette } from '@/constants/Colors'; import { useI18n } from '@/hooks/useI18n'; import { getMedicationRecognitionStatus } from '@/services/medications'; import { MedicationRecognitionTask } from '@/types/medication'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { router, useLocalSearchParams } from 'expo-router'; import React, { useEffect, useMemo, useRef, useState } from 'react'; diff --git a/app/medications/manage-medications.tsx b/app/medications/manage-medications.tsx index 60a2ce8..0a683d1 100644 --- a/app/medications/manage-medications.tsx +++ b/app/medications/manage-medications.tsx @@ -2,6 +2,7 @@ import { ThemedText } from '@/components/ThemedText'; import { ConfirmationSheet } from '@/components/ui/ConfirmationSheet'; import { HeaderBar } from '@/components/ui/HeaderBar'; import { IconSymbol } from '@/components/ui/IconSymbol'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; @@ -18,7 +19,6 @@ import type { Medication, MedicationForm } from '@/types/medication'; import { useFocusEffect } from '@react-navigation/native'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { router } from 'expo-router'; import React, { useCallback, useMemo, useState } from 'react'; diff --git a/app/nutrition/records.tsx b/app/nutrition/records.tsx index d84dac0..ab848d3 100644 --- a/app/nutrition/records.tsx +++ b/app/nutrition/records.tsx @@ -3,6 +3,7 @@ import { DateSelector } from '@/components/DateSelector'; import { FloatingFoodOverlay } from '@/components/FloatingFoodOverlay'; import { NutritionRecordCard } from '@/components/NutritionRecordCard'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; @@ -27,7 +28,6 @@ import { Ionicons } from '@expo/vector-icons'; import { useFocusEffect } from '@react-navigation/native'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { router } from 'expo-router'; import React, { useCallback, useEffect, useState } from 'react'; diff --git a/app/profile/edit.tsx b/app/profile/edit.tsx index de4520f..dd32271 100644 --- a/app/profile/edit.tsx +++ b/app/profile/edit.tsx @@ -1,4 +1,5 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; @@ -13,7 +14,6 @@ import { Ionicons } from '@expo/vector-icons'; import DateTimePicker from '@react-native-community/datetimepicker'; import { Picker } from '@react-native-picker/picker'; import { useFocusEffect } from '@react-navigation/native'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { router } from 'expo-router'; import React, { useEffect, useMemo, useState } from 'react'; diff --git a/app/sleep-detail.tsx b/app/sleep-detail.tsx index 6d90a1a..29b7061 100644 --- a/app/sleep-detail.tsx +++ b/app/sleep-detail.tsx @@ -2,6 +2,7 @@ import { InfoModal, type SleepDetailData } from '@/components/sleep/InfoModal'; import { SleepStagesInfoModal } from '@/components/sleep/SleepStagesInfoModal'; import { SleepStageTimeline } from '@/components/sleep/SleepStageTimeline'; import { HeaderBar } from '@/components/ui/HeaderBar'; +import { Image } from '@/components/ui/Image'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useI18n } from '@/hooks/useI18n'; import { @@ -14,7 +15,6 @@ import { import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { router, useLocalSearchParams } from 'expo-router'; import React, { useCallback, useEffect, useState } from 'react'; diff --git a/app/water/detail.tsx b/app/water/detail.tsx index 403ad08..6473d79 100644 --- a/app/water/detail.tsx +++ b/app/water/detail.tsx @@ -1,10 +1,10 @@ +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useWaterDataByDate } from '@/hooks/useWaterData'; import { getQuickWaterAmount } from '@/utils/userPreferences'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { router, useLocalSearchParams } from 'expo-router'; import React, { useEffect, useState } from 'react'; diff --git a/components/AddWaterModal.tsx b/components/AddWaterModal.tsx index ffa2a05..abea45f 100644 --- a/components/AddWaterModal.tsx +++ b/components/AddWaterModal.tsx @@ -1,10 +1,10 @@ +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useWaterDataByDate } from '@/hooks/useWaterData'; import { getQuickWaterAmount, setQuickWaterAmount } from '@/utils/userPreferences'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import React, { useEffect, useState } from 'react'; import { Alert, diff --git a/components/BasalMetabolismCard.tsx b/components/BasalMetabolismCard.tsx index e0b6510..2726b4f 100644 --- a/components/BasalMetabolismCard.tsx +++ b/components/BasalMetabolismCard.tsx @@ -1,9 +1,9 @@ +import { Image } from '@/components/ui/Image'; import { ROUTES } from '@/constants/Routes'; import { useAppSelector } from '@/hooks/redux'; import { selectUserAge, selectUserProfile } from '@/store/userSlice'; import { fetchBasalEnergyBurned } from '@/utils/health'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import { router } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/components/NutritionRecordCard.tsx b/components/NutritionRecordCard.tsx index 3d0c482..fcf3a45 100644 --- a/components/NutritionRecordCard.tsx +++ b/components/NutritionRecordCard.tsx @@ -1,10 +1,10 @@ import { ThemedText } from '@/components/ThemedText'; +import { Image } from '@/components/ui/Image'; import { useI18n } from '@/hooks/useI18n'; import { useThemeColor } from '@/hooks/useThemeColor'; import { DietRecord } from '@/services/dietRecords'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import React, { useMemo, useRef, useState } from 'react'; import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { RectButton, Swipeable } from 'react-native-gesture-handler'; diff --git a/components/StepsCard.tsx b/components/StepsCard.tsx index ca187d8..468f7e1 100644 --- a/components/StepsCard.tsx +++ b/components/StepsCard.tsx @@ -1,21 +1,21 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { - Animated, - InteractionManager, - StyleSheet, - Text, - TouchableOpacity, - View, - ViewStyle + Animated, + InteractionManager, + StyleSheet, + Text, + TouchableOpacity, + View, + ViewStyle } from 'react-native'; +import { Image } from '@/components/ui/Image'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { ChallengeType } from '@/services/challengesApi'; import { reportChallengeProgress, selectChallengeList } from '@/store/challengesSlice'; import { fetchHourlyStepSamples, fetchStepCount, HourlyStepData } from '@/utils/health'; import { logger } from '@/utils/logger'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import { useRouter } from 'expo-router'; import { useTranslation } from 'react-i18next'; import { AnimatedNumber } from './AnimatedNumber'; diff --git a/components/StressMeter.tsx b/components/StressMeter.tsx index 3610af1..2f91c58 100644 --- a/components/StressMeter.tsx +++ b/components/StressMeter.tsx @@ -1,6 +1,6 @@ +import { Image } from '@/components/ui/Image'; import { fetchHRVWithStatus } from '@/utils/health'; import { convertHrvToStressIndex } from '@/utils/stress'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/components/WaterIntakeCard.tsx b/components/WaterIntakeCard.tsx index 39b881d..dce6f4c 100644 --- a/components/WaterIntakeCard.tsx +++ b/components/WaterIntakeCard.tsx @@ -1,10 +1,10 @@ +import { Image } from '@/components/ui/Image'; import { useWaterDataByDate } from '@/hooks/useWaterData'; import { appStoreReviewService } from '@/services/appStoreReview'; import { getQuickWaterAmount } from '@/utils/userPreferences'; import { useFocusEffect } from '@react-navigation/native'; import dayjs from 'dayjs'; import * as Haptics from 'expo-haptics'; -import { Image } from 'expo-image'; import { useRouter } from 'expo-router'; import LottieView from 'lottie-react-native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/components/WorkoutSummaryCard.tsx b/components/WorkoutSummaryCard.tsx index 0c390db..21ec668 100644 --- a/components/WorkoutSummaryCard.tsx +++ b/components/WorkoutSummaryCard.tsx @@ -1,6 +1,6 @@ +import { Image } from '@/components/ui/Image'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import { useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/components/badges/BadgeShowcaseModal.tsx b/components/badges/BadgeShowcaseModal.tsx index ab73d30..b695103 100644 --- a/components/badges/BadgeShowcaseModal.tsx +++ b/components/badges/BadgeShowcaseModal.tsx @@ -1,10 +1,10 @@ +import { Image } from '@/components/ui/Image'; import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; +import dayjs from 'dayjs'; import { BlurView } from 'expo-blur'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; -import dayjs from 'dayjs'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Animated, Modal, Platform, Pressable, Share, StyleSheet, Text, View } from 'react-native'; diff --git a/components/challenges/ChallengeRankingItem.tsx b/components/challenges/ChallengeRankingItem.tsx index ef545c3..393282e 100644 --- a/components/challenges/ChallengeRankingItem.tsx +++ b/components/challenges/ChallengeRankingItem.tsx @@ -1,7 +1,7 @@ +import { Image } from '@/components/ui/Image'; import { useI18n } from '@/hooks/useI18n'; import type { RankingItem } from '@/store/challengesSlice'; import { Ionicons } from '@expo/vector-icons'; -import { Image } from 'expo-image'; import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; diff --git a/components/coach/ChatComposer.tsx b/components/coach/ChatComposer.tsx index c696cb5..5ad3e93 100644 --- a/components/coach/ChatComposer.tsx +++ b/components/coach/ChatComposer.tsx @@ -1,8 +1,8 @@ +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import { Ionicons } from '@expo/vector-icons'; import { BlurView } from 'expo-blur'; -import { Image } from 'expo-image'; import React from 'react'; import { ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'; import QuickChips from './QuickChips'; diff --git a/components/coach/ChatMessage.tsx b/components/coach/ChatMessage.tsx index 68a88b6..8b93d1e 100644 --- a/components/coach/ChatMessage.tsx +++ b/components/coach/ChatMessage.tsx @@ -1,7 +1,7 @@ +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import { Ionicons } from '@expo/vector-icons'; -import { Image } from 'expo-image'; import React from 'react'; import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import Markdown from 'react-native-markdown-display'; diff --git a/components/health/MedicalRecordCard.tsx b/components/health/MedicalRecordCard.tsx index 0f47217..d912b46 100644 --- a/components/health/MedicalRecordCard.tsx +++ b/components/health/MedicalRecordCard.tsx @@ -1,8 +1,8 @@ -import { Colors, palette } from '@/constants/Colors'; +import { Image } from '@/components/ui/Image'; +import { palette } from '@/constants/Colors'; import { MedicalRecordItem } from '@/services/healthProfile'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import React from 'react'; import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; diff --git a/components/health/tabs/MedicalRecordsTab.tsx b/components/health/tabs/MedicalRecordsTab.tsx index 1a6623a..c0be983 100644 --- a/components/health/tabs/MedicalRecordsTab.tsx +++ b/components/health/tabs/MedicalRecordsTab.tsx @@ -1,4 +1,5 @@ import { MedicalRecordCard } from '@/components/health/MedicalRecordCard'; +import { Image } from '@/components/ui/Image'; import { palette } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useCosUpload } from '@/hooks/useCosUpload'; @@ -13,7 +14,6 @@ import { import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import * as DocumentPicker from 'expo-document-picker'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useState } from 'react'; diff --git a/components/medication/MedicationAddOptionsSheet.tsx b/components/medication/MedicationAddOptionsSheet.tsx index 3552220..da21eca 100644 --- a/components/medication/MedicationAddOptionsSheet.tsx +++ b/components/medication/MedicationAddOptionsSheet.tsx @@ -1,5 +1,5 @@ +import { Image } from '@/components/ui/Image'; import { Ionicons } from '@expo/vector-icons'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useRef, useState } from 'react'; import { Animated, Modal, Pressable, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; diff --git a/components/medication/MedicationCard.tsx b/components/medication/MedicationCard.tsx index fa37cd6..de65b41 100644 --- a/components/medication/MedicationCard.tsx +++ b/components/medication/MedicationCard.tsx @@ -1,4 +1,5 @@ import { ThemedText } from '@/components/ThemedText'; +import { Image } from '@/components/ui/Image'; import { useAppDispatch } from '@/hooks/redux'; import { useI18n } from '@/hooks/useI18n'; import { skipMedicationAction, takeMedicationAction } from '@/store/medicationsSlice'; @@ -6,7 +7,6 @@ import type { MedicationDisplayItem } from '@/types/medication'; import { Ionicons } from '@expo/vector-icons'; import dayjs, { Dayjs } from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import React, { useEffect, useState } from 'react'; import { Alert, StyleSheet, TouchableOpacity, View } from 'react-native'; diff --git a/components/medications/MedicationPhotoGuideModal.tsx b/components/medications/MedicationPhotoGuideModal.tsx index c60d0b7..8fda90f 100644 --- a/components/medications/MedicationPhotoGuideModal.tsx +++ b/components/medications/MedicationPhotoGuideModal.tsx @@ -1,7 +1,7 @@ +import { Image } from '@/components/ui/Image'; import { useI18n } from '@/hooks/useI18n'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import React from 'react'; import { diff --git a/components/model/food/CreateCustomFoodModal.tsx b/components/model/food/CreateCustomFoodModal.tsx index 2046867..6401d8c 100644 --- a/components/model/food/CreateCustomFoodModal.tsx +++ b/components/model/food/CreateCustomFoodModal.tsx @@ -1,9 +1,9 @@ +import { Image } from '@/components/ui/Image'; import { useAppSelector } from '@/hooks/redux'; import { useCosUpload } from '@/hooks/useCosUpload'; import { useI18n } from '@/hooks/useI18n'; import { Ionicons } from '@expo/vector-icons'; import { BlurView } from 'expo-blur'; -import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useMemo, useState } from 'react'; diff --git a/components/model/food/FoodDetailModal.tsx b/components/model/food/FoodDetailModal.tsx index 7db9792..21e7936 100644 --- a/components/model/food/FoodDetailModal.tsx +++ b/components/model/food/FoodDetailModal.tsx @@ -15,10 +15,10 @@ import { View, } from 'react-native'; // 导入统一的食物类型定义 +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { DEFAULT_IMAGE_FOOD } from '@/constants/Image'; import type { FoodItem } from '@/types/food'; -import { Image } from 'expo-image'; // 导入统一的食物类型定义 diff --git a/components/statistic/HealthDataCard.tsx b/components/statistic/HealthDataCard.tsx index 38119de..5815626 100644 --- a/components/statistic/HealthDataCard.tsx +++ b/components/statistic/HealthDataCard.tsx @@ -1,4 +1,4 @@ -import { Image } from 'expo-image'; +import { Image } from '@/components/ui/Image'; import React from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; diff --git a/components/statistic/SleepCard.tsx b/components/statistic/SleepCard.tsx index 5722628..f66b649 100644 --- a/components/statistic/SleepCard.tsx +++ b/components/statistic/SleepCard.tsx @@ -1,10 +1,10 @@ +import { Image } from '@/components/ui/Image'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { ChallengeType } from '@/services/challengesApi'; import { reportChallengeProgress, selectChallengeList } from '@/store/challengesSlice'; import { logger } from '@/utils/logger'; import { fetchCompleteSleepData, formatSleepTime } from '@/utils/sleepHealthKit'; import dayjs from 'dayjs'; -import { Image } from 'expo-image'; import { router } from 'expo-router'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/components/ui/Image.tsx b/components/ui/Image.tsx new file mode 100644 index 0000000..dd9ad48 --- /dev/null +++ b/components/ui/Image.tsx @@ -0,0 +1,44 @@ +import { API_ORIGIN } from '@/constants/Api'; +import Constants from 'expo-constants'; +import { Image as ExpoImage, ImageProps as ExpoImageProps } from 'expo-image'; +import React, { forwardRef, useMemo } from 'react'; + +// Construct User-Agent +const APP_NAME = Constants.expoConfig?.name || 'Out Live'; +const APP_VERSION = Constants.expoConfig?.version || '1.1.5'; +const USER_AGENT = `${APP_NAME}/${APP_VERSION} (iOS)`; + +export type ImageProps = ExpoImageProps; + +export const Image = forwardRef(({ source, ...props }, ref) => { + const finalSource = useMemo(() => { + if (!source) return source; + + const headers = { + 'User-Agent': USER_AGENT, + 'Referer': API_ORIGIN, + }; + + const addHeaders = (src: any) => { + if (typeof src === 'number' || src === null || src === undefined) return src; + if (typeof src === 'string') return { uri: src, headers }; + if (typeof src === 'object' && 'uri' in src) { + return { + ...src, + headers: { ...headers, ...(src.headers || {}) } + }; + } + return src; + }; + + if (Array.isArray(source)) { + return source.map(addHeaders); + } + + return addHeaders(source); + }, [source]); + + return ; +}); + +Image.displayName = 'Image'; \ No newline at end of file diff --git a/components/ui/MedicationAiSummaryInfoSheet.tsx b/components/ui/MedicationAiSummaryInfoSheet.tsx index 93b12ef..98b0fc5 100644 --- a/components/ui/MedicationAiSummaryInfoSheet.tsx +++ b/components/ui/MedicationAiSummaryInfoSheet.tsx @@ -1,7 +1,7 @@ +import { Image } from '@/components/ui/Image'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import * as Haptics from 'expo-haptics'; -import { Image } from 'expo-image'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { ActivityIndicator, diff --git a/components/weight/WeightHistoryCard.tsx b/components/weight/WeightHistoryCard.tsx index ee9cffc..56c2e71 100644 --- a/components/weight/WeightHistoryCard.tsx +++ b/components/weight/WeightHistoryCard.tsx @@ -1,3 +1,4 @@ +import { Image } from '@/components/ui/Image'; import { Colors } from '@/constants/Colors'; import { ROUTES } from '@/constants/Routes'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; @@ -7,7 +8,6 @@ import { fetchWeightHistory } from '@/store/userSlice'; import { BMI_CATEGORIES } from '@/utils/bmi'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; -import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useEffect, useState } from 'react';