mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 08:41:57 +00:00
Improved Localization in Account Manage Screen
This commit is contained in:
parent
18c40ced29
commit
821a6b864f
3 changed files with 1973 additions and 1820 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1535,5 +1535,12 @@
|
||||||
"provider_logs": "Log Provider",
|
"provider_logs": "Log Provider",
|
||||||
"no_logs_captured": "Nessun log catturato."
|
"no_logs_captured": "Nessun log catturato."
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"account_manager":{
|
||||||
|
"sign_out":"Esci",
|
||||||
|
"sign_out_desc":"",
|
||||||
|
"user_id":"ID Utente",
|
||||||
|
"display_name":"Nickname",
|
||||||
|
"display_name_placeholder":"Aggiungi un nickname"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { View, Text, StyleSheet, TouchableOpacity, StatusBar, Platform, Animated, Easing, TextInput, ActivityIndicator } from 'react-native';
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
StatusBar,
|
||||||
|
Platform,
|
||||||
|
Animated,
|
||||||
|
Easing,
|
||||||
|
TextInput,
|
||||||
|
ActivityIndicator,
|
||||||
|
} from 'react-native';
|
||||||
import FastImage from '@d11/react-native-fast-image';
|
import FastImage from '@d11/react-native-fast-image';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
@ -7,331 +18,458 @@ import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { useAccount } from '../contexts/AccountContext';
|
import { useAccount } from '../contexts/AccountContext';
|
||||||
import { useTheme } from '../contexts/ThemeContext';
|
import { useTheme } from '../contexts/ThemeContext';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import CustomAlert from '../components/CustomAlert';
|
import CustomAlert from '../components/CustomAlert';
|
||||||
|
|
||||||
const AccountManageScreen: React.FC = () => {
|
const AccountManageScreen: React.FC = () => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { user, signOut, updateProfile } = useAccount();
|
const { user, signOut, updateProfile } = useAccount();
|
||||||
const { currentTheme } = useTheme();
|
const { currentTheme } = useTheme();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const headerOpacity = useRef(new Animated.Value(0)).current;
|
||||||
|
const headerTranslateY = useRef(new Animated.Value(8)).current;
|
||||||
|
const contentOpacity = useRef(new Animated.Value(0)).current;
|
||||||
|
const contentTranslateY = useRef(new Animated.Value(8)).current;
|
||||||
|
|
||||||
const headerOpacity = useRef(new Animated.Value(0)).current;
|
useEffect(() => {
|
||||||
const headerTranslateY = useRef(new Animated.Value(8)).current;
|
Animated.parallel([
|
||||||
const contentOpacity = useRef(new Animated.Value(0)).current;
|
Animated.timing(headerOpacity, {
|
||||||
const contentTranslateY = useRef(new Animated.Value(8)).current;
|
toValue: 1,
|
||||||
|
duration: 260,
|
||||||
|
easing: Easing.out(Easing.cubic),
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(headerTranslateY, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 260,
|
||||||
|
easing: Easing.out(Easing.cubic),
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(contentOpacity, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 320,
|
||||||
|
delay: 80,
|
||||||
|
easing: Easing.out(Easing.cubic),
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(contentTranslateY, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 320,
|
||||||
|
delay: 80,
|
||||||
|
easing: Easing.out(Easing.cubic),
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start();
|
||||||
|
}, [headerOpacity, headerTranslateY, contentOpacity, contentTranslateY]);
|
||||||
|
|
||||||
useEffect(() => {
|
const initial = useMemo(
|
||||||
Animated.parallel([
|
() => user?.email?.[0]?.toUpperCase() || 'U',
|
||||||
Animated.timing(headerOpacity, { toValue: 1, duration: 260, easing: Easing.out(Easing.cubic), useNativeDriver: true }),
|
[user?.email],
|
||||||
Animated.timing(headerTranslateY, { toValue: 0, duration: 260, easing: Easing.out(Easing.cubic), useNativeDriver: true }),
|
);
|
||||||
Animated.timing(contentOpacity, { toValue: 1, duration: 320, delay: 80, easing: Easing.out(Easing.cubic), useNativeDriver: true }),
|
const [displayName, setDisplayName] = useState(user?.displayName || '');
|
||||||
Animated.timing(contentTranslateY, { toValue: 0, duration: 320, delay: 80, easing: Easing.out(Easing.cubic), useNativeDriver: true }),
|
const [avatarUrl, setAvatarUrl] = useState(user?.avatarUrl || '');
|
||||||
]).start();
|
const [saving, setSaving] = useState(false);
|
||||||
}, [headerOpacity, headerTranslateY, contentOpacity, contentTranslateY]);
|
const [avatarError, setAvatarError] = useState(false);
|
||||||
|
const [alertVisible, setAlertVisible] = useState(false);
|
||||||
|
const [alertTitle, setAlertTitle] = useState('');
|
||||||
|
const [alertMessage, setAlertMessage] = useState('');
|
||||||
|
const [alertActions, setAlertActions] = useState<any[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
// Reset image error state when URL changes
|
||||||
|
setAvatarError(false);
|
||||||
|
}, [avatarUrl]);
|
||||||
|
|
||||||
const initial = useMemo(() => (user?.email?.[0]?.toUpperCase() || 'U'), [user?.email]);
|
const handleSave = async () => {
|
||||||
const [displayName, setDisplayName] = useState(user?.displayName || '');
|
if (saving) return;
|
||||||
const [avatarUrl, setAvatarUrl] = useState(user?.avatarUrl || '');
|
setSaving(true);
|
||||||
const [saving, setSaving] = useState(false);
|
const err = await updateProfile({
|
||||||
const [avatarError, setAvatarError] = useState(false);
|
displayName: displayName.trim() || undefined,
|
||||||
const [alertVisible, setAlertVisible] = useState(false);
|
avatarUrl: avatarUrl.trim() || undefined,
|
||||||
const [alertTitle, setAlertTitle] = useState('');
|
});
|
||||||
const [alertMessage, setAlertMessage] = useState('');
|
if (err) {
|
||||||
const [alertActions, setAlertActions] = useState<any[]>([]);
|
setAlertTitle(t('common.error'));
|
||||||
|
setAlertMessage(err);
|
||||||
|
setAlertActions([{ label: 'OK', onPress: () => {} }]);
|
||||||
|
setAlertVisible(true);
|
||||||
|
}
|
||||||
|
setSaving(false);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const handleSignOut = () => {
|
||||||
// Reset image error state when URL changes
|
setAlertTitle(t('account_manager.sign_out'));
|
||||||
setAvatarError(false);
|
setAlertMessage(t('account_manager.sign_out_desc'));
|
||||||
}, [avatarUrl]);
|
setAlertActions([
|
||||||
|
{ label: t('common.cancel'), onPress: () => {} },
|
||||||
|
{
|
||||||
|
label: t('account_manager.sign_out'),
|
||||||
|
onPress: async () => {
|
||||||
|
try {
|
||||||
|
await signOut();
|
||||||
|
// @ts-ignore
|
||||||
|
navigation.goBack();
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
style: { opacity: 1 },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setAlertVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
return (
|
||||||
if (saving) return;
|
<View
|
||||||
setSaving(true);
|
style={[
|
||||||
const err = await updateProfile({ displayName: displayName.trim() || undefined, avatarUrl: avatarUrl.trim() || undefined });
|
styles.container,
|
||||||
if (err) {
|
{ backgroundColor: currentTheme.colors.darkBackground },
|
||||||
setAlertTitle('Error');
|
]}
|
||||||
setAlertMessage(err);
|
>
|
||||||
setAlertActions([{ label: 'OK', onPress: () => {} }]);
|
<StatusBar
|
||||||
setAlertVisible(true);
|
translucent
|
||||||
}
|
barStyle="light-content"
|
||||||
setSaving(false);
|
backgroundColor="transparent"
|
||||||
};
|
/>
|
||||||
|
|
||||||
const handleSignOut = () => {
|
{/* Header */}
|
||||||
setAlertTitle('Sign out');
|
<Animated.View
|
||||||
setAlertMessage('Are you sure you want to sign out?');
|
style={[
|
||||||
setAlertActions([
|
styles.header,
|
||||||
{ label: 'Cancel', onPress: () => {} },
|
{
|
||||||
{
|
paddingTop:
|
||||||
label: 'Sign out',
|
(Platform.OS === 'android' ? StatusBar.currentHeight || 0 : insets.top) +
|
||||||
onPress: async () => {
|
12,
|
||||||
try {
|
opacity: headerOpacity,
|
||||||
await signOut();
|
transform: [{ translateY: headerTranslateY }],
|
||||||
// @ts-ignore
|
},
|
||||||
navigation.goBack();
|
]}
|
||||||
} catch (_) {}
|
>
|
||||||
},
|
<LinearGradient
|
||||||
style: { opacity: 1 },
|
colors={[currentTheme.colors.darkBackground, '#111318']}
|
||||||
},
|
style={StyleSheet.absoluteFill}
|
||||||
]);
|
/>
|
||||||
setAlertVisible(true);
|
<TouchableOpacity
|
||||||
};
|
onPress={() => navigation.goBack()}
|
||||||
|
style={styles.headerBack}
|
||||||
|
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name="arrow-back"
|
||||||
|
size={22}
|
||||||
|
color={currentTheme.colors.white}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={[styles.headerTitle, { color: currentTheme.colors.white }]}>
|
||||||
|
Account
|
||||||
|
</Text>
|
||||||
|
<View style={{ width: 22, height: 22 }} />
|
||||||
|
</Animated.View>
|
||||||
|
|
||||||
return (
|
{/* Content */}
|
||||||
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
<Animated.View
|
||||||
<StatusBar translucent barStyle="light-content" backgroundColor="transparent" />
|
style={[
|
||||||
|
styles.content,
|
||||||
|
{
|
||||||
|
opacity: contentOpacity,
|
||||||
|
transform: [{ translateY: contentTranslateY }],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{/* Profile Badge */}
|
||||||
|
<View style={styles.profileContainer}>
|
||||||
|
{avatarUrl && !avatarError ? (
|
||||||
|
<View style={[styles.avatar, { overflow: 'hidden' }]}>
|
||||||
|
<FastImage
|
||||||
|
source={{ uri: avatarUrl }}
|
||||||
|
style={styles.avatarImage}
|
||||||
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
|
onError={() => setAvatarError(true)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.avatar,
|
||||||
|
{ backgroundColor: currentTheme.colors.elevation2 },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text style={styles.avatarText}>{displayName?.[0] || initial}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* Header */}
|
{/* Account details card */}
|
||||||
<Animated.View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.header,
|
styles.card,
|
||||||
{
|
{
|
||||||
paddingTop: (Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top) + 12,
|
backgroundColor: currentTheme.colors.elevation1,
|
||||||
opacity: headerOpacity,
|
borderColor: currentTheme.colors.elevation2,
|
||||||
transform: [{ translateY: headerTranslateY }],
|
},
|
||||||
},
|
]}
|
||||||
]}
|
>
|
||||||
>
|
<View style={styles.itemRow}>
|
||||||
<LinearGradient
|
<View style={styles.itemLeft}>
|
||||||
colors={[currentTheme.colors.darkBackground, '#111318']}
|
<MaterialIcons
|
||||||
style={StyleSheet.absoluteFill}
|
name="badge"
|
||||||
/>
|
size={20}
|
||||||
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.headerBack} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
color={currentTheme.colors.primary}
|
||||||
<MaterialIcons name="arrow-back" size={22} color={currentTheme.colors.white} />
|
/>
|
||||||
</TouchableOpacity>
|
<Text
|
||||||
<Text style={[styles.headerTitle, { color: currentTheme.colors.white }]}>Account</Text>
|
style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}
|
||||||
<View style={{ width: 22, height: 22 }} />
|
>
|
||||||
</Animated.View>
|
{t('account_manager.display_name')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<TextInput
|
||||||
|
placeholder={t('account_manager.display_name_placeholder')}
|
||||||
|
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
||||||
|
style={[styles.input, { color: currentTheme.colors.white }]}
|
||||||
|
value={displayName}
|
||||||
|
onChangeText={setDisplayName}
|
||||||
|
numberOfLines={1}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* Content */}
|
<View style={styles.divider} />
|
||||||
<Animated.View style={[styles.content, { opacity: contentOpacity, transform: [{ translateY: contentTranslateY }] }]}>
|
|
||||||
{/* Profile Badge */}
|
|
||||||
<View style={styles.profileContainer}>
|
|
||||||
{avatarUrl && !avatarError ? (
|
|
||||||
<View style={[styles.avatar, { overflow: 'hidden' }]}>
|
|
||||||
<FastImage
|
|
||||||
source={{ uri: avatarUrl }}
|
|
||||||
style={styles.avatarImage}
|
|
||||||
resizeMode={FastImage.resizeMode.cover}
|
|
||||||
onError={() => setAvatarError(true)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<View style={[styles.avatar, { backgroundColor: currentTheme.colors.elevation2 }]}>
|
|
||||||
<Text style={styles.avatarText}>{(displayName?.[0] || initial)}</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Account details card */}
|
<View
|
||||||
<View style={[styles.card, { backgroundColor: currentTheme.colors.elevation1, borderColor: currentTheme.colors.elevation2 }]}>
|
style={[
|
||||||
<View style={styles.itemRow}>
|
styles.itemRow,
|
||||||
<View style={styles.itemLeft}>
|
Platform.OS === 'android' && styles.itemRowCompact,
|
||||||
<MaterialIcons name="badge" size={20} color={currentTheme.colors.primary} />
|
]}
|
||||||
<Text style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}>Display name</Text>
|
>
|
||||||
</View>
|
<View style={styles.itemLeft}>
|
||||||
<TextInput
|
<MaterialIcons
|
||||||
placeholder="Add a display name"
|
name="image"
|
||||||
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
size={20}
|
||||||
style={[styles.input, { color: currentTheme.colors.white }]}
|
color={currentTheme.colors.primary}
|
||||||
value={displayName}
|
/>
|
||||||
onChangeText={setDisplayName}
|
<Text
|
||||||
numberOfLines={1}
|
style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}
|
||||||
/>
|
>
|
||||||
</View>
|
Avatar URL
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<TextInput
|
||||||
|
placeholder="https://..."
|
||||||
|
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
||||||
|
style={[styles.input, { color: currentTheme.colors.white }]}
|
||||||
|
value={avatarUrl}
|
||||||
|
onChangeText={setAvatarUrl}
|
||||||
|
autoCapitalize="none"
|
||||||
|
numberOfLines={1}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View style={styles.divider} />
|
<View style={styles.divider} />
|
||||||
|
|
||||||
<View style={[styles.itemRow, Platform.OS === 'android' && styles.itemRowCompact]}>
|
<View style={styles.itemRow}>
|
||||||
<View style={styles.itemLeft}>
|
<View style={styles.itemLeft}>
|
||||||
<MaterialIcons name="image" size={20} color={currentTheme.colors.primary} />
|
<MaterialIcons
|
||||||
<Text style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}>Avatar URL</Text>
|
name="account-circle"
|
||||||
</View>
|
size={20}
|
||||||
<TextInput
|
color={currentTheme.colors.primary}
|
||||||
placeholder="https://..."
|
/>
|
||||||
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
<Text
|
||||||
style={[styles.input, { color: currentTheme.colors.white }]}
|
style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}
|
||||||
value={avatarUrl}
|
>
|
||||||
onChangeText={setAvatarUrl}
|
{t('common.email')}
|
||||||
autoCapitalize="none"
|
</Text>
|
||||||
numberOfLines={1}
|
</View>
|
||||||
/>
|
<Text
|
||||||
</View>
|
style={[styles.itemValue, { color: currentTheme.colors.mediumEmphasis }]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{user?.email || '—'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View style={styles.divider} />
|
<View style={styles.divider} />
|
||||||
|
|
||||||
<View style={styles.itemRow}>
|
<View style={styles.itemRow}>
|
||||||
<View style={styles.itemLeft}>
|
<View style={styles.itemLeft}>
|
||||||
<MaterialIcons name="account-circle" size={20} color={currentTheme.colors.primary} />
|
<MaterialIcons
|
||||||
<Text style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}>Email</Text>
|
name="fingerprint"
|
||||||
</View>
|
size={20}
|
||||||
<Text style={[styles.itemValue, { color: currentTheme.colors.mediumEmphasis }]} numberOfLines={1}>
|
color={currentTheme.colors.primary}
|
||||||
{user?.email || '—'}
|
/>
|
||||||
</Text>
|
<Text
|
||||||
</View>
|
style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}
|
||||||
|
>
|
||||||
|
{t('account_manager.user_id')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Text
|
||||||
|
style={[styles.itemValue, { color: currentTheme.colors.mediumEmphasis }]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{user?.id}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View style={styles.divider} />
|
{/* Save and Sign out */}
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.85}
|
||||||
|
style={[
|
||||||
|
styles.saveButton,
|
||||||
|
{
|
||||||
|
backgroundColor: currentTheme.colors.elevation2,
|
||||||
|
borderColor: currentTheme.colors.elevation2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onPress={handleSave}
|
||||||
|
disabled={saving}
|
||||||
|
>
|
||||||
|
{saving ? (
|
||||||
|
<ActivityIndicator color={currentTheme.colors.white} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<MaterialIcons
|
||||||
|
name="save-alt"
|
||||||
|
size={18}
|
||||||
|
color={currentTheme.colors.white}
|
||||||
|
style={{ marginRight: 8 }}
|
||||||
|
/>
|
||||||
|
<Text style={styles.saveText}>{t('common.save')}</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
<View style={styles.itemRow}>
|
<TouchableOpacity
|
||||||
<View style={styles.itemLeft}>
|
activeOpacity={0.85}
|
||||||
<MaterialIcons name="fingerprint" size={20} color={currentTheme.colors.primary} />
|
style={[
|
||||||
<Text style={[styles.itemTitle, { color: currentTheme.colors.highEmphasis }]}>User ID</Text>
|
styles.signOutButton,
|
||||||
</View>
|
{ backgroundColor: currentTheme.colors.primary },
|
||||||
<Text style={[styles.itemValue, { color: currentTheme.colors.mediumEmphasis }]} numberOfLines={1}>
|
]}
|
||||||
{user?.id}
|
onPress={handleSignOut}
|
||||||
</Text>
|
>
|
||||||
</View>
|
<MaterialIcons
|
||||||
</View>
|
name="logout"
|
||||||
|
size={18}
|
||||||
{/* Save and Sign out */}
|
color="#fff"
|
||||||
<TouchableOpacity
|
style={{ marginRight: 8 }}
|
||||||
activeOpacity={0.85}
|
/>
|
||||||
style={[styles.saveButton, { backgroundColor: currentTheme.colors.elevation2, borderColor: currentTheme.colors.elevation2 }]}
|
<Text style={styles.signOutText}>{t('account_manager.sign_out')}</Text>
|
||||||
onPress={handleSave}
|
</TouchableOpacity>
|
||||||
disabled={saving}
|
</Animated.View>
|
||||||
>
|
<CustomAlert
|
||||||
{saving ? (
|
visible={alertVisible}
|
||||||
<ActivityIndicator color={currentTheme.colors.white} />
|
title={alertTitle}
|
||||||
) : (
|
message={alertMessage}
|
||||||
<>
|
actions={alertActions}
|
||||||
<MaterialIcons name="save-alt" size={18} color={currentTheme.colors.white} style={{ marginRight: 8 }} />
|
onClose={() => setAlertVisible(false)}
|
||||||
<Text style={styles.saveText}>Save changes</Text>
|
/>
|
||||||
</>
|
</View>
|
||||||
)}
|
);
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
activeOpacity={0.85}
|
|
||||||
style={[
|
|
||||||
styles.signOutButton,
|
|
||||||
{ backgroundColor: currentTheme.colors.primary },
|
|
||||||
]}
|
|
||||||
onPress={handleSignOut}
|
|
||||||
>
|
|
||||||
<MaterialIcons name="logout" size={18} color="#fff" style={{ marginRight: 8 }} />
|
|
||||||
<Text style={styles.signOutText}>Sign out</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</Animated.View>
|
|
||||||
<CustomAlert
|
|
||||||
visible={alertVisible}
|
|
||||||
title={alertTitle}
|
|
||||||
message={alertMessage}
|
|
||||||
actions={alertActions}
|
|
||||||
onClose={() => setAlertVisible(false)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingBottom: 14,
|
paddingBottom: 14,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
headerBack: {
|
headerBack: {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
},
|
},
|
||||||
headerTitle: {
|
headerTitle: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingTop: 10,
|
paddingTop: 10,
|
||||||
},
|
},
|
||||||
profileContainer: {
|
profileContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
width: 72,
|
width: 72,
|
||||||
height: 72,
|
height: 72,
|
||||||
borderRadius: 36,
|
borderRadius: 36,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
avatarImage: {
|
avatarImage: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
avatarText: {
|
avatarText: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
borderWidth: StyleSheet.hairlineWidth,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
},
|
},
|
||||||
itemRow: {
|
itemRow: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
},
|
},
|
||||||
itemRowCompact: {
|
itemRowCompact: {
|
||||||
paddingVertical: 6,
|
paddingVertical: 6,
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
paddingVertical: 6,
|
paddingVertical: 6,
|
||||||
marginLeft: 12,
|
marginLeft: 12,
|
||||||
},
|
},
|
||||||
itemLeft: {
|
itemLeft: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 8,
|
gap: 8,
|
||||||
},
|
},
|
||||||
itemTitle: {
|
itemTitle: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
itemValue: {
|
itemValue: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
maxWidth: '65%',
|
maxWidth: '65%',
|
||||||
},
|
},
|
||||||
divider: {
|
divider: {
|
||||||
height: StyleSheet.hairlineWidth,
|
height: StyleSheet.hairlineWidth,
|
||||||
backgroundColor: 'rgba(255,255,255,0.08)',
|
backgroundColor: 'rgba(255,255,255,0.08)',
|
||||||
},
|
},
|
||||||
signOutButton: {
|
signOutButton: {
|
||||||
marginTop: 16,
|
marginTop: 16,
|
||||||
height: 48,
|
height: 48,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
},
|
},
|
||||||
signOutText: {
|
signOutText: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
saveButton: {
|
saveButton: {
|
||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
height: 46,
|
height: 46,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
borderWidth: StyleSheet.hairlineWidth,
|
||||||
},
|
},
|
||||||
saveText: {
|
saveText: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default AccountManageScreen;
|
export default AccountManageScreen;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue