import { FC, useCallback, useEffect, useRef, useState } from "react" import { ActivityIndicator, Animated, TextStyle, TouchableOpacity, View, ViewStyle, } from "react-native" import * as Application from "expo-application" import * as Clipboard from "expo-clipboard" import * as ImagePicker from "expo-image-picker" import { Avatar } from "@/components/Avatar" import { Button } from "@/components/Button" import { Icon, PressableIcon } from "@/components/Icon" import { ListItem } from "@/components/ListItem" import { Modal } from "@/components/Modal" import { Screen } from "@/components/Screen" import { Text } from "@/components/Text" import { TextField } from "@/components/TextField" import { useAuth } from "@/context/AuthContext" import { translate } from "@/i18n/translate" import { AppStackScreenProps } from "@/navigators/navigationTypes" import { uploadFile } from "@/services/api/uploadApi" import { useAppTheme } from "@/theme/context" import { $styles } from "@/theme/styles" import type { ThemedStyle } from "@/theme/types" import { s } from "@/utils/responsive" import { useHeader } from "@/utils/useHeader" export const ProfileScreen: FC> = function ProfileScreen({ navigation, }) { const { user, updateProfile, isLoading, error, clearError } = useAuth() const { themed, theme } = useAppTheme() const copyToastAnim = useRef(new Animated.Value(0)).current const copyToastTimeout = useRef | null>(null) useEffect(() => { return () => { if (copyToastTimeout.current) { clearTimeout(copyToastTimeout.current) } } }, []) const showCopyToast = useCallback(() => { if (copyToastTimeout.current) { clearTimeout(copyToastTimeout.current) } copyToastAnim.setValue(0) Animated.timing(copyToastAnim, { toValue: 1, duration: 200, useNativeDriver: true, }).start() copyToastTimeout.current = setTimeout(() => { Animated.timing(copyToastAnim, { toValue: 0, duration: 200, useNativeDriver: true, }).start() }, 1600) }, [copyToastAnim]) // Copy to clipboard const copyToClipboard = useCallback( async (text: string) => { const normalizedText = text.trim() if (!normalizedText) return await Clipboard.setStringAsync(normalizedText) showCopyToast() }, [showCopyToast], ) // Edit profile modal state const [isEditProfileVisible, setIsEditProfileVisible] = useState(false) const [nickname, setNickname] = useState(user?.profile?.nickname || "") const [uploadedAvatarUrl, setUploadedAvatarUrl] = useState(null) const [isUploading, setIsUploading] = useState(false) // Check if there are any changes to save const hasChanges = nickname.trim() !== (user?.profile?.nickname || "") || uploadedAvatarUrl !== null // Open edit profile modal const openEditProfile = useCallback(() => { setNickname(user?.profile?.nickname || "") setUploadedAvatarUrl(null) clearError() setIsEditProfileVisible(true) }, [user?.profile?.nickname, clearError]) // Pick image from library and upload immediately const pickImage = useCallback(async () => { const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync() if (!permissionResult.granted) { return } const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [1, 1], quality: 0.8, }) if (!result.canceled && result.assets && result.assets.length > 0) { const asset = result.assets[0] const uri = asset.uri const fileName = asset.fileName || `avatar_${Date.now()}.jpg` const mimeType = asset.mimeType || "image/jpeg" // Upload immediately after selection setIsUploading(true) try { const uploadResult = await uploadFile(uri, fileName, mimeType) if (uploadResult.kind === "ok") { setUploadedAvatarUrl(uploadResult.url) } else { console.error("Upload failed:", uploadResult.message) } } finally { setIsUploading(false) } } }, []) // Save profile (avatar already uploaded, just update profile) const handleSaveProfile = useCallback(async () => { if (!nickname.trim()) return const success = await updateProfile({ nickname: nickname.trim(), ...(uploadedAvatarUrl && { avatar: uploadedAvatarUrl }), }) if (success) { setIsEditProfileVisible(false) setUploadedAvatarUrl(null) } }, [nickname, uploadedAvatarUrl, updateProfile]) // Navigate to settings screen const navigateToSettings = useCallback(() => { navigation.navigate("Settings") }, [navigation]) // Navigate to security screen const navigateToSecurity = useCallback(() => { navigation.navigate("Security") }, [navigation]) // Navigate to about screen const navigateToAbout = useCallback(() => { navigation.navigate("About") }, [navigation]) // 使用 useHeader hook 设置导航栏 useHeader( { title: translate("profileScreen:title"), leftIcon: "back", onLeftPress: () => navigation.goBack(), rightIcon: "settings", onRightPress: navigateToSettings, }, [navigateToSettings], ) // Get display avatar - either newly uploaded or existing const displayAvatar = uploadedAvatarUrl || user?.profile?.avatar return ( {/* User Profile Header - Horizontal Layout */} {/* Left: Avatar */} {/* Middle: User Info */} {/* Top: UID */} UID: {user?.userId || "-"} {/* Middle: Name */} {user?.profile?.nickname || user?.username || translate("profileScreen:guest")} {/* Bottom: Account Status Badge */} {user?.status === "active" ? translate("profileScreen:regular") : translate("profileScreen:inactive")} {/* Right: Edit Arrow */} {/* Settings Section */} v{Application.nativeApplicationVersion || "1.0.0"} } onPress={navigateToAbout} /> {/* Edit Profile Modal */} setIsEditProfileVisible(false)} preset="bottom" titleTx="profileScreen:editProfile" FooterComponent={