Expo Router file-based navigation with glass tab bar, 3 tabs (Créations, Explorer, Profil), Phosphor icons, modal picker, and fade transitions.
243 lines
6.8 KiB
TypeScript
243 lines
6.8 KiB
TypeScript
import { ScrollView, StyleSheet, Switch, Text, View } from "react-native";
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
import Animated, { FadeInDown } from "react-native-reanimated";
|
|
import { GlassCard } from "@/components/glass";
|
|
import { useSettingsStore } from "@/stores/settings.store";
|
|
import { useWallpaperStore } from "@/stores/wallpaper.store";
|
|
import { colors, typography, spacing, layout } from "@/theme";
|
|
|
|
export default function ProfileScreen() {
|
|
const insets = useSafeAreaInsets();
|
|
const settings = useSettingsStore();
|
|
const wallpaperCount = useWallpaperStore((s) => s.savedWallpapers.length);
|
|
|
|
return (
|
|
<ScrollView
|
|
style={[styles.container, { paddingTop: insets.top + spacing.md }]}
|
|
contentContainerStyle={styles.content}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<Animated.View entering={FadeInDown.duration(500)}>
|
|
<GlassCard variant="subtle" radius="lg" padding={16}>
|
|
<Text style={typography.title}>Profil</Text>
|
|
<Text style={[typography.caption, { marginTop: 2 }]}>
|
|
Préférences & réglages
|
|
</Text>
|
|
</GlassCard>
|
|
</Animated.View>
|
|
|
|
{/* Stats */}
|
|
<Animated.View entering={FadeInDown.delay(100).duration(400)} style={styles.section}>
|
|
<GlassCard variant="medium" radius="lg">
|
|
<Text style={typography.label}>Statistiques</Text>
|
|
<View style={styles.statsRow}>
|
|
<View style={styles.stat}>
|
|
<Text style={typography.title}>{wallpaperCount}</Text>
|
|
<Text style={typography.caption}>Créations</Text>
|
|
</View>
|
|
<View style={styles.statDivider} />
|
|
<View style={styles.stat}>
|
|
<Text style={typography.title}>5</Text>
|
|
<Text style={typography.caption}>Animations</Text>
|
|
</View>
|
|
</View>
|
|
</GlassCard>
|
|
</Animated.View>
|
|
|
|
{/* Settings */}
|
|
<Animated.View entering={FadeInDown.delay(200).duration(400)} style={styles.section}>
|
|
<GlassCard variant="medium" radius="lg">
|
|
<Text style={[typography.label, { marginBottom: 16 }]}>Réglages</Text>
|
|
|
|
<SettingRow
|
|
label="Retour haptique"
|
|
description="Vibrations au toucher"
|
|
value={settings.hapticFeedback}
|
|
onToggle={settings.setHapticFeedback}
|
|
/>
|
|
|
|
<SettingRow
|
|
label="Depth map auto"
|
|
description="Générer automatiquement (V1.5)"
|
|
value={settings.autoDepthMap}
|
|
onToggle={settings.setAutoDepthMap}
|
|
/>
|
|
</GlassCard>
|
|
</Animated.View>
|
|
|
|
{/* Export Quality */}
|
|
<Animated.View entering={FadeInDown.delay(300).duration(400)} style={styles.section}>
|
|
<GlassCard variant="medium" radius="lg">
|
|
<Text style={[typography.label, { marginBottom: 16 }]}>
|
|
Qualité d'export
|
|
</Text>
|
|
{(["low", "medium", "high"] as const).map((q) => (
|
|
<QualityOption
|
|
key={q}
|
|
label={q === "low" ? "Économique" : q === "medium" ? "Standard" : "Maximum"}
|
|
description={
|
|
q === "low"
|
|
? "15 FPS, compression forte"
|
|
: q === "medium"
|
|
? "24 FPS, bon compromis"
|
|
: "30 FPS, qualité maximale"
|
|
}
|
|
selected={settings.exportQuality === q}
|
|
onPress={() => settings.setExportQuality(q)}
|
|
/>
|
|
))}
|
|
</GlassCard>
|
|
</Animated.View>
|
|
|
|
{/* About */}
|
|
<Animated.View entering={FadeInDown.delay(400).duration(400)} style={styles.section}>
|
|
<GlassCard variant="subtle" radius="lg">
|
|
<Text style={typography.label}>À propos</Text>
|
|
<Text style={[typography.bodySecondary, { marginTop: 8 }]}>
|
|
Lively v1.0.0
|
|
</Text>
|
|
<Text style={[typography.caption, { marginTop: 4 }]}>
|
|
Fonds d'écran animés depuis votre galerie
|
|
</Text>
|
|
</GlassCard>
|
|
</Animated.View>
|
|
|
|
<View style={{ height: layout.tabBarHeight + 40 }} />
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
function SettingRow({
|
|
label,
|
|
description,
|
|
value,
|
|
onToggle,
|
|
}: {
|
|
label: string;
|
|
description: string;
|
|
value: boolean;
|
|
onToggle: (v: boolean) => void;
|
|
}) {
|
|
return (
|
|
<View style={styles.settingRow}>
|
|
<View style={styles.settingText}>
|
|
<Text style={typography.body}>{label}</Text>
|
|
<Text style={typography.caption}>{description}</Text>
|
|
</View>
|
|
<Switch
|
|
value={value}
|
|
onValueChange={onToggle}
|
|
trackColor={{
|
|
false: "rgba(255,255,255,0.1)",
|
|
true: "rgba(255,255,255,0.3)",
|
|
}}
|
|
thumbColor={value ? colors.text.primary : colors.text.tertiary}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function QualityOption({
|
|
label,
|
|
description,
|
|
selected,
|
|
onPress,
|
|
}: {
|
|
label: string;
|
|
description: string;
|
|
selected: boolean;
|
|
onPress: () => void;
|
|
}) {
|
|
return (
|
|
<View
|
|
style={[styles.qualityRow, selected && styles.qualitySelected]}
|
|
onTouchEnd={onPress}
|
|
>
|
|
<View style={styles.settingText}>
|
|
<Text style={[typography.body, selected && { color: colors.text.primary }]}>
|
|
{label}
|
|
</Text>
|
|
<Text style={typography.caption}>{description}</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
styles.radio,
|
|
selected && styles.radioSelected,
|
|
]}
|
|
>
|
|
{selected && <View style={styles.radioDot} />}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: colors.background,
|
|
paddingHorizontal: layout.screenPadding,
|
|
},
|
|
content: {
|
|
paddingBottom: 40,
|
|
},
|
|
section: {
|
|
marginTop: spacing.md,
|
|
},
|
|
statsRow: {
|
|
flexDirection: "row",
|
|
marginTop: 16,
|
|
alignItems: "center",
|
|
},
|
|
stat: {
|
|
flex: 1,
|
|
alignItems: "center",
|
|
},
|
|
statDivider: {
|
|
width: 1,
|
|
height: 40,
|
|
backgroundColor: colors.glass.border,
|
|
},
|
|
settingRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.glass.highlight,
|
|
},
|
|
settingText: {
|
|
flex: 1,
|
|
marginRight: 16,
|
|
},
|
|
qualityRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
paddingVertical: 12,
|
|
paddingHorizontal: 12,
|
|
borderRadius: 12,
|
|
marginBottom: 4,
|
|
},
|
|
qualitySelected: {
|
|
backgroundColor: "rgba(255, 255, 255, 0.06)",
|
|
},
|
|
radio: {
|
|
width: 20,
|
|
height: 20,
|
|
borderRadius: 10,
|
|
borderWidth: 1.5,
|
|
borderColor: colors.text.tertiary,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
},
|
|
radioSelected: {
|
|
borderColor: colors.text.primary,
|
|
},
|
|
radioDot: {
|
|
width: 10,
|
|
height: 10,
|
|
borderRadius: 5,
|
|
backgroundColor: colors.text.primary,
|
|
},
|
|
});
|