- packages/ui-native theme.ts accent: amber->main, amberDim->dim, amberGlow->glow (값 보존)
- apps/mobile 20건 + apps/mobile-rn 18건 + ui-native 컴포넌트 9건 = 47 참조 치환
SKIP: Led/PhosphorText/Header color prop 'amber'(의미론적 컴포넌트 API, 별도 마이그레이션)
green/greenGlow 키(amber계 범위外)
정책: docs/REFACTOR_POLICY.md DP1, 이식 인사이트 P7
209 lines
6.4 KiB
TypeScript
209 lines
6.4 KiB
TypeScript
// apps/mobile/app/(tabs)/settings.tsx
|
|
// Settings tab — account, backend, preferences
|
|
// Design ref: docs/v3/designs/settings.html
|
|
|
|
import { View, ScrollView, StyleSheet, Alert, Switch } from 'react-native'
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
import {
|
|
MetalCard,
|
|
PhosphorText,
|
|
PhysicalButton,
|
|
Led,
|
|
AppStatusBar,
|
|
d3roNativePalette
|
|
} from '@d3ro/ui-native'
|
|
import { useI18n } from '@d3ro/i18n'
|
|
import { useAuth } from '../../lib/auth-context'
|
|
import { supabase } from '../../lib/supabase'
|
|
|
|
export default function SettingsScreen(): React.ReactElement {
|
|
const insets = useSafeAreaInsets()
|
|
const { t } = useI18n()
|
|
const { user } = useAuth()
|
|
|
|
async function handleLogout(): Promise<void> {
|
|
Alert.alert(t('mobile.set.logout'), t('mobile.set.logoutConfirm'), [
|
|
{ text: t('mobile.rec.cancel'), style: 'cancel' },
|
|
{
|
|
text: t('mobile.set.logout'),
|
|
style: 'destructive',
|
|
onPress: async () => {
|
|
await supabase.auth.signOut()
|
|
}
|
|
}
|
|
])
|
|
}
|
|
|
|
const initials = user?.email
|
|
? user.email.substring(0, 2).toUpperCase()
|
|
: 'US'
|
|
|
|
return (
|
|
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
|
<PhosphorText
|
|
variant="title"
|
|
color="primary"
|
|
style={[styles.title, { paddingTop: insets.top + 8 }]}
|
|
>
|
|
{t('mobile.set.title')}
|
|
</PhosphorText>
|
|
|
|
{/* Account Section */}
|
|
<PhosphorText variant="label" color="muted" style={styles.sectionLabel}>
|
|
{t('mobile.set.accountPlan')}
|
|
</PhosphorText>
|
|
<MetalCard style={styles.section}>
|
|
<View style={styles.profileRow}>
|
|
<View style={styles.avatar}>
|
|
<PhosphorText variant="body" color="amber">{initials}</PhosphorText>
|
|
</View>
|
|
<View style={styles.profileInfo}>
|
|
<PhosphorText variant="body" color="primary">
|
|
{user?.email?.split('@')[0] ?? 'User'}
|
|
</PhosphorText>
|
|
<PhosphorText variant="label" color="muted">
|
|
{user?.email ?? '\u2014'}
|
|
</PhosphorText>
|
|
</View>
|
|
</View>
|
|
<View style={[styles.row, styles.rowInset]}>
|
|
<PhosphorText variant="small" color="muted">{t('mobile.dash.tier')}</PhosphorText>
|
|
<View style={styles.rowRight}>
|
|
<Led color="amber" size={6} />
|
|
<PhosphorText variant="body" color="amber" style={styles.rowValue}>
|
|
{t('mobile.dash.free')}
|
|
</PhosphorText>
|
|
</View>
|
|
</View>
|
|
</MetalCard>
|
|
|
|
{/* Backend Section */}
|
|
<PhosphorText variant="label" color="muted" style={styles.sectionLabel}>
|
|
{t('mobile.set.backendConfig')}
|
|
</PhosphorText>
|
|
<MetalCard style={styles.section}>
|
|
<View style={styles.row}>
|
|
<View>
|
|
<PhosphorText variant="body" color="primary">{t('mobile.set.llmModel')}</PhosphorText>
|
|
<PhosphorText variant="label" color="muted">{t('mobile.set.llmDesc')}</PhosphorText>
|
|
</View>
|
|
<PhosphorText variant="body" color="amber">CLAUDE</PhosphorText>
|
|
</View>
|
|
<View style={[styles.row, styles.rowBorder]}>
|
|
<View>
|
|
<PhosphorText variant="body" color="primary">{t('mobile.set.cloudStt')}</PhosphorText>
|
|
<PhosphorText variant="label" color="muted">{t('mobile.set.cloudSttDesc')}</PhosphorText>
|
|
</View>
|
|
<Switch
|
|
value={true}
|
|
trackColor={{
|
|
false: d3roNativePalette.bg.inset,
|
|
true: d3roNativePalette.accent.main
|
|
}}
|
|
thumbColor="#ffffff"
|
|
/>
|
|
</View>
|
|
</MetalCard>
|
|
|
|
{/* Preferences Section */}
|
|
<PhosphorText variant="label" color="muted" style={styles.sectionLabel}>
|
|
{t('mobile.set.preferences')}
|
|
</PhosphorText>
|
|
<MetalCard style={styles.section}>
|
|
<View style={styles.row}>
|
|
<PhosphorText variant="body" color="primary">{t('mobile.set.language')}</PhosphorText>
|
|
<PhosphorText variant="body" color="muted">Korean</PhosphorText>
|
|
</View>
|
|
<View style={[styles.row, styles.rowBorder]}>
|
|
<PhosphorText variant="body" color="primary">{t('mobile.set.autoPolish')}</PhosphorText>
|
|
<Switch
|
|
value={true}
|
|
trackColor={{
|
|
false: d3roNativePalette.bg.inset,
|
|
true: d3roNativePalette.accent.main
|
|
}}
|
|
thumbColor="#ffffff"
|
|
/>
|
|
</View>
|
|
<View style={[styles.row, styles.rowBorder]}>
|
|
<PhosphorText variant="body" color="primary">{t('mobile.set.haptic')}</PhosphorText>
|
|
<Switch
|
|
value={true}
|
|
trackColor={{
|
|
false: d3roNativePalette.bg.inset,
|
|
true: d3roNativePalette.accent.main
|
|
}}
|
|
thumbColor="#ffffff"
|
|
/>
|
|
</View>
|
|
</MetalCard>
|
|
|
|
{/* Logout */}
|
|
<View style={styles.logoutWrap}>
|
|
<PhysicalButton
|
|
label={t('mobile.set.logout')}
|
|
variant="secondary"
|
|
onPress={() => void handleLogout()}
|
|
/>
|
|
</View>
|
|
|
|
{/* Status Footer */}
|
|
<AppStatusBar />
|
|
</ScrollView>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: d3roNativePalette.bg.app },
|
|
content: { paddingBottom: 120 },
|
|
title: {
|
|
paddingHorizontal: 20,
|
|
paddingBottom: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: d3roNativePalette.border.subtle
|
|
},
|
|
sectionLabel: {
|
|
paddingHorizontal: 24,
|
|
paddingTop: 20,
|
|
paddingBottom: 8,
|
|
letterSpacing: 3
|
|
},
|
|
section: {
|
|
marginHorizontal: 20,
|
|
padding: 0,
|
|
overflow: 'hidden'
|
|
},
|
|
profileRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
padding: 16,
|
|
gap: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: d3roNativePalette.border.subtle
|
|
},
|
|
avatar: {
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 20,
|
|
backgroundColor: d3roNativePalette.bg.inset,
|
|
borderWidth: 1,
|
|
borderColor: d3roNativePalette.border.default,
|
|
justifyContent: 'center',
|
|
alignItems: 'center'
|
|
},
|
|
profileInfo: { gap: 2 },
|
|
row: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
padding: 16
|
|
},
|
|
rowInset: { backgroundColor: d3roNativePalette.bg.inset },
|
|
rowBorder: {
|
|
borderTopWidth: 1,
|
|
borderTopColor: d3roNativePalette.border.subtle
|
|
},
|
|
rowRight: { flexDirection: 'row', alignItems: 'center' },
|
|
rowValue: { marginLeft: 8 },
|
|
logoutWrap: { margin: 20 }
|
|
})
|