@oxyhq/services 22.12.0 → 22.12.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/hooks/useSurfaceHeader.js +2 -2
- package/lib/commonjs/ui/hooks/useSurfaceHeader.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +11 -5
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +35 -41
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/hooks/useSurfaceHeader.js +2 -2
- package/lib/module/ui/hooks/useSurfaceHeader.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +11 -5
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +35 -41
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/ui/hooks/useSurfaceHeader.ts +2 -17
- package/src/ui/screens/FileManagementScreen.tsx +7 -4
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +35 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "22.12.
|
|
3
|
+
"version": "22.12.1",
|
|
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",
|
|
@@ -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.50.0",
|
|
180
180
|
"@oxyhq/core": "^12.10.0",
|
|
181
181
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
182
182
|
"@react-native-community/netinfo": "^11.4.1",
|
|
@@ -57,21 +57,6 @@ export function useSurfaceHeader(content: SurfaceHeaderContent | null | undefine
|
|
|
57
57
|
if (!set) return;
|
|
58
58
|
set(content ?? null);
|
|
59
59
|
return () => set(null);
|
|
60
|
-
//
|
|
61
|
-
}, [
|
|
62
|
-
set,
|
|
63
|
-
content?.title,
|
|
64
|
-
content?.titleContent,
|
|
65
|
-
content?.subtitle,
|
|
66
|
-
content?.largeTitle,
|
|
67
|
-
content?.left,
|
|
68
|
-
content?.right,
|
|
69
|
-
content?.onBack,
|
|
70
|
-
content?.primaryAction,
|
|
71
|
-
content?.actions,
|
|
72
|
-
content?.search,
|
|
73
|
-
content?.segments,
|
|
74
|
-
content?.tone,
|
|
75
|
-
content?.progress,
|
|
76
|
-
]);
|
|
60
|
+
// Callers must memoize `content` (especially slot nodes) so the header does not thrash.
|
|
61
|
+
}, [set, content]);
|
|
77
62
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useState, useCallback, useMemo, useRef } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
View,
|
|
4
5
|
Text,
|
|
@@ -1100,12 +1101,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1100
1101
|
<Skeleton.Box width="100%" height={44} borderRadius={22} />
|
|
1101
1102
|
</View>
|
|
1102
1103
|
|
|
1103
|
-
{/* Photo grid */}
|
|
1104
|
+
{/* Photo grid — static skeleton placeholders; index keys are stable */}
|
|
1104
1105
|
<View style={{ padding: GRID_PADDING, gap: TILE_GAP }}>
|
|
1105
1106
|
{Array.from({ length: GRID_ROWS }, (_, row) => (
|
|
1106
|
-
|
|
1107
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: fixed-size skeleton grid, order never changes
|
|
1108
|
+
<Skeleton.Row key={`skeleton-row-${row}`} style={{ gap: TILE_GAP }}>
|
|
1107
1109
|
{Array.from({ length: GRID_COLUMNS }, (_, col) => (
|
|
1108
|
-
|
|
1110
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: fixed-size skeleton grid, order never changes
|
|
1111
|
+
<Skeleton.Box key={`skeleton-${row}-${col}`} width={tileSize} height={tileSize} borderRadius={6} />
|
|
1109
1112
|
))}
|
|
1110
1113
|
</Skeleton.Row>
|
|
1111
1114
|
))}
|
|
@@ -74,26 +74,22 @@ const hapticSelection = async (): Promise<void> => {
|
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* The picker renders inside a Bloom `<Dialog>` in NAV-HEADER mode
|
|
78
|
-
* narrow, centered card on `md+`). The Dialog
|
|
79
|
-
* (Cancel / "Choose Photo" / Upload, wired by
|
|
80
|
-
* `useSurfaceHeader`)
|
|
81
|
-
* the Dialog
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* content; the grid's `pt-[24px]` (fade + 4dp breathing room) clears it. The
|
|
92
|
-
* `GRID_CONTENT_PADDING_TOP` constant MUST stay in sync with that class.
|
|
77
|
+
* The picker renders inside a Bloom `<Dialog>` in NAV-HEADER mode with
|
|
78
|
+
* `tone: 'onImage'` (bottom-sheet on narrow, centered card on `md+`). The Dialog
|
|
79
|
+
* owns the shared gradient nav bar (Cancel / "Choose Photo" / Upload, wired by
|
|
80
|
+
* `FileManagementScreen` through `useSurfaceHeader`). Because the tone is
|
|
81
|
+
* `'onImage'`, the Dialog inserts NO nav-bar spacer above this content
|
|
82
|
+
* (`DialogNavBarSpacer` collapses to nothing), so the black photo grid slides UP
|
|
83
|
+
* under the floating translucent bar and reads EDGE-TO-EDGE — the Apple/Google
|
|
84
|
+
* Photos immersive picker. The grid therefore takes the FULL `frameSize` height
|
|
85
|
+
* and carries no top padding; only the pull-to-refresh spinner is offset so it
|
|
86
|
+
* lands below the floating bar.
|
|
87
|
+
*
|
|
88
|
+
* `FLOATING_NAV_BAR_HEIGHT` mirrors Bloom's `DIALOG_NAV_BAR_HEIGHT` (not exported
|
|
89
|
+
* from `@oxyhq/bloom/dialog`) — the height of the floating bar the refresh spinner
|
|
90
|
+
* must clear.
|
|
93
91
|
*/
|
|
94
|
-
const
|
|
95
|
-
const HEADER_GRADIENT_FADE = 20;
|
|
96
|
-
const GRID_CONTENT_PADDING_TOP = HEADER_GRADIENT_FADE + 4; // 24 — matches `pt-[24px]`
|
|
92
|
+
const FLOATING_NAV_BAR_HEIGHT = 52;
|
|
97
93
|
|
|
98
94
|
/**
|
|
99
95
|
* Props for the dedicated photo picker view. Used by FileManagementScreen
|
|
@@ -383,18 +379,15 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
383
379
|
|
|
384
380
|
// The picker FILLS its panel: the surface morphs the container UP to an
|
|
385
381
|
// explicit large target (`frameSize.heightRatio: 0.9` in `surfaceRegistry`),
|
|
386
|
-
// and this root takes that SAME 0.9-of-viewport height
|
|
387
|
-
// nav-bar spacer
|
|
388
|
-
//
|
|
389
|
-
// `maxHeightRatio: 0.9` cap
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
const panelHeight = useMemo(
|
|
395
|
-
() => Math.round(windowHeight * 0.9) - DIALOG_NAV_BAR_SPACER,
|
|
396
|
-
[windowHeight],
|
|
397
|
-
);
|
|
382
|
+
// and this root takes that SAME 0.9-of-viewport height. Under `tone:'onImage'`
|
|
383
|
+
// the Dialog inserts NO nav-bar spacer, so the grid uses the FULL morph target
|
|
384
|
+
// (no `-52`) and slides up under the floating bar, edge-to-edge, within the
|
|
385
|
+
// panel's `maxHeightRatio: 0.9` cap. A `flex: 1` root can't fill (no definite
|
|
386
|
+
// bound propagates through the sheet's flex chain at rest, where the panel is
|
|
387
|
+
// height:auto), so the fixed height is required; the FlatList scrolls within
|
|
388
|
+
// it. Keep the 0.9 ratio in sync with the picker's `frameSize` in
|
|
389
|
+
// `surfaceRegistry.ts`.
|
|
390
|
+
const panelHeight = useMemo(() => Math.round(windowHeight * 0.9), [windowHeight]);
|
|
398
391
|
const rootStyle = useMemo(() => ({ height: panelHeight }), [panelHeight]);
|
|
399
392
|
|
|
400
393
|
const effectiveWidth = gridWidth > 0 ? gridWidth : windowWidth;
|
|
@@ -410,7 +403,7 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
410
403
|
const skeletonTileCount = useMemo(() => {
|
|
411
404
|
const rowHeight = cellSize + gutter;
|
|
412
405
|
if (rowHeight <= 0) return columns * SKELETON_MIN_ROWS;
|
|
413
|
-
const available = Math.max(0, panelHeight -
|
|
406
|
+
const available = Math.max(0, panelHeight - FLOATING_NAV_BAR_HEIGHT);
|
|
414
407
|
const rows = Math.max(SKELETON_MIN_ROWS, Math.floor(available / rowHeight));
|
|
415
408
|
return rows * columns;
|
|
416
409
|
}, [cellSize, gutter, columns, panelHeight]);
|
|
@@ -531,9 +524,9 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
531
524
|
clears that fade). */}
|
|
532
525
|
{isEmpty && loadError ? (
|
|
533
526
|
/* Terminal load failure — a DISTINCT error surface (not the
|
|
534
|
-
empty state),
|
|
535
|
-
|
|
536
|
-
<View className="flex-1 items-center justify-center
|
|
527
|
+
empty state), centered in the immersive frame. Retry re-runs
|
|
528
|
+
the list query. */
|
|
529
|
+
<View className="flex-1 items-center justify-center">
|
|
537
530
|
<FileLibraryError
|
|
538
531
|
title={t('fileManagement.loadError.title')}
|
|
539
532
|
description={t('fileManagement.loadError.description')}
|
|
@@ -548,13 +541,11 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
548
541
|
) : isEmpty && loading ? (
|
|
549
542
|
/* Loading skeleton — the picker's OWN shape: shimmer tiles laid
|
|
550
543
|
out with the SAME placement-aware geometry the real grid uses
|
|
551
|
-
(`computePhotoGridLayout(effectiveWidth)`),
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
construction). */
|
|
544
|
+
(`computePhotoGridLayout(effectiveWidth)`), edge-to-edge under
|
|
545
|
+
the floating bar. `flexWrap` breaks rows exactly at `columns`
|
|
546
|
+
because the last tile in each row carries no right margin (a
|
|
547
|
+
full row + gutters fits the measured width by construction). */
|
|
556
548
|
<View
|
|
557
|
-
className="pt-[24px]"
|
|
558
549
|
style={{ flexDirection: 'row', flexWrap: 'wrap' }}
|
|
559
550
|
pointerEvents="none"
|
|
560
551
|
accessibilityElementsHidden
|
|
@@ -575,7 +566,7 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
575
566
|
))}
|
|
576
567
|
</View>
|
|
577
568
|
) : isEmpty ? (
|
|
578
|
-
<View className="flex-1 items-center justify-center px-space-32
|
|
569
|
+
<View className="flex-1 items-center justify-center px-space-32">
|
|
579
570
|
<View className="opacity-30 mb-space-16">
|
|
580
571
|
<MaterialCommunityIcons name="image-outline" size={64} color="#FFFFFF" />
|
|
581
572
|
</View>
|
|
@@ -616,7 +607,7 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
616
607
|
keyExtractor={keyExtractor}
|
|
617
608
|
numColumns={columns}
|
|
618
609
|
className="flex-1 min-h-0"
|
|
619
|
-
contentContainerClassName="
|
|
610
|
+
contentContainerClassName="pb-space-24"
|
|
620
611
|
showsVerticalScrollIndicator={false}
|
|
621
612
|
refreshControl={
|
|
622
613
|
<RefreshControl
|
|
@@ -624,7 +615,7 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
624
615
|
onRefresh={onRefresh}
|
|
625
616
|
tintColor="#FFFFFF"
|
|
626
617
|
colors={[primaryColor]}
|
|
627
|
-
progressViewOffset={
|
|
618
|
+
progressViewOffset={FLOATING_NAV_BAR_HEIGHT}
|
|
628
619
|
/>
|
|
629
620
|
}
|
|
630
621
|
onEndReached={handleEndReached}
|