diff --git a/app.json b/app.json index d2ad426..28df652 100644 --- a/app.json +++ b/app.json @@ -10,7 +10,7 @@ "newArchEnabled": true, "ios": { "supportsTablet": true, - "bundleIdentifier": "com.wellnuo.BluetoothScanner", + "bundleIdentifier": "com.serter2069.wellnuo.test", "appleTeamId": "UHLZD54ULZ", "deploymentTarget": "16.0", "infoPlist": { diff --git a/app/(tabs)/profile/index.tsx b/app/(tabs)/profile/index.tsx index 12e4099..2922f9a 100644 --- a/app/(tabs)/profile/index.tsx +++ b/app/(tabs)/profile/index.tsx @@ -7,7 +7,9 @@ import { Alert, Image, ScrollView, + ActivityIndicator, } from 'react-native'; +import { api } from '@/services/api'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import * as SecureStore from 'expo-secure-store'; @@ -61,6 +63,7 @@ export default function ProfileScreen() { // Avatar const [avatarUri, setAvatarUri] = useState(null); + const [isUploadingAvatar, setIsUploadingAvatar] = useState(false); useEffect(() => { loadAvatar(); @@ -68,6 +71,13 @@ export default function ProfileScreen() { const loadAvatar = async () => { try { + // First try to get cloud URL from user profile + if (user?.avatarUrl) { + setAvatarUri(user.avatarUrl); + await SecureStore.setItemAsync('userAvatar', user.avatarUrl); + return; + } + // Fallback to cached local avatar const uri = await SecureStore.getItemAsync('userAvatar'); if (uri) { setAvatarUri(uri); @@ -98,9 +108,30 @@ export default function ProfileScreen() { // Optimize image: resize to 400x400 and compress const optimizedUri = await optimizeAvatarImage(originalUri); + // Show optimistic update immediately setAvatarUri(optimizedUri); - await SecureStore.setItemAsync('userAvatar', optimizedUri); - toast.success('Avatar updated'); + + // Upload to cloud storage + setIsUploadingAvatar(true); + try { + const response = await api.updateProfileAvatar(optimizedUri); + if (response.ok && response.data?.avatarUrl) { + // Use cloud URL instead of local + setAvatarUri(response.data.avatarUrl); + await SecureStore.setItemAsync('userAvatar', response.data.avatarUrl); + toast.success('Avatar updated'); + } else { + // Fallback to local storage if cloud upload fails + await SecureStore.setItemAsync('userAvatar', optimizedUri); + toast.error(response.error?.message || 'Cloud upload failed, saved locally'); + } + } catch (error) { + console.error('Avatar upload error:', error); + await SecureStore.setItemAsync('userAvatar', optimizedUri); + toast.error('Upload failed, saved locally'); + } finally { + setIsUploadingAvatar(false); + } } }; @@ -180,14 +211,24 @@ export default function ProfileScreen() { {avatarUri ? ( ) : ( {userInitial} )} + {isUploadingAvatar && ( + + + + )} - + @@ -339,6 +380,13 @@ const styles = StyleSheet.create({ fontWeight: FontWeights.bold, color: AppColors.white, }, + avatarLoadingOverlay: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + borderRadius: AvatarSizes.xl / 2, + justifyContent: 'center', + alignItems: 'center', + }, avatarEditBadge: { position: 'absolute', bottom: 0,