Blame · Line-by-line history
SettingsScreen.tsx
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 9a8fbed | 1 | import React, { useCallback, useState } from 'react'; |
| 2 | import { | |
| 3 | Alert, | |
| 4 | ScrollView, | |
| 5 | StyleSheet, | |
| 6 | Switch, | |
| 7 | Text, | |
| 8 | TextInput, | |
| 9 | TouchableOpacity, | |
| 10 | View, | |
| 11 | } from 'react-native'; | |
| 12 | import { SafeAreaView } from 'react-native-safe-area-context'; | |
| 13 | import { useAuth } from '../hooks/useAuth'; | |
| 14 | import { useSettingsStore } from '../store/settingsStore'; | |
| 15 | import { colors } from '../theme/colors'; | |
| 16 | ||
| 17 | export function SettingsScreen(): React.ReactElement { | |
| 18 | const { user, logout } = useAuth(); | |
| 19 | const { host, setHost, notificationsEnabled, setNotificationsEnabled } = | |
| 20 | useSettingsStore(); | |
| 21 | ||
| 22 | const [hostInput, setHostInput] = useState(host); | |
| 23 | const [hostSaved, setHostSaved] = useState(false); | |
| 24 | ||
| 25 | const handleSaveHost = useCallback(() => { | |
| 26 | const trimmed = hostInput.trim().replace(/\/$/, ''); | |
| 27 | if (!trimmed) return; | |
| 28 | setHost(trimmed); | |
| 29 | setHostSaved(true); | |
| 30 | setTimeout(() => setHostSaved(false), 2000); | |
| 31 | }, [hostInput, setHost]); | |
| 32 | ||
| 33 | const handleLogout = useCallback(() => { | |
| 34 | Alert.alert( | |
| 35 | 'Sign Out', | |
| 36 | 'Are you sure you want to sign out?', | |
| 37 | [ | |
| 38 | { text: 'Cancel', style: 'cancel' }, | |
| 39 | { | |
| 40 | text: 'Sign Out', | |
| 41 | style: 'destructive', | |
| 42 | onPress: () => logout(), | |
| 43 | }, | |
| 44 | ], | |
| 45 | ); | |
| 46 | }, [logout]); | |
| 47 | ||
| 48 | return ( | |
| 49 | <SafeAreaView style={styles.safe} edges={['top', 'bottom']}> | |
| 50 | <ScrollView showsVerticalScrollIndicator={false}> | |
| 51 | {/* Header */} | |
| 52 | <View style={styles.pageHeader}> | |
| 53 | <Text style={styles.pageTitle}>Settings</Text> | |
| 54 | </View> | |
| 55 | ||
| 56 | {/* Account section */} | |
| 57 | <View style={styles.section}> | |
| 58 | <Text style={styles.sectionTitle}>Account</Text> | |
| 59 | <View style={styles.card}> | |
| 60 | <View style={styles.accountRow}> | |
| 61 | <View style={styles.avatar}> | |
| 62 | <Text style={styles.avatarText}> | |
| 63 | {user?.username.charAt(0).toUpperCase() ?? '?'} | |
| 64 | </Text> | |
| 65 | </View> | |
| 66 | <View style={styles.accountInfo}> | |
| 67 | <Text style={styles.username}>{user?.username ?? 'Unknown'}</Text> | |
| 68 | <Text style={styles.email}>{user?.email ?? ''}</Text> | |
| 69 | </View> | |
| 70 | </View> | |
| 71 | </View> | |
| 72 | </View> | |
| 73 | ||
| 74 | {/* Connection section */} | |
| 75 | <View style={styles.section}> | |
| 76 | <Text style={styles.sectionTitle}>Connection</Text> | |
| 77 | <View style={styles.card}> | |
| 78 | <View style={styles.formGroup}> | |
| 79 | <Text style={styles.label}>Host URL</Text> | |
| 80 | <View style={styles.inputRow}> | |
| 81 | <TextInput | |
| 82 | style={styles.input} | |
| 83 | value={hostInput} | |
| 84 | onChangeText={setHostInput} | |
| 85 | placeholder="https://gluecron.com" | |
| 86 | placeholderTextColor={colors.textMuted} | |
| 87 | autoCapitalize="none" | |
| 88 | autoCorrect={false} | |
| 89 | keyboardType="url" | |
| 90 | /> | |
| 91 | <TouchableOpacity | |
| 92 | style={[styles.saveButton, hostSaved && styles.saveButtonSaved]} | |
| 93 | onPress={handleSaveHost} | |
| 94 | activeOpacity={0.8} | |
| 95 | > | |
| 96 | <Text style={styles.saveButtonText}> | |
| 97 | {hostSaved ? '✓ Saved' : 'Save'} | |
| 98 | </Text> | |
| 99 | </TouchableOpacity> | |
| 100 | </View> | |
| 101 | <Text style={styles.hint}> | |
| 102 | Current: {host} | |
| 103 | </Text> | |
| 104 | </View> | |
| 105 | </View> | |
| 106 | </View> | |
| 107 | ||
| 108 | {/* Preferences section */} | |
| 109 | <View style={styles.section}> | |
| 110 | <Text style={styles.sectionTitle}>Preferences</Text> | |
| 111 | <View style={styles.card}> | |
| 112 | <View style={styles.toggleRow}> | |
| 113 | <View> | |
| 114 | <Text style={styles.toggleLabel}>Notifications</Text> | |
| 115 | <Text style={styles.toggleDesc}> | |
| 116 | Show notification badges | |
| 117 | </Text> | |
| 118 | </View> | |
| 119 | <Switch | |
| 120 | value={notificationsEnabled} | |
| 121 | onValueChange={setNotificationsEnabled} | |
| 122 | trackColor={{ false: colors.bgTertiary, true: colors.accent }} | |
| 123 | thumbColor={colors.text} | |
| 124 | /> | |
| 125 | </View> | |
| 126 | </View> | |
| 127 | </View> | |
| 128 | ||
| 129 | {/* About section */} | |
| 130 | <View style={styles.section}> | |
| 131 | <Text style={styles.sectionTitle}>About</Text> | |
| 132 | <View style={styles.card}> | |
| 133 | <View style={styles.aboutRow}> | |
| 134 | <Text style={styles.aboutLabel}>App Version</Text> | |
| 135 | <Text style={styles.aboutValue}>1.0.0</Text> | |
| 136 | </View> | |
| 137 | <View style={[styles.aboutRow, styles.aboutRowBorder]}> | |
| 138 | <Text style={styles.aboutLabel}>Platform</Text> | |
| 139 | <Text style={styles.aboutValue}>Gluecron Mobile</Text> | |
| 140 | </View> | |
| 141 | <View style={[styles.aboutRow, styles.aboutRowBorder]}> | |
| 142 | <Text style={styles.aboutLabel}>Connected to</Text> | |
| 143 | <Text style={styles.aboutValue} numberOfLines={1}> | |
| 144 | {host} | |
| 145 | </Text> | |
| 146 | </View> | |
| 147 | </View> | |
| 148 | </View> | |
| 149 | ||
| 150 | {/* Danger zone */} | |
| 151 | <View style={styles.section}> | |
| 152 | <TouchableOpacity | |
| 153 | style={styles.logoutButton} | |
| 154 | onPress={handleLogout} | |
| 155 | activeOpacity={0.8} | |
| 156 | > | |
| 157 | <Text style={styles.logoutText}>Sign Out</Text> | |
| 158 | </TouchableOpacity> | |
| 159 | </View> | |
| 160 | ||
| 161 | <View style={styles.footer}> | |
| 162 | <Text style={styles.footerText}> | |
| 163 | Gluecron — AI-native code intelligence platform | |
| 164 | </Text> | |
| 165 | <Text style={styles.footerText}>gluecron.com</Text> | |
| 166 | </View> | |
| 167 | </ScrollView> | |
| 168 | </SafeAreaView> | |
| 169 | ); | |
| 170 | } | |
| 171 | ||
| 172 | const styles = StyleSheet.create({ | |
| 173 | safe: { | |
| 174 | flex: 1, | |
| 175 | backgroundColor: colors.bg, | |
| 176 | }, | |
| 177 | pageHeader: { | |
| 178 | paddingHorizontal: 16, | |
| 179 | paddingVertical: 16, | |
| 180 | backgroundColor: colors.bgSecondary, | |
| 181 | borderBottomWidth: 1, | |
| 182 | borderBottomColor: colors.border, | |
| 183 | }, | |
| 184 | pageTitle: { | |
| 185 | fontSize: 20, | |
| 186 | fontWeight: '700', | |
| 187 | color: colors.text, | |
| 188 | }, | |
| 189 | section: { | |
| 190 | marginTop: 24, | |
| 191 | paddingHorizontal: 16, | |
| 192 | gap: 8, | |
| 193 | }, | |
| 194 | sectionTitle: { | |
| 195 | fontSize: 12, | |
| 196 | fontWeight: '600', | |
| 197 | color: colors.textMuted, | |
| 198 | textTransform: 'uppercase', | |
| 199 | letterSpacing: 0.5, | |
| 200 | marginBottom: 4, | |
| 201 | }, | |
| 202 | card: { | |
| 203 | backgroundColor: colors.bgSecondary, | |
| 204 | borderRadius: 10, | |
| 205 | borderWidth: 1, | |
| 206 | borderColor: colors.border, | |
| 207 | overflow: 'hidden', | |
| 208 | }, | |
| 209 | accountRow: { | |
| 210 | flexDirection: 'row', | |
| 211 | alignItems: 'center', | |
| 212 | padding: 14, | |
| 213 | gap: 12, | |
| 214 | }, | |
| 215 | avatar: { | |
| 216 | width: 48, | |
| 217 | height: 48, | |
| 218 | borderRadius: 24, | |
| 219 | backgroundColor: colors.accentBlue, | |
| 220 | alignItems: 'center', | |
| 221 | justifyContent: 'center', | |
| 222 | }, | |
| 223 | avatarText: { | |
| 224 | fontSize: 20, | |
| 225 | fontWeight: '700', | |
| 226 | color: colors.bg, | |
| 227 | }, | |
| 228 | accountInfo: { | |
| 229 | gap: 3, | |
| 230 | }, | |
| 231 | username: { | |
| 232 | fontSize: 16, | |
| 233 | fontWeight: '600', | |
| 234 | color: colors.text, | |
| 235 | }, | |
| 236 | email: { | |
| 237 | fontSize: 13, | |
| 238 | color: colors.textMuted, | |
| 239 | }, | |
| 240 | formGroup: { | |
| 241 | padding: 14, | |
| 242 | gap: 8, | |
| 243 | }, | |
| 244 | label: { | |
| 245 | fontSize: 12, | |
| 246 | fontWeight: '500', | |
| 247 | color: colors.textMuted, | |
| 248 | textTransform: 'uppercase', | |
| 249 | letterSpacing: 0.4, | |
| 250 | }, | |
| 251 | inputRow: { | |
| 252 | flexDirection: 'row', | |
| 253 | gap: 8, | |
| 254 | alignItems: 'center', | |
| 255 | }, | |
| 256 | input: { | |
| 257 | flex: 1, | |
| 258 | backgroundColor: colors.bg, | |
| 259 | borderWidth: 1, | |
| 260 | borderColor: colors.border, | |
| 261 | borderRadius: 8, | |
| 262 | paddingHorizontal: 12, | |
| 263 | paddingVertical: 10, | |
| 264 | fontSize: 14, | |
| 265 | color: colors.text, | |
| 266 | }, | |
| 267 | saveButton: { | |
| 268 | paddingHorizontal: 14, | |
| 269 | paddingVertical: 10, | |
| 270 | backgroundColor: colors.bgTertiary, | |
| 271 | borderRadius: 8, | |
| 272 | borderWidth: 1, | |
| 273 | borderColor: colors.border, | |
| 274 | }, | |
| 275 | saveButtonSaved: { | |
| 276 | borderColor: colors.accent, | |
| 277 | }, | |
| 278 | saveButtonText: { | |
| 279 | fontSize: 13, | |
| 280 | fontWeight: '600', | |
| 281 | color: colors.textLink, | |
| 282 | }, | |
| 283 | hint: { | |
| 284 | fontSize: 12, | |
| 285 | color: colors.textMuted, | |
| 286 | }, | |
| 287 | toggleRow: { | |
| 288 | flexDirection: 'row', | |
| 289 | alignItems: 'center', | |
| 290 | justifyContent: 'space-between', | |
| 291 | padding: 14, | |
| 292 | }, | |
| 293 | toggleLabel: { | |
| 294 | fontSize: 15, | |
| 295 | fontWeight: '500', | |
| 296 | color: colors.text, | |
| 297 | }, | |
| 298 | toggleDesc: { | |
| 299 | fontSize: 12, | |
| 300 | color: colors.textMuted, | |
| 301 | marginTop: 2, | |
| 302 | }, | |
| 303 | aboutRow: { | |
| 304 | flexDirection: 'row', | |
| 305 | alignItems: 'center', | |
| 306 | justifyContent: 'space-between', | |
| 307 | paddingVertical: 12, | |
| 308 | paddingHorizontal: 14, | |
| 309 | }, | |
| 310 | aboutRowBorder: { | |
| 311 | borderTopWidth: 1, | |
| 312 | borderTopColor: colors.border, | |
| 313 | }, | |
| 314 | aboutLabel: { | |
| 315 | fontSize: 14, | |
| 316 | color: colors.textMuted, | |
| 317 | }, | |
| 318 | aboutValue: { | |
| 319 | fontSize: 14, | |
| 320 | color: colors.text, | |
| 321 | maxWidth: '60%', | |
| 322 | textAlign: 'right', | |
| 323 | }, | |
| 324 | logoutButton: { | |
| 325 | backgroundColor: colors.bgSecondary, | |
| 326 | borderRadius: 10, | |
| 327 | borderWidth: 1, | |
| 328 | borderColor: colors.accentRed, | |
| 329 | paddingVertical: 14, | |
| 330 | alignItems: 'center', | |
| 331 | }, | |
| 332 | logoutText: { | |
| 333 | fontSize: 15, | |
| 334 | fontWeight: '600', | |
| 335 | color: colors.accentRed, | |
| 336 | }, | |
| 337 | footer: { | |
| 338 | padding: 32, | |
| 339 | alignItems: 'center', | |
| 340 | gap: 4, | |
| 341 | }, | |
| 342 | footerText: { | |
| 343 | fontSize: 12, | |
| 344 | color: colors.textMuted, | |
| 345 | textAlign: 'center', | |
| 346 | }, | |
| 347 | }); |