Launch
This commit is contained in:
@@ -1,229 +1,229 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useApp } from '../../context/AppContext';
|
||||
import { useColors } from '../../constants/Colors';
|
||||
import { ThemeBackdrop } from '../../components/ThemeBackdrop';
|
||||
import { AppearanceMode, ColorPalette, Language } from '../../types';
|
||||
|
||||
const PALETTE_SWATCHES: Record<ColorPalette, string[]> = {
|
||||
forest: ['#5fa779', '#3d7f57'],
|
||||
ocean: ['#5a90be', '#3d6f99'],
|
||||
sunset: ['#c98965', '#a36442'],
|
||||
mono: ['#7b8796', '#5b6574'],
|
||||
};
|
||||
|
||||
const getPreferencesCopy = (language: Language) => {
|
||||
if (language === 'de') {
|
||||
return {
|
||||
title: 'Einstellungen',
|
||||
appearanceMode: 'Modell',
|
||||
colorPalette: 'Farbpalette',
|
||||
languageLabel: 'Sprache',
|
||||
themeSystem: 'System',
|
||||
themeLight: 'Hell',
|
||||
themeDark: 'Dunkel',
|
||||
paletteForest: 'Forest',
|
||||
paletteOcean: 'Ocean',
|
||||
paletteSunset: 'Sunset',
|
||||
paletteMono: 'Mono',
|
||||
};
|
||||
} else if (language === 'es') {
|
||||
return {
|
||||
title: 'Ajustes',
|
||||
appearanceMode: 'Modo',
|
||||
colorPalette: 'Paleta',
|
||||
languageLabel: 'Idioma',
|
||||
themeSystem: 'Sistema',
|
||||
themeLight: 'Claro',
|
||||
themeDark: 'Oscuro',
|
||||
paletteForest: 'Forest',
|
||||
paletteOcean: 'Ocean',
|
||||
paletteSunset: 'Sunset',
|
||||
paletteMono: 'Mono',
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: 'Preferences',
|
||||
appearanceMode: 'Appearance Mode',
|
||||
colorPalette: 'Color Palette',
|
||||
languageLabel: 'Language',
|
||||
themeSystem: 'System',
|
||||
themeLight: 'Light',
|
||||
themeDark: 'Dark',
|
||||
paletteForest: 'Forest',
|
||||
paletteOcean: 'Ocean',
|
||||
paletteSunset: 'Sunset',
|
||||
paletteMono: 'Mono',
|
||||
};
|
||||
};
|
||||
|
||||
export default function PreferencesScreen() {
|
||||
const router = useRouter();
|
||||
const { isDarkMode, appearanceMode, colorPalette, language, setAppearanceMode, setColorPalette, changeLanguage } = useApp();
|
||||
const colors = useColors(isDarkMode, colorPalette);
|
||||
const copy = getPreferencesCopy(language);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<ThemeBackdrop colors={colors} />
|
||||
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
|
||||
<Ionicons name="arrow-back" size={24} color={colors.text} />
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.title, { color: colors.text }]}>{copy.title}</Text>
|
||||
<View style={{ width: 40 }} />
|
||||
</View>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: colors.cardBg, borderColor: colors.border }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.text }]}>{copy.appearanceMode}</Text>
|
||||
<View style={styles.segmentedControl}>
|
||||
{(['system', 'light', 'dark'] as AppearanceMode[]).map((mode) => {
|
||||
const isActive = appearanceMode === mode;
|
||||
const label = mode === 'system' ? copy.themeSystem : mode === 'light' ? copy.themeLight : copy.themeDark;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={mode}
|
||||
style={[
|
||||
styles.segmentBtn,
|
||||
isActive && { backgroundColor: colors.primary },
|
||||
]}
|
||||
onPress={() => setAppearanceMode(mode)}
|
||||
>
|
||||
<Text style={[
|
||||
styles.segmentText,
|
||||
{ color: isActive ? '#fff' : colors.text }
|
||||
]}>
|
||||
{label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: colors.cardBg, borderColor: colors.border }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.text }]}>{copy.colorPalette}</Text>
|
||||
<View style={styles.swatchContainer}>
|
||||
{(['forest', 'ocean', 'sunset', 'mono'] as ColorPalette[]).map((p) => {
|
||||
const isActive = colorPalette === p;
|
||||
const swatch = PALETTE_SWATCHES[p] || ['#ccc', '#999'];
|
||||
const label = p === 'forest' ? copy.paletteForest : p === 'ocean' ? copy.paletteOcean : p === 'sunset' ? copy.paletteSunset : copy.paletteMono;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={p}
|
||||
style={[
|
||||
styles.swatchWrap,
|
||||
isActive && { borderColor: colors.primary, borderWidth: 2 }
|
||||
]}
|
||||
onPress={() => setColorPalette(p)}
|
||||
>
|
||||
<View style={[styles.swatch, { backgroundColor: swatch[0] }]} />
|
||||
<Text style={[styles.swatchLabel, { color: colors.text }]}>{label}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: colors.cardBg, borderColor: colors.border }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.text }]}>{copy.languageLabel}</Text>
|
||||
<View style={styles.langRow}>
|
||||
{(['en', 'de', 'es'] as Language[]).map(lang => {
|
||||
const isActive = language === lang;
|
||||
const label = lang === 'en' ? 'English' : lang === 'de' ? 'Deutsch' : 'Español';
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={lang}
|
||||
style={[
|
||||
styles.langBtn,
|
||||
isActive && { backgroundColor: colors.primary }
|
||||
]}
|
||||
onPress={() => changeLanguage(lang)}
|
||||
>
|
||||
<Text style={isActive ? { color: '#fff', fontWeight: '600' } : { color: colors.text }}>
|
||||
{label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
safeArea: { flex: 1 },
|
||||
header: { flexDirection: 'row', alignItems: 'center', padding: 16 },
|
||||
backButton: { width: 40, height: 40, justifyContent: 'center' },
|
||||
title: { flex: 1, fontSize: 20, fontWeight: '700', textAlign: 'center' },
|
||||
scrollContent: { padding: 16, gap: 16 },
|
||||
card: {
|
||||
padding: 16,
|
||||
borderRadius: 16,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 16,
|
||||
},
|
||||
segmentedControl: {
|
||||
flexDirection: 'row',
|
||||
backgroundColor: '#00000010',
|
||||
borderRadius: 12,
|
||||
padding: 4,
|
||||
},
|
||||
segmentBtn: {
|
||||
flex: 1,
|
||||
paddingVertical: 10,
|
||||
alignItems: 'center',
|
||||
borderRadius: 8,
|
||||
},
|
||||
segmentText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
swatchContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
swatchWrap: {
|
||||
alignItems: 'center',
|
||||
padding: 4,
|
||||
borderRadius: 12,
|
||||
borderWidth: 2,
|
||||
borderColor: 'transparent',
|
||||
gap: 6,
|
||||
},
|
||||
swatch: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 24,
|
||||
},
|
||||
swatchLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
},
|
||||
langRow: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 12,
|
||||
},
|
||||
langBtn: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#00000010',
|
||||
},
|
||||
});
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useApp } from '../../context/AppContext';
|
||||
import { useColors } from '../../constants/Colors';
|
||||
import { ThemeBackdrop } from '../../components/ThemeBackdrop';
|
||||
import { AppearanceMode, ColorPalette, Language } from '../../types';
|
||||
|
||||
const PALETTE_SWATCHES: Record<ColorPalette, string[]> = {
|
||||
forest: ['#5fa779', '#3d7f57'],
|
||||
ocean: ['#5a90be', '#3d6f99'],
|
||||
sunset: ['#c98965', '#a36442'],
|
||||
mono: ['#7b8796', '#5b6574'],
|
||||
};
|
||||
|
||||
const getPreferencesCopy = (language: Language) => {
|
||||
if (language === 'de') {
|
||||
return {
|
||||
title: 'Einstellungen',
|
||||
appearanceMode: 'Modell',
|
||||
colorPalette: 'Farbpalette',
|
||||
languageLabel: 'Sprache',
|
||||
themeSystem: 'System',
|
||||
themeLight: 'Hell',
|
||||
themeDark: 'Dunkel',
|
||||
paletteForest: 'Forest',
|
||||
paletteOcean: 'Ocean',
|
||||
paletteSunset: 'Sunset',
|
||||
paletteMono: 'Mono',
|
||||
};
|
||||
} else if (language === 'es') {
|
||||
return {
|
||||
title: 'Ajustes',
|
||||
appearanceMode: 'Modo',
|
||||
colorPalette: 'Paleta',
|
||||
languageLabel: 'Idioma',
|
||||
themeSystem: 'Sistema',
|
||||
themeLight: 'Claro',
|
||||
themeDark: 'Oscuro',
|
||||
paletteForest: 'Forest',
|
||||
paletteOcean: 'Ocean',
|
||||
paletteSunset: 'Sunset',
|
||||
paletteMono: 'Mono',
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: 'Preferences',
|
||||
appearanceMode: 'Appearance Mode',
|
||||
colorPalette: 'Color Palette',
|
||||
languageLabel: 'Language',
|
||||
themeSystem: 'System',
|
||||
themeLight: 'Light',
|
||||
themeDark: 'Dark',
|
||||
paletteForest: 'Forest',
|
||||
paletteOcean: 'Ocean',
|
||||
paletteSunset: 'Sunset',
|
||||
paletteMono: 'Mono',
|
||||
};
|
||||
};
|
||||
|
||||
export default function PreferencesScreen() {
|
||||
const router = useRouter();
|
||||
const { isDarkMode, appearanceMode, colorPalette, language, setAppearanceMode, setColorPalette, changeLanguage } = useApp();
|
||||
const colors = useColors(isDarkMode, colorPalette);
|
||||
const copy = getPreferencesCopy(language);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<ThemeBackdrop colors={colors} />
|
||||
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
|
||||
<Ionicons name="arrow-back" size={24} color={colors.text} />
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.title, { color: colors.text }]}>{copy.title}</Text>
|
||||
<View style={{ width: 40 }} />
|
||||
</View>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: colors.cardBg, borderColor: colors.border }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.text }]}>{copy.appearanceMode}</Text>
|
||||
<View style={styles.segmentedControl}>
|
||||
{(['system', 'light', 'dark'] as AppearanceMode[]).map((mode) => {
|
||||
const isActive = appearanceMode === mode;
|
||||
const label = mode === 'system' ? copy.themeSystem : mode === 'light' ? copy.themeLight : copy.themeDark;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={mode}
|
||||
style={[
|
||||
styles.segmentBtn,
|
||||
isActive && { backgroundColor: colors.primary },
|
||||
]}
|
||||
onPress={() => setAppearanceMode(mode)}
|
||||
>
|
||||
<Text style={[
|
||||
styles.segmentText,
|
||||
{ color: isActive ? '#fff' : colors.text }
|
||||
]}>
|
||||
{label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: colors.cardBg, borderColor: colors.border }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.text }]}>{copy.colorPalette}</Text>
|
||||
<View style={styles.swatchContainer}>
|
||||
{(['forest', 'ocean', 'sunset', 'mono'] as ColorPalette[]).map((p) => {
|
||||
const isActive = colorPalette === p;
|
||||
const swatch = PALETTE_SWATCHES[p] || ['#ccc', '#999'];
|
||||
const label = p === 'forest' ? copy.paletteForest : p === 'ocean' ? copy.paletteOcean : p === 'sunset' ? copy.paletteSunset : copy.paletteMono;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={p}
|
||||
style={[
|
||||
styles.swatchWrap,
|
||||
isActive && { borderColor: colors.primary, borderWidth: 2 }
|
||||
]}
|
||||
onPress={() => setColorPalette(p)}
|
||||
>
|
||||
<View style={[styles.swatch, { backgroundColor: swatch[0] }]} />
|
||||
<Text style={[styles.swatchLabel, { color: colors.text }]}>{label}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: colors.cardBg, borderColor: colors.border }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.text }]}>{copy.languageLabel}</Text>
|
||||
<View style={styles.langRow}>
|
||||
{(['en', 'de', 'es'] as Language[]).map(lang => {
|
||||
const isActive = language === lang;
|
||||
const label = lang === 'en' ? 'English' : lang === 'de' ? 'Deutsch' : 'Español';
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={lang}
|
||||
style={[
|
||||
styles.langBtn,
|
||||
isActive && { backgroundColor: colors.primary }
|
||||
]}
|
||||
onPress={() => changeLanguage(lang)}
|
||||
>
|
||||
<Text style={isActive ? { color: '#fff', fontWeight: '600' } : { color: colors.text }}>
|
||||
{label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
safeArea: { flex: 1 },
|
||||
header: { flexDirection: 'row', alignItems: 'center', padding: 16 },
|
||||
backButton: { width: 40, height: 40, justifyContent: 'center' },
|
||||
title: { flex: 1, fontSize: 20, fontWeight: '700', textAlign: 'center' },
|
||||
scrollContent: { padding: 16, gap: 16 },
|
||||
card: {
|
||||
padding: 16,
|
||||
borderRadius: 16,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 16,
|
||||
},
|
||||
segmentedControl: {
|
||||
flexDirection: 'row',
|
||||
backgroundColor: '#00000010',
|
||||
borderRadius: 12,
|
||||
padding: 4,
|
||||
},
|
||||
segmentBtn: {
|
||||
flex: 1,
|
||||
paddingVertical: 10,
|
||||
alignItems: 'center',
|
||||
borderRadius: 8,
|
||||
},
|
||||
segmentText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
swatchContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
swatchWrap: {
|
||||
alignItems: 'center',
|
||||
padding: 4,
|
||||
borderRadius: 12,
|
||||
borderWidth: 2,
|
||||
borderColor: 'transparent',
|
||||
gap: 6,
|
||||
},
|
||||
swatch: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 24,
|
||||
},
|
||||
swatchLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
},
|
||||
langRow: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 12,
|
||||
},
|
||||
langBtn: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#00000010',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user