@oxyhq/services 5.13.30 → 5.13.31
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 +19 -1
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +15 -8
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +150 -13
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +19 -1
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +15 -8
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +150 -13
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts +2 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +1 -0
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/context/OxyContext.tsx +23 -1
- package/src/ui/hooks/useSessionSocket.ts +16 -9
- package/src/ui/screens/AccountSettingsScreen.tsx +158 -18
|
@@ -33,12 +33,13 @@ const locationSearchCache = new TTLCache<any[]>(60 * 60 * 1000); // 1 hour cache
|
|
|
33
33
|
registerCacheForCleanup(linkMetadataCache);
|
|
34
34
|
registerCacheForCleanup(locationSearchCache);
|
|
35
35
|
|
|
36
|
-
const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string }> = ({
|
|
36
|
+
const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string; initialSection?: string }> = ({
|
|
37
37
|
onClose,
|
|
38
38
|
theme,
|
|
39
39
|
goBack,
|
|
40
40
|
navigate,
|
|
41
41
|
initialField,
|
|
42
|
+
initialSection,
|
|
42
43
|
}) => {
|
|
43
44
|
const { user: userFromContext, oxyServices, isLoading: authLoading, isAuthenticated, showBottomSheet, activeSessionId } = useOxy();
|
|
44
45
|
const { t } = useI18n();
|
|
@@ -52,6 +53,20 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
52
53
|
const scrollViewRef = useRef<ScrollView>(null);
|
|
53
54
|
const avatarSectionRef = useRef<View>(null);
|
|
54
55
|
const [avatarSectionY, setAvatarSectionY] = useState<number | null>(null);
|
|
56
|
+
|
|
57
|
+
// Section refs for navigation
|
|
58
|
+
const profilePictureSectionRef = useRef<View>(null);
|
|
59
|
+
const basicInfoSectionRef = useRef<View>(null);
|
|
60
|
+
const aboutSectionRef = useRef<View>(null);
|
|
61
|
+
const quickActionsSectionRef = useRef<View>(null);
|
|
62
|
+
const securitySectionRef = useRef<View>(null);
|
|
63
|
+
|
|
64
|
+
// Section Y positions for scrolling
|
|
65
|
+
const [profilePictureSectionY, setProfilePictureSectionY] = useState<number | null>(null);
|
|
66
|
+
const [basicInfoSectionY, setBasicInfoSectionY] = useState<number | null>(null);
|
|
67
|
+
const [aboutSectionY, setAboutSectionY] = useState<number | null>(null);
|
|
68
|
+
const [quickActionsSectionY, setQuickActionsSectionY] = useState<number | null>(null);
|
|
69
|
+
const [securitySectionY, setSecuritySectionY] = useState<number | null>(null);
|
|
55
70
|
|
|
56
71
|
// Two-Factor (TOTP) state
|
|
57
72
|
const [totpSetupUrl, setTotpSetupUrl] = useState<string | null>(null);
|
|
@@ -246,19 +261,69 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
246
261
|
// Use a ref to track if we've already set the initial field to avoid loops
|
|
247
262
|
const hasSetInitialFieldRef = useRef(false);
|
|
248
263
|
const previousInitialFieldRef = useRef<string | undefined>(undefined);
|
|
264
|
+
const initialFieldTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
265
|
+
|
|
266
|
+
// Delay constant for scroll completion
|
|
267
|
+
const SCROLL_DELAY_MS = 600;
|
|
268
|
+
|
|
269
|
+
// Helper to get current value for a field
|
|
270
|
+
const getFieldCurrentValue = useCallback((field: string): string => {
|
|
271
|
+
switch (field) {
|
|
272
|
+
case 'displayName':
|
|
273
|
+
return displayName;
|
|
274
|
+
case 'username':
|
|
275
|
+
return username;
|
|
276
|
+
case 'email':
|
|
277
|
+
return email;
|
|
278
|
+
case 'bio':
|
|
279
|
+
return bio;
|
|
280
|
+
case 'location':
|
|
281
|
+
case 'links':
|
|
282
|
+
case 'twoFactor':
|
|
283
|
+
return '';
|
|
284
|
+
default:
|
|
285
|
+
return '';
|
|
286
|
+
}
|
|
287
|
+
}, [displayName, username, email, bio]);
|
|
288
|
+
|
|
289
|
+
// Handle initialSection prop to scroll to specific section
|
|
290
|
+
const hasScrolledToSectionRef = useRef(false);
|
|
291
|
+
const previousInitialSectionRef = useRef<string | undefined>(undefined);
|
|
292
|
+
const SCROLL_OFFSET = 100; // Offset to show section near top of viewport
|
|
293
|
+
|
|
294
|
+
// Map section names to their Y positions
|
|
295
|
+
const sectionYPositions = useMemo(() => ({
|
|
296
|
+
profilePicture: profilePictureSectionY,
|
|
297
|
+
basicInfo: basicInfoSectionY,
|
|
298
|
+
about: aboutSectionY,
|
|
299
|
+
quickActions: quickActionsSectionY,
|
|
300
|
+
security: securitySectionY,
|
|
301
|
+
}), [profilePictureSectionY, basicInfoSectionY, aboutSectionY, quickActionsSectionY, securitySectionY]);
|
|
302
|
+
|
|
249
303
|
useEffect(() => {
|
|
250
|
-
// If
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
304
|
+
// If initialSection changed, reset the flag
|
|
305
|
+
if (previousInitialSectionRef.current !== initialSection) {
|
|
306
|
+
hasScrolledToSectionRef.current = false;
|
|
307
|
+
previousInitialSectionRef.current = initialSection;
|
|
254
308
|
}
|
|
255
|
-
|
|
256
|
-
//
|
|
257
|
-
if (
|
|
258
|
-
|
|
259
|
-
|
|
309
|
+
|
|
310
|
+
// Scroll to the specified section if initialSection is provided and we haven't scrolled yet
|
|
311
|
+
if (initialSection && !hasScrolledToSectionRef.current) {
|
|
312
|
+
const sectionY = sectionYPositions[initialSection as keyof typeof sectionYPositions];
|
|
313
|
+
|
|
314
|
+
if (sectionY !== null && sectionY !== undefined && scrollViewRef.current) {
|
|
315
|
+
requestAnimationFrame(() => {
|
|
316
|
+
requestAnimationFrame(() => {
|
|
317
|
+
scrollViewRef.current?.scrollTo({
|
|
318
|
+
y: Math.max(0, sectionY - SCROLL_OFFSET),
|
|
319
|
+
animated: true,
|
|
320
|
+
});
|
|
321
|
+
hasScrolledToSectionRef.current = true;
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
}
|
|
260
325
|
}
|
|
261
|
-
}, [
|
|
326
|
+
}, [initialSection, sectionYPositions]);
|
|
262
327
|
|
|
263
328
|
const handleSave = async () => {
|
|
264
329
|
if (!user) return;
|
|
@@ -400,7 +465,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
400
465
|
});
|
|
401
466
|
}, [showBottomSheet, oxyServices, avatarFileId, updateUser, user]);
|
|
402
467
|
|
|
403
|
-
const startEditing = (type: string, currentValue: string) => {
|
|
468
|
+
const startEditing = useCallback((type: string, currentValue: string) => {
|
|
404
469
|
switch (type) {
|
|
405
470
|
case 'displayName':
|
|
406
471
|
setTempDisplayName(displayName);
|
|
@@ -429,7 +494,50 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
429
494
|
break;
|
|
430
495
|
}
|
|
431
496
|
setEditingField(type);
|
|
432
|
-
};
|
|
497
|
+
}, [displayName, lastName]);
|
|
498
|
+
|
|
499
|
+
// Handle initialField prop - must be after startEditing and openAvatarPicker are declared
|
|
500
|
+
useEffect(() => {
|
|
501
|
+
// Clear any pending timeout
|
|
502
|
+
if (initialFieldTimeoutRef.current) {
|
|
503
|
+
clearTimeout(initialFieldTimeoutRef.current);
|
|
504
|
+
initialFieldTimeoutRef.current = null;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// If initialField changed, reset the flag
|
|
508
|
+
if (previousInitialFieldRef.current !== initialField) {
|
|
509
|
+
hasSetInitialFieldRef.current = false;
|
|
510
|
+
previousInitialFieldRef.current = initialField;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Set the editing field if initialField is provided and we haven't set it yet
|
|
514
|
+
if (initialField && !hasSetInitialFieldRef.current) {
|
|
515
|
+
// Special handling for avatar - open avatar picker directly
|
|
516
|
+
if (initialField === 'avatar') {
|
|
517
|
+
// Wait for section to be scrolled, then open picker
|
|
518
|
+
initialFieldTimeoutRef.current = setTimeout(() => {
|
|
519
|
+
openAvatarPicker();
|
|
520
|
+
hasSetInitialFieldRef.current = true;
|
|
521
|
+
}, SCROLL_DELAY_MS);
|
|
522
|
+
} else {
|
|
523
|
+
// For other fields, get current value and start editing after scroll
|
|
524
|
+
const currentValue = getFieldCurrentValue(initialField);
|
|
525
|
+
|
|
526
|
+
// Wait for section to be scrolled, then start editing
|
|
527
|
+
initialFieldTimeoutRef.current = setTimeout(() => {
|
|
528
|
+
startEditing(initialField, currentValue);
|
|
529
|
+
hasSetInitialFieldRef.current = true;
|
|
530
|
+
}, SCROLL_DELAY_MS);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return () => {
|
|
535
|
+
if (initialFieldTimeoutRef.current) {
|
|
536
|
+
clearTimeout(initialFieldTimeoutRef.current);
|
|
537
|
+
initialFieldTimeoutRef.current = null;
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}, [initialField, getFieldCurrentValue, startEditing, openAvatarPicker]);
|
|
433
541
|
|
|
434
542
|
const saveField = (type: string) => {
|
|
435
543
|
animateSaveButton(0.95); // Scale down slightly for animation
|
|
@@ -1247,11 +1355,15 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
1247
1355
|
)}
|
|
1248
1356
|
{/* Profile Picture Section */}
|
|
1249
1357
|
<View
|
|
1250
|
-
ref={
|
|
1358
|
+
ref={(ref) => {
|
|
1359
|
+
avatarSectionRef.current = ref;
|
|
1360
|
+
profilePictureSectionRef.current = ref;
|
|
1361
|
+
}}
|
|
1251
1362
|
style={styles.section}
|
|
1252
1363
|
onLayout={(event) => {
|
|
1253
1364
|
const { y } = event.nativeEvent.layout;
|
|
1254
1365
|
setAvatarSectionY(y);
|
|
1366
|
+
setProfilePictureSectionY(y);
|
|
1255
1367
|
}}
|
|
1256
1368
|
>
|
|
1257
1369
|
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
@@ -1319,7 +1431,14 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
1319
1431
|
</View>
|
|
1320
1432
|
|
|
1321
1433
|
{/* Basic Information */}
|
|
1322
|
-
<View
|
|
1434
|
+
<View
|
|
1435
|
+
ref={basicInfoSectionRef}
|
|
1436
|
+
style={styles.section}
|
|
1437
|
+
onLayout={(event) => {
|
|
1438
|
+
const { y } = event.nativeEvent.layout;
|
|
1439
|
+
setBasicInfoSectionY(y);
|
|
1440
|
+
}}
|
|
1441
|
+
>
|
|
1323
1442
|
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1324
1443
|
{t('editProfile.sections.basicInfo') || 'BASIC INFORMATION'}
|
|
1325
1444
|
</Text>
|
|
@@ -1357,7 +1476,14 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
1357
1476
|
</View>
|
|
1358
1477
|
|
|
1359
1478
|
{/* About You */}
|
|
1360
|
-
<View
|
|
1479
|
+
<View
|
|
1480
|
+
ref={aboutSectionRef}
|
|
1481
|
+
style={styles.section}
|
|
1482
|
+
onLayout={(event) => {
|
|
1483
|
+
const { y } = event.nativeEvent.layout;
|
|
1484
|
+
setAboutSectionY(y);
|
|
1485
|
+
}}
|
|
1486
|
+
>
|
|
1361
1487
|
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1362
1488
|
{t('editProfile.sections.about') || 'ABOUT YOU'}
|
|
1363
1489
|
</Text>
|
|
@@ -1457,7 +1583,14 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
1457
1583
|
</View>
|
|
1458
1584
|
|
|
1459
1585
|
{/* Quick Actions */}
|
|
1460
|
-
<View
|
|
1586
|
+
<View
|
|
1587
|
+
ref={quickActionsSectionRef}
|
|
1588
|
+
style={styles.section}
|
|
1589
|
+
onLayout={(event) => {
|
|
1590
|
+
const { y } = event.nativeEvent.layout;
|
|
1591
|
+
setQuickActionsSectionY(y);
|
|
1592
|
+
}}
|
|
1593
|
+
>
|
|
1461
1594
|
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1462
1595
|
{t('editProfile.sections.quickActions') || 'QUICK ACTIONS'}
|
|
1463
1596
|
</Text>
|
|
@@ -1495,7 +1628,14 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string
|
|
|
1495
1628
|
</View>
|
|
1496
1629
|
|
|
1497
1630
|
{/* Security */}
|
|
1498
|
-
<View
|
|
1631
|
+
<View
|
|
1632
|
+
ref={securitySectionRef}
|
|
1633
|
+
style={styles.section}
|
|
1634
|
+
onLayout={(event) => {
|
|
1635
|
+
const { y } = event.nativeEvent.layout;
|
|
1636
|
+
setSecuritySectionY(y);
|
|
1637
|
+
}}
|
|
1638
|
+
>
|
|
1499
1639
|
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1500
1640
|
{t('editProfile.sections.security') || 'SECURITY'}
|
|
1501
1641
|
</Text>
|