@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.
@@ -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 [website, setWebsite] = useState('');
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 [tempWebsite, setTempWebsite] = useState('');
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?.full || user.name?.first || '';
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
- setWebsite(user.website || '');
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
- website,
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 oxyServices.updateProfile(updates);
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(currentValue);
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 'website':
162
- setTempWebsite(currentValue);
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 'website':
188
- setWebsite(tempWebsite);
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
- website: 'Website'
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
- website: { name: 'link', color: '#32D74B' }
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
- website: { label: 'Website', value: website, placeholder: 'Enter your website URL', icon: 'link', color: '#32D74B', multiline: false, keyboardType: 'url' as const }
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 'website': return tempWebsite;
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 'website': setTempWebsite(text); break;
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
- 'website',
518
- 'Website',
519
- website,
520
- 'Add your website',
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
  }));