@oxyhq/services 5.6.0 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ui/context/OxyContext.js +95 -53
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +84 -25
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +36 -1
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +96 -54
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +84 -25
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +36 -1
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +6 -0
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +2 -0
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/models/interfaces.ts +7 -1
- package/src/ui/context/OxyContext.tsx +52 -55
- package/src/ui/screens/AccountSettingsScreen.tsx +68 -24
- package/src/ui/stores/authStore.ts +21 -0
|
@@ -19,6 +19,7 @@ import { Ionicons } from '@expo/vector-icons';
|
|
|
19
19
|
import { toast } from '../../lib/sonner';
|
|
20
20
|
import { fontFamilies } from '../styles/fonts';
|
|
21
21
|
import { confirmAction } from '../utils/confirmAction';
|
|
22
|
+
import { useAuthStore } from '../stores/authStore';
|
|
22
23
|
|
|
23
24
|
const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
24
25
|
onClose,
|
|
@@ -27,6 +28,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
27
28
|
navigate,
|
|
28
29
|
}) => {
|
|
29
30
|
const { user, oxyServices, isLoading: authLoading, isAuthenticated } = useOxy();
|
|
31
|
+
const updateUser = useAuthStore((state) => state.updateUser);
|
|
30
32
|
const [isLoading, setIsLoading] = useState(false);
|
|
31
33
|
const [isSaving, setIsSaving] = useState(false);
|
|
32
34
|
|
|
@@ -35,11 +37,12 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
35
37
|
|
|
36
38
|
// Form state
|
|
37
39
|
const [displayName, setDisplayName] = useState('');
|
|
40
|
+
const [lastName, setLastName] = useState('');
|
|
38
41
|
const [username, setUsername] = useState('');
|
|
39
42
|
const [email, setEmail] = useState('');
|
|
40
43
|
const [bio, setBio] = useState('');
|
|
41
44
|
const [location, setLocation] = useState('');
|
|
42
|
-
const [
|
|
45
|
+
const [links, setLinks] = useState<string[]>([]);
|
|
43
46
|
const [avatarUrl, setAvatarUrl] = useState('');
|
|
44
47
|
|
|
45
48
|
// Editing states
|
|
@@ -47,11 +50,12 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
47
50
|
|
|
48
51
|
// Temporary input states for inline editing
|
|
49
52
|
const [tempDisplayName, setTempDisplayName] = useState('');
|
|
53
|
+
const [tempLastName, setTempLastName] = useState('');
|
|
50
54
|
const [tempUsername, setTempUsername] = useState('');
|
|
51
55
|
const [tempEmail, setTempEmail] = useState('');
|
|
52
56
|
const [tempBio, setTempBio] = useState('');
|
|
53
57
|
const [tempLocation, setTempLocation] = useState('');
|
|
54
|
-
const [
|
|
58
|
+
const [tempLinks, setTempLinks] = useState<string[]>([]);
|
|
55
59
|
|
|
56
60
|
// Memoize theme-related calculations to prevent unnecessary recalculations
|
|
57
61
|
const themeStyles = useMemo(() => {
|
|
@@ -78,14 +82,19 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
78
82
|
if (user) {
|
|
79
83
|
const userDisplayName = typeof user.name === 'string'
|
|
80
84
|
? user.name
|
|
81
|
-
: user.name?.
|
|
82
|
-
|
|
85
|
+
: user.name?.first || user.name?.full || '';
|
|
86
|
+
const userLastName = typeof user.name === 'object' ? user.name?.last || '' : '';
|
|
83
87
|
setDisplayName(userDisplayName);
|
|
88
|
+
setLastName(userLastName);
|
|
84
89
|
setUsername(user.username || '');
|
|
85
90
|
setEmail(user.email || '');
|
|
86
91
|
setBio(user.bio || '');
|
|
87
92
|
setLocation(user.location || '');
|
|
88
|
-
|
|
93
|
+
setLinks(
|
|
94
|
+
Array.isArray(user.links)
|
|
95
|
+
? user.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean)
|
|
96
|
+
: user.website ? [user.website] : []
|
|
97
|
+
);
|
|
89
98
|
setAvatarUrl(user.avatar?.url || '');
|
|
90
99
|
}
|
|
91
100
|
}, [user]);
|
|
@@ -102,12 +111,12 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
102
111
|
email,
|
|
103
112
|
bio,
|
|
104
113
|
location,
|
|
105
|
-
|
|
114
|
+
links,
|
|
106
115
|
};
|
|
107
116
|
|
|
108
117
|
// Handle name field
|
|
109
|
-
if (displayName) {
|
|
110
|
-
updates.name = displayName;
|
|
118
|
+
if (displayName || lastName) {
|
|
119
|
+
updates.name = { first: displayName, last: lastName };
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
// Handle avatar
|
|
@@ -115,7 +124,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
115
124
|
updates.avatar = { url: avatarUrl };
|
|
116
125
|
}
|
|
117
126
|
|
|
118
|
-
await
|
|
127
|
+
await updateUser(updates, oxyServices);
|
|
119
128
|
toast.success('Profile updated successfully');
|
|
120
129
|
|
|
121
130
|
animateSaveButton(1); // Scale back to normal
|
|
@@ -144,7 +153,8 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
144
153
|
const startEditing = (type: string, currentValue: string) => {
|
|
145
154
|
switch (type) {
|
|
146
155
|
case 'displayName':
|
|
147
|
-
setTempDisplayName(
|
|
156
|
+
setTempDisplayName(displayName);
|
|
157
|
+
setTempLastName(lastName);
|
|
148
158
|
break;
|
|
149
159
|
case 'username':
|
|
150
160
|
setTempUsername(currentValue);
|
|
@@ -158,8 +168,8 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
158
168
|
case 'location':
|
|
159
169
|
setTempLocation(currentValue);
|
|
160
170
|
break;
|
|
161
|
-
case '
|
|
162
|
-
|
|
171
|
+
case 'links':
|
|
172
|
+
setTempLinks([...links]);
|
|
163
173
|
break;
|
|
164
174
|
}
|
|
165
175
|
setEditingField(type);
|
|
@@ -171,6 +181,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
171
181
|
switch (type) {
|
|
172
182
|
case 'displayName':
|
|
173
183
|
setDisplayName(tempDisplayName);
|
|
184
|
+
setLastName(tempLastName);
|
|
174
185
|
break;
|
|
175
186
|
case 'username':
|
|
176
187
|
setUsername(tempUsername);
|
|
@@ -184,8 +195,8 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
184
195
|
case 'location':
|
|
185
196
|
setLocation(tempLocation);
|
|
186
197
|
break;
|
|
187
|
-
case '
|
|
188
|
-
|
|
198
|
+
case 'links':
|
|
199
|
+
setLinks(tempLinks);
|
|
189
200
|
break;
|
|
190
201
|
}
|
|
191
202
|
|
|
@@ -207,7 +218,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
207
218
|
email: 'Email',
|
|
208
219
|
bio: 'Bio',
|
|
209
220
|
location: 'Location',
|
|
210
|
-
|
|
221
|
+
links: 'Links'
|
|
211
222
|
};
|
|
212
223
|
return labels[type as keyof typeof labels] || 'Field';
|
|
213
224
|
};
|
|
@@ -219,19 +230,52 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
219
230
|
email: { name: 'mail', color: '#FF9500' },
|
|
220
231
|
bio: { name: 'document-text', color: '#34C759' },
|
|
221
232
|
location: { name: 'location', color: '#FF3B30' },
|
|
222
|
-
|
|
233
|
+
links: { name: 'link', color: '#32D74B' }
|
|
223
234
|
};
|
|
224
235
|
return icons[type as keyof typeof icons] || { name: 'person', color: '#007AFF' };
|
|
225
236
|
};
|
|
226
237
|
|
|
227
238
|
const renderEditingField = (type: string) => {
|
|
239
|
+
if (type === 'displayName') {
|
|
240
|
+
return (
|
|
241
|
+
<View style={styles.editingFieldContainer}>
|
|
242
|
+
<View style={styles.editingFieldContent}>
|
|
243
|
+
<View style={[styles.newValueSection, { flexDirection: 'row', gap: 12 }]}>
|
|
244
|
+
<View style={{ flex: 1 }}>
|
|
245
|
+
<Text style={styles.editingFieldLabel}>First Name</Text>
|
|
246
|
+
<TextInput
|
|
247
|
+
style={styles.editingFieldInput}
|
|
248
|
+
value={tempDisplayName}
|
|
249
|
+
onChangeText={setTempDisplayName}
|
|
250
|
+
placeholder="Enter your first name"
|
|
251
|
+
placeholderTextColor={themeStyles.isDarkTheme ? '#aaa' : '#999'}
|
|
252
|
+
autoFocus
|
|
253
|
+
selectionColor={themeStyles.primaryColor}
|
|
254
|
+
/>
|
|
255
|
+
</View>
|
|
256
|
+
<View style={{ flex: 1 }}>
|
|
257
|
+
<Text style={styles.editingFieldLabel}>Last Name</Text>
|
|
258
|
+
<TextInput
|
|
259
|
+
style={styles.editingFieldInput}
|
|
260
|
+
value={tempLastName}
|
|
261
|
+
onChangeText={setTempLastName}
|
|
262
|
+
placeholder="Enter your last name"
|
|
263
|
+
placeholderTextColor={themeStyles.isDarkTheme ? '#aaa' : '#999'}
|
|
264
|
+
selectionColor={themeStyles.primaryColor}
|
|
265
|
+
/>
|
|
266
|
+
</View>
|
|
267
|
+
</View>
|
|
268
|
+
</View>
|
|
269
|
+
</View>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
228
272
|
const fieldConfig = {
|
|
229
273
|
displayName: { label: 'Display Name', value: displayName, placeholder: 'Enter your display name', icon: 'person', color: '#007AFF', multiline: false, keyboardType: 'default' as const },
|
|
230
274
|
username: { label: 'Username', value: username, placeholder: 'Choose a username', icon: 'at', color: '#5856D6', multiline: false, keyboardType: 'default' as const },
|
|
231
275
|
email: { label: 'Email', value: email, placeholder: 'Enter your email address', icon: 'mail', color: '#FF9500', multiline: false, keyboardType: 'email-address' as const },
|
|
232
276
|
bio: { label: 'Bio', value: bio, placeholder: 'Tell people about yourself...', icon: 'document-text', color: '#34C759', multiline: true, keyboardType: 'default' as const },
|
|
233
277
|
location: { label: 'Location', value: location, placeholder: 'Enter your location', icon: 'location', color: '#FF3B30', multiline: false, keyboardType: 'default' as const },
|
|
234
|
-
|
|
278
|
+
links: { label: 'Links', value: links.join(', '), placeholder: 'Enter your links (comma separated)', icon: 'link', color: '#32D74B', multiline: false, keyboardType: 'url' as const }
|
|
235
279
|
};
|
|
236
280
|
|
|
237
281
|
const config = fieldConfig[type as keyof typeof fieldConfig];
|
|
@@ -244,7 +288,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
244
288
|
case 'email': return tempEmail;
|
|
245
289
|
case 'bio': return tempBio;
|
|
246
290
|
case 'location': return tempLocation;
|
|
247
|
-
case '
|
|
291
|
+
case 'links': return tempLinks.join(', ');
|
|
248
292
|
default: return '';
|
|
249
293
|
}
|
|
250
294
|
})();
|
|
@@ -256,7 +300,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
256
300
|
case 'email': setTempEmail(text); break;
|
|
257
301
|
case 'bio': setTempBio(text); break;
|
|
258
302
|
case 'location': setTempLocation(text); break;
|
|
259
|
-
case '
|
|
303
|
+
case 'links': setTempLinks(text.split(',').map(s => s.trim()).filter(Boolean)); break;
|
|
260
304
|
}
|
|
261
305
|
};
|
|
262
306
|
|
|
@@ -446,7 +490,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
446
490
|
{renderField(
|
|
447
491
|
'displayName',
|
|
448
492
|
'Display Name',
|
|
449
|
-
displayName,
|
|
493
|
+
[displayName, lastName].filter(Boolean).join(' '), // Show full name
|
|
450
494
|
'Add your display name',
|
|
451
495
|
'person',
|
|
452
496
|
'#007AFF',
|
|
@@ -514,10 +558,10 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
514
558
|
)}
|
|
515
559
|
|
|
516
560
|
{renderField(
|
|
517
|
-
'
|
|
518
|
-
'
|
|
519
|
-
|
|
520
|
-
'Add your
|
|
561
|
+
'links',
|
|
562
|
+
'Links',
|
|
563
|
+
links.join(', '),
|
|
564
|
+
'Add your links',
|
|
521
565
|
'link',
|
|
522
566
|
'#32D74B',
|
|
523
567
|
false,
|
|
@@ -10,6 +10,8 @@ interface AuthState {
|
|
|
10
10
|
loginSuccess: (user: User) => void;
|
|
11
11
|
loginFailure: (error: string) => void;
|
|
12
12
|
logout: () => void;
|
|
13
|
+
fetchUser: (oxyServices: any) => Promise<void>;
|
|
14
|
+
updateUser: (updates: Partial<User>, oxyServices: any) => Promise<void>;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>) => void) => ({
|
|
@@ -21,4 +23,23 @@ export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>)
|
|
|
21
23
|
loginSuccess: (user: User) => set({ isLoading: false, isAuthenticated: true, user }),
|
|
22
24
|
loginFailure: (error: string) => set({ isLoading: false, error }),
|
|
23
25
|
logout: () => set({ user: null, isAuthenticated: false }),
|
|
26
|
+
fetchUser: async (oxyServices) => {
|
|
27
|
+
set({ isLoading: true, error: null });
|
|
28
|
+
try {
|
|
29
|
+
const user = await oxyServices.getCurrentUser();
|
|
30
|
+
set({ user, isLoading: false, isAuthenticated: true });
|
|
31
|
+
} catch (error: any) {
|
|
32
|
+
set({ error: error.message || 'Failed to fetch user', isLoading: false });
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
updateUser: async (updates, oxyServices) => {
|
|
36
|
+
set({ isLoading: true, error: null });
|
|
37
|
+
try {
|
|
38
|
+
await oxyServices.updateProfile(updates);
|
|
39
|
+
// Immediately fetch the latest user data after update
|
|
40
|
+
await useAuthStore.getState().fetchUser(oxyServices);
|
|
41
|
+
} catch (error: any) {
|
|
42
|
+
set({ error: error.message || 'Failed to update user', isLoading: false });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
24
45
|
}));
|