@oxyhq/services 22.8.2 → 22.8.3
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/hooks/queries/userCacheRelationship.js +4 -1
- package/lib/commonjs/ui/hooks/queries/userCacheRelationship.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +15 -15
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +15 -2
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/hooks/queries/userCacheRelationship.js +4 -1
- package/lib/module/ui/hooks/queries/userCacheRelationship.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +15 -15
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +15 -2
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/userCacheRelationship.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/userCacheRelationship.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/ui/hooks/queries/__tests__/userCache.test.ts +33 -1
- package/src/ui/hooks/queries/userCacheRelationship.ts +6 -1
- package/src/ui/screens/FileManagementScreen.tsx +13 -13
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +10 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "22.8.
|
|
3
|
+
"version": "22.8.3",
|
|
4
4
|
"description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"@commitlint/cli": "^17.6.5",
|
|
137
137
|
"@commitlint/config-conventional": "^17.6.5",
|
|
138
138
|
"@expo/vector-icons": "^15.0.3",
|
|
139
|
-
"@oxyhq/core": "12.
|
|
139
|
+
"@oxyhq/core": "12.10.0",
|
|
140
140
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
141
141
|
"@react-native-community/netinfo": "^11.4.1",
|
|
142
142
|
"@react-native/babel-preset": "^0.86.0",
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
},
|
|
177
177
|
"peerDependencies": {
|
|
178
178
|
"@expo/vector-icons": "^15.0.3",
|
|
179
|
-
"@oxyhq/bloom": ">=0.
|
|
179
|
+
"@oxyhq/bloom": ">=0.43.0",
|
|
180
180
|
"@oxyhq/core": "^12.9.0",
|
|
181
181
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
182
182
|
"@react-native-community/netinfo": "^11.4.1",
|
|
@@ -374,11 +374,43 @@ describe('patchCachedUserRelationship', () => {
|
|
|
374
374
|
'viewer-1',
|
|
375
375
|
);
|
|
376
376
|
|
|
377
|
-
patchCachedUserRelationship(qc, 'u1', true
|
|
377
|
+
patchCachedUserRelationship(qc, 'u1', true);
|
|
378
378
|
|
|
379
379
|
expect(readByUsername(qc, 'alice', 'viewer-1')?.relationship).toEqual({
|
|
380
380
|
isFollowing: true,
|
|
381
381
|
followsYou: true,
|
|
382
382
|
});
|
|
383
383
|
});
|
|
384
|
+
|
|
385
|
+
it('never writes relationship onto the viewer-independent by-id key', () => {
|
|
386
|
+
const qc = makeClient();
|
|
387
|
+
upsertCachedUser(
|
|
388
|
+
qc,
|
|
389
|
+
{
|
|
390
|
+
id: 'u1',
|
|
391
|
+
username: 'alice',
|
|
392
|
+
relationship: { isFollowing: false, followsYou: true },
|
|
393
|
+
},
|
|
394
|
+
'viewer-1',
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
patchCachedUserRelationship(qc, 'u1', true);
|
|
398
|
+
|
|
399
|
+
expect(readById(qc, 'u1')?.relationship).toBeUndefined();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('updates viewer-scoped detailForViewer keys', () => {
|
|
403
|
+
const qc = makeClient();
|
|
404
|
+
qc.setQueryData(queryKeys.users.detailForViewer('u1', 'viewer-1'), {
|
|
405
|
+
id: 'u1',
|
|
406
|
+
username: 'alice',
|
|
407
|
+
relationship: { isFollowing: false, followsYou: false },
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
patchCachedUserRelationship(qc, 'u1', true);
|
|
411
|
+
|
|
412
|
+
expect(
|
|
413
|
+
qc.getQueryData<CacheableUser>(queryKeys.users.detailForViewer('u1', 'viewer-1'))?.relationship,
|
|
414
|
+
).toEqual({ isFollowing: true, followsYou: false });
|
|
415
|
+
});
|
|
384
416
|
});
|
|
@@ -16,7 +16,12 @@ export function patchCachedUserRelationship(
|
|
|
16
16
|
isFollowing: boolean,
|
|
17
17
|
): void {
|
|
18
18
|
queryClient.setQueriesData<CachedUser>(
|
|
19
|
-
{
|
|
19
|
+
{
|
|
20
|
+
queryKey: queryKeys.users.details(),
|
|
21
|
+
// Viewer-relative `relationship` must never be written on the viewer-
|
|
22
|
+
// independent by-id key — only viewer-scoped detail / by-username keys.
|
|
23
|
+
predicate: (query) => (query.queryKey as readonly unknown[]).includes('viewer'),
|
|
24
|
+
},
|
|
20
25
|
(existing) => {
|
|
21
26
|
if (!existing || existing.id !== targetUserId) return existing;
|
|
22
27
|
return {
|
|
@@ -206,6 +206,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
206
206
|
return sorted;
|
|
207
207
|
}, [files, searchQuery, viewMode, sortBy, sortOrder]);
|
|
208
208
|
const [photoDimensions, setPhotoDimensions] = useState<{ [key: string]: { width: number, height: number } }>({});
|
|
209
|
+
const photoDimensionsRef = useRef(photoDimensions);
|
|
210
|
+
photoDimensionsRef.current = photoDimensions;
|
|
209
211
|
const [loadingDimensions, setLoadingDimensions] = useState(false);
|
|
210
212
|
// Selection state
|
|
211
213
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set(initialSelectedIds));
|
|
@@ -441,11 +443,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
441
443
|
if (photos.length === 0) return;
|
|
442
444
|
|
|
443
445
|
setLoadingDimensions(true);
|
|
444
|
-
const newDimensions: { [key: string]: { width: number, height: number } } = { ...photoDimensions };
|
|
445
|
-
let hasNewDimensions = false;
|
|
446
446
|
|
|
447
|
-
//
|
|
448
|
-
|
|
447
|
+
// Snapshot ids missing from the cache at kick-off; concurrent runs may
|
|
448
|
+
// overlap but each merge is applied via a functional updater.
|
|
449
|
+
const photosToLoad = photos.filter((photo) => !photoDimensionsRef.current[photo.id]);
|
|
449
450
|
|
|
450
451
|
if (photosToLoad.length === 0) {
|
|
451
452
|
setLoadingDimensions(false);
|
|
@@ -453,6 +454,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
453
454
|
}
|
|
454
455
|
|
|
455
456
|
try {
|
|
457
|
+
const measured: { [key: string]: { width: number; height: number } } = {};
|
|
458
|
+
|
|
456
459
|
await Promise.all(
|
|
457
460
|
photosToLoad.map(async (photo) => {
|
|
458
461
|
try {
|
|
@@ -470,35 +473,32 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
470
473
|
Image.getSize(
|
|
471
474
|
downloadUrl,
|
|
472
475
|
(width: number, height: number) => {
|
|
473
|
-
|
|
474
|
-
hasNewDimensions = true;
|
|
476
|
+
measured[photo.id] = { width, height };
|
|
475
477
|
resolve();
|
|
476
478
|
},
|
|
477
479
|
() => {
|
|
478
480
|
// Fallback dimensions
|
|
479
|
-
|
|
480
|
-
hasNewDimensions = true;
|
|
481
|
+
measured[photo.id] = { width: 1, height: 1 };
|
|
481
482
|
resolve();
|
|
482
483
|
}
|
|
483
484
|
);
|
|
484
485
|
});
|
|
485
486
|
} catch (error) {
|
|
486
487
|
// Fallback dimensions for any errors
|
|
487
|
-
|
|
488
|
-
hasNewDimensions = true;
|
|
488
|
+
measured[photo.id] = { width: 1, height: 1 };
|
|
489
489
|
}
|
|
490
490
|
})
|
|
491
491
|
);
|
|
492
492
|
|
|
493
|
-
if (
|
|
494
|
-
setPhotoDimensions(
|
|
493
|
+
if (Object.keys(measured).length > 0) {
|
|
494
|
+
setPhotoDimensions((prev) => ({ ...prev, ...measured }));
|
|
495
495
|
}
|
|
496
496
|
} catch (error) {
|
|
497
497
|
// Photo dimensions loading failed, continue without dimensions
|
|
498
498
|
} finally {
|
|
499
499
|
setLoadingDimensions(false);
|
|
500
500
|
}
|
|
501
|
-
}, [thumbSourceFor
|
|
501
|
+
}, [thumbSourceFor]);
|
|
502
502
|
|
|
503
503
|
// Re-measure photo dimensions when their private-safe URLs resolve. The
|
|
504
504
|
// justified grid's own trigger fires on the photo SET, not on URL
|
|
@@ -571,8 +571,14 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
571
571
|
|
|
572
572
|
return (
|
|
573
573
|
// Measured wrapper: `style`-only (no className) so RN-Web fires onLayout.
|
|
574
|
-
|
|
575
|
-
|
|
574
|
+
// `minHeight: 0` down the whole flex chain: the sheet is `scrollable=false`
|
|
575
|
+
// (the FlatList owns scrolling) and clamped by its `maxHeight`, but on web a
|
|
576
|
+
// flex child defaults to `min-height: auto` and grows to its content, so the
|
|
577
|
+
// list overflows the clamp and is clipped (renders "half", no scroll). Letting
|
|
578
|
+
// each flex link shrink to the clamped height gives the FlatList a bounded
|
|
579
|
+
// scroll area. Native (Yoga) already defaults min to 0, so this is web-only.
|
|
580
|
+
<View style={{ flex: 1, minHeight: 0 }} onLayout={onRootLayout}>
|
|
581
|
+
<View className="flex-1 bg-black" style={{ minHeight: 0 }}>
|
|
576
582
|
{/* Photo grid (renders behind translucent header) */}
|
|
577
583
|
{isEmpty ? (
|
|
578
584
|
<View className="flex-1 items-center justify-center px-space-32 pt-[88px]">
|
|
@@ -616,6 +622,7 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
616
622
|
keyExtractor={keyExtractor}
|
|
617
623
|
numColumns={columns}
|
|
618
624
|
className="flex-1"
|
|
625
|
+
style={{ minHeight: 0 }}
|
|
619
626
|
contentContainerClassName="pt-[88px] pb-space-24"
|
|
620
627
|
showsVerticalScrollIndicator={false}
|
|
621
628
|
refreshControl={
|
|
@@ -630,7 +637,7 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
630
637
|
onEndReached={handleEndReached}
|
|
631
638
|
onEndReachedThreshold={0.4}
|
|
632
639
|
ListFooterComponent={listFooter}
|
|
633
|
-
removeClippedSubviews
|
|
640
|
+
removeClippedSubviews={Platform.OS !== 'web'}
|
|
634
641
|
initialNumToRender={Math.max(12, columns * 6)}
|
|
635
642
|
windowSize={9}
|
|
636
643
|
/>
|