Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
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.

SettingsScreen.tsxBlame248 lines · 1 contributor
c53cf00Claude1import React, { useContext, useState } from 'react';
2import {
3 View,
4 Text,
5 StyleSheet,
6 TouchableOpacity,
7 ScrollView,
8 Alert,
9 TextInput,
10} from 'react-native';
11import { SafeAreaView } from 'react-native-safe-area-context';
12import { colors } from '../theme/colors';
13import { fontSizes, fontWeights } from '../theme/typography';
9d7a803Claude14import { AuthContext } from '../navigation/AuthContext';
c53cf00Claude15import { getBaseUrl, setBaseUrl } from '../api/client';
16import { saveHost } from '../store/auth';
17
18const APP_VERSION = '1.0.0';
19
20export function SettingsScreen() {
21 const { user, logout } = useContext(AuthContext);
22 const [host, setHostState] = useState(getBaseUrl());
23 const [editingHost, setEditingHost] = useState(false);
24
25 async function handleLogout() {
26 Alert.alert(
27 'Sign out',
28 'Are you sure you want to sign out?',
29 [
30 { text: 'Cancel', style: 'cancel' },
31 {
32 text: 'Sign out',
33 style: 'destructive',
34 onPress: async () => {
35 await logout();
36 },
37 },
38 ]
39 );
40 }
41
42 async function handleSaveHost() {
43 const trimmed = host.trim().replace(/\/$/, '');
44 if (!trimmed) return;
45 setBaseUrl(trimmed);
46 await saveHost(trimmed);
47 setEditingHost(false);
48 }
49
50 return (
51 <SafeAreaView style={styles.safe} edges={['top']}>
52 <ScrollView contentContainerStyle={styles.content}>
53 {/* Title */}
54 <Text style={styles.pageTitle}>Settings</Text>
55
56 {/* User profile section */}
57 <View style={styles.section}>
58 <Text style={styles.sectionLabel}>Account</Text>
59 <View style={styles.card}>
60 <View style={styles.avatarRow}>
61 <View style={styles.avatar}>
62 <Text style={styles.avatarText}>
63 {(user?.displayName || user?.username || '?')[0].toUpperCase()}
64 </Text>
65 </View>
66 <View style={styles.userInfo}>
67 <Text style={styles.displayName}>{user?.displayName || user?.username}</Text>
68 <Text style={styles.username}>@{user?.username}</Text>
69 {user?.email && <Text style={styles.email}>{user.email}</Text>}
70 </View>
71 </View>
72 {user?.bio ? (
73 <Text style={styles.bio}>{user.bio}</Text>
74 ) : null}
75 </View>
76 </View>
77
78 {/* Host section */}
79 <View style={styles.section}>
80 <Text style={styles.sectionLabel}>Gluecron Host</Text>
81 <View style={styles.card}>
82 <View style={styles.hostRow}>
83 <Text style={styles.hostLabel}>Server URL</Text>
84 {!editingHost ? (
85 <View style={styles.hostValueRow}>
86 <Text style={styles.hostValue} numberOfLines={1}>{host}</Text>
87 <TouchableOpacity
88 onPress={() => setEditingHost(true)}
89 style={styles.editBtn}
90 activeOpacity={0.75}
91 >
92 <Text style={styles.editBtnText}>Edit</Text>
93 </TouchableOpacity>
94 </View>
95 ) : (
96 <View style={styles.hostEditRow}>
97 <TextInput
98 style={styles.hostInput}
99 value={host}
100 onChangeText={setHostState}
101 autoCapitalize="none"
102 autoCorrect={false}
103 keyboardType="url"
104 placeholder="https://gluecron.com"
105 placeholderTextColor={colors.textMuted}
106 returnKeyType="done"
107 onSubmitEditing={handleSaveHost}
108 />
109 <TouchableOpacity onPress={handleSaveHost} style={styles.saveBtn} activeOpacity={0.75}>
110 <Text style={styles.saveBtnText}>Save</Text>
111 </TouchableOpacity>
112 <TouchableOpacity
113 onPress={() => { setHostState(getBaseUrl()); setEditingHost(false); }}
114 style={styles.cancelBtn}
115 activeOpacity={0.75}
116 >
117 <Text style={styles.cancelBtnText}>Cancel</Text>
118 </TouchableOpacity>
119 </View>
120 )}
121 </View>
122 <Text style={styles.hostHint}>
123 Change this if you run a self-hosted Gluecron instance.
124 </Text>
125 </View>
126 </View>
127
128 {/* App info */}
129 <View style={styles.section}>
130 <Text style={styles.sectionLabel}>About</Text>
131 <View style={styles.card}>
132 <View style={styles.infoRow}>
133 <Text style={styles.infoLabel}>App version</Text>
134 <Text style={styles.infoValue}>{APP_VERSION}</Text>
135 </View>
136 <View style={styles.divider} />
137 <View style={styles.infoRow}>
138 <Text style={styles.infoLabel}>Platform</Text>
139 <Text style={styles.infoValue}>Gluecron Mobile</Text>
140 </View>
141 </View>
142 </View>
143
144 {/* Logout */}
145 <View style={styles.section}>
146 <TouchableOpacity style={styles.logoutBtn} onPress={handleLogout} activeOpacity={0.8}>
147 <Text style={styles.logoutText}>Sign out</Text>
148 </TouchableOpacity>
149 </View>
150 </ScrollView>
151 </SafeAreaView>
152 );
153}
154
155const styles = StyleSheet.create({
156 safe: { flex: 1, backgroundColor: colors.bg },
157 content: { padding: 16, paddingBottom: 40 },
158 pageTitle: {
159 color: colors.text,
160 fontSize: fontSizes.xl,
161 fontWeight: fontWeights.bold,
162 marginBottom: 20,
163 },
164 section: { marginBottom: 24 },
165 sectionLabel: {
166 color: colors.textMuted,
167 fontSize: fontSizes.xs,
168 fontWeight: fontWeights.semibold,
169 textTransform: 'uppercase',
170 letterSpacing: 0.8,
171 marginBottom: 8,
172 },
173 card: {
174 backgroundColor: colors.bgSurface,
175 borderRadius: 12,
176 padding: 16,
177 borderWidth: 1,
178 borderColor: colors.border,
179 },
180 avatarRow: {
181 flexDirection: 'row',
182 alignItems: 'center',
183 gap: 14,
184 marginBottom: 8,
185 },
186 avatar: {
187 width: 52,
188 height: 52,
189 borderRadius: 26,
190 backgroundColor: colors.accentDim,
191 alignItems: 'center',
192 justifyContent: 'center',
193 borderWidth: 2,
194 borderColor: colors.accent,
195 },
196 avatarText: {
197 color: colors.accent,
198 fontSize: fontSizes.xl,
199 fontWeight: fontWeights.bold,
200 },
201 userInfo: { flex: 1 },
202 displayName: {
203 color: colors.text,
204 fontSize: fontSizes.md,
205 fontWeight: fontWeights.semibold,
206 marginBottom: 2,
207 },
208 username: { color: colors.textMuted, fontSize: fontSizes.sm, marginBottom: 2 },
209 email: { color: colors.textMuted, fontSize: fontSizes.xs },
210 bio: { color: colors.textMuted, fontSize: fontSizes.sm, lineHeight: 18, marginTop: 4 },
211 hostRow: { marginBottom: 8 },
212 hostLabel: { color: colors.textMuted, fontSize: fontSizes.xs, fontWeight: fontWeights.medium, marginBottom: 6, textTransform: 'uppercase' },
213 hostValueRow: { flexDirection: 'row', alignItems: 'center', gap: 10 },
214 hostValue: { color: colors.text, fontSize: fontSizes.sm, flex: 1 },
215 editBtn: { paddingHorizontal: 10, paddingVertical: 5, backgroundColor: colors.bgSecondary, borderRadius: 6 },
216 editBtnText: { color: colors.accent, fontSize: fontSizes.xs, fontWeight: fontWeights.medium },
217 hostEditRow: { flexDirection: 'row', alignItems: 'center', gap: 8, flexWrap: 'wrap' },
218 hostInput: {
219 flex: 1,
220 backgroundColor: colors.bgSecondary,
221 borderWidth: 1,
222 borderColor: colors.border,
223 borderRadius: 6,
224 paddingHorizontal: 10,
225 paddingVertical: 8,
226 color: colors.text,
227 fontSize: fontSizes.sm,
228 minHeight: 36,
229 },
230 saveBtn: { paddingHorizontal: 12, paddingVertical: 8, backgroundColor: colors.accent, borderRadius: 6 },
231 saveBtnText: { color: colors.white, fontSize: fontSizes.sm, fontWeight: fontWeights.medium },
232 cancelBtn: { paddingHorizontal: 10, paddingVertical: 8 },
233 cancelBtnText: { color: colors.textMuted, fontSize: fontSizes.sm },
234 hostHint: { color: colors.textMuted, fontSize: fontSizes.xs, lineHeight: 16 },
235 infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 4 },
236 infoLabel: { color: colors.textMuted, fontSize: fontSizes.sm },
237 infoValue: { color: colors.text, fontSize: fontSizes.sm, fontWeight: fontWeights.medium },
238 divider: { height: 1, backgroundColor: colors.border, marginVertical: 8 },
239 logoutBtn: {
240 backgroundColor: colors.red + '22',
241 borderWidth: 1,
242 borderColor: colors.red + '55',
243 borderRadius: 10,
244 paddingVertical: 14,
245 alignItems: 'center',
246 },
247 logoutText: { color: colors.red, fontSize: fontSizes.base, fontWeight: fontWeights.semibold },
248});