diff options
Diffstat (limited to 'apps/mobile/app/(tabs)/settings.tsx')
| -rw-r--r-- | apps/mobile/app/(tabs)/settings.tsx | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/apps/mobile/app/(tabs)/settings.tsx b/apps/mobile/app/(tabs)/settings.tsx new file mode 100644 index 0000000..f90e86c --- /dev/null +++ b/apps/mobile/app/(tabs)/settings.tsx @@ -0,0 +1,248 @@ +import React, { useCallback } from 'react'; +import { + View, + Text, + StyleSheet, + ScrollView, + TouchableOpacity, + useColorScheme, + Alert, + Linking, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { Colors } from '../../constants/Colors'; +import { getEnvironment } from '../../lib/api'; +import { useAuthStore } from '../../stores/authStore'; +import { config } from '../../lib/config'; + +interface SettingsRowProps { + icon: keyof typeof Ionicons.glyphMap; + title: string; + value?: string; + onPress?: () => void; + showChevron?: boolean; +} + +function SettingsRow({ + icon, + title, + value, + onPress, + showChevron = true, +}: SettingsRowProps) { + const colorScheme = useColorScheme() ?? 'light'; + const colors = Colors[colorScheme]; + + const content = ( + <View style={[styles.row, { borderBottomColor: colors.border }]}> + <View style={[styles.iconContainer, { backgroundColor: colors.tint + '20' }]}> + <Ionicons name={icon} size={20} color={colors.tint} /> + </View> + <Text style={[styles.rowTitle, { color: colors.text }]}>{title}</Text> + {value && ( + <Text style={[styles.rowValue, { color: colors.secondaryText }]}> + {value} + </Text> + )} + {showChevron && onPress && ( + <Ionicons name="chevron-forward" size={20} color={colors.secondaryText} /> + )} + </View> + ); + + if (onPress) { + return ( + <TouchableOpacity onPress={onPress} activeOpacity={0.7}> + {content} + </TouchableOpacity> + ); + } + + return content; +} + +export default function SettingsScreen() { + const colorScheme = useColorScheme() ?? 'light'; + const colors = Colors[colorScheme]; + const signOut = useAuthStore((state) => state.signOut); + const isLoading = useAuthStore((state) => state.isLoading); + + const environment = getEnvironment(); + + const handleSignOut = useCallback(() => { + Alert.alert( + 'Sign Out', + 'Are you sure you want to sign out?', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Sign Out', + style: 'destructive', + onPress: () => signOut(), + }, + ] + ); + }, [signOut]); + + const handleOpenUrl = useCallback((url: string) => { + Linking.openURL(url); + }, []); + + return ( + <ScrollView + style={[styles.container, { backgroundColor: colors.background }]} + contentContainerStyle={styles.content} + > + {/* Account Section */} + <View style={styles.section}> + <Text style={[styles.sectionTitle, { color: colors.secondaryText }]}> + Account + </Text> + <View style={[styles.card, { backgroundColor: colors.card }]}> + <SettingsRow + icon="person-outline" + title="Profile" + onPress={() => {}} + /> + <SettingsRow + icon="notifications-outline" + title="Notifications" + onPress={() => {}} + /> + <SettingsRow + icon="key-outline" + title="API Key" + onPress={() => {}} + /> + </View> + </View> + + {/* App Section */} + <View style={styles.section}> + <Text style={[styles.sectionTitle, { color: colors.secondaryText }]}> + App + </Text> + <View style={[styles.card, { backgroundColor: colors.card }]}> + <SettingsRow + icon="color-palette-outline" + title="Appearance" + value={colorScheme === 'dark' ? 'Dark' : 'Light'} + showChevron={false} + /> + <SettingsRow + icon="server-outline" + title="Environment" + value={environment === 'local' ? 'Local' : 'Production'} + showChevron={false} + /> + </View> + </View> + + {/* Support Section */} + <View style={styles.section}> + <Text style={[styles.sectionTitle, { color: colors.secondaryText }]}> + Support + </Text> + <View style={[styles.card, { backgroundColor: colors.card }]}> + <SettingsRow + icon="help-circle-outline" + title="Help & Support" + onPress={() => handleOpenUrl(config.supportUrl)} + /> + <SettingsRow + icon="document-text-outline" + title="Terms of Service" + onPress={() => handleOpenUrl(config.termsOfServiceUrl)} + /> + <SettingsRow + icon="shield-checkmark-outline" + title="Privacy Policy" + onPress={() => handleOpenUrl(config.privacyPolicyUrl)} + /> + </View> + </View> + + {/* Sign Out */} + <View style={styles.section}> + <TouchableOpacity + style={[styles.signOutButton, { backgroundColor: colors.card }]} + onPress={handleSignOut} + activeOpacity={0.7} + disabled={isLoading} + > + <Ionicons name="log-out-outline" size={20} color="#ef4444" /> + <Text style={styles.signOutText}>Sign Out</Text> + </TouchableOpacity> + </View> + + {/* Version */} + <Text style={[styles.version, { color: colors.secondaryText }]}> + Makima Mobile v1.0.0 + </Text> + </ScrollView> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + content: { + padding: 16, + gap: 24, + }, + section: { + gap: 8, + }, + sectionTitle: { + fontSize: 13, + fontWeight: '600', + textTransform: 'uppercase', + letterSpacing: 0.5, + marginLeft: 16, + }, + card: { + borderRadius: 12, + overflow: 'hidden', + }, + row: { + flexDirection: 'row', + alignItems: 'center', + padding: 12, + paddingHorizontal: 16, + borderBottomWidth: StyleSheet.hairlineWidth, + gap: 12, + }, + iconContainer: { + width: 32, + height: 32, + borderRadius: 8, + alignItems: 'center', + justifyContent: 'center', + }, + rowTitle: { + flex: 1, + fontSize: 16, + }, + rowValue: { + fontSize: 14, + }, + signOutButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + padding: 16, + borderRadius: 12, + gap: 8, + }, + signOutText: { + fontSize: 16, + fontWeight: '600', + color: '#ef4444', + }, + version: { + textAlign: 'center', + fontSize: 12, + marginTop: 8, + }, +}); |
