@oxyhq/services 22.12.0 → 22.12.2

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.
Files changed (71) hide show
  1. package/lib/commonjs/ui/components/FollowButton.js +5 -8
  2. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  3. package/lib/commonjs/ui/hooks/useSurfaceHeader.js +2 -2
  4. package/lib/commonjs/ui/hooks/useSurfaceHeader.js.map +1 -1
  5. package/lib/commonjs/ui/navigation/surfaceNavStack.js +8 -7
  6. package/lib/commonjs/ui/navigation/surfaceNavStack.js.map +1 -1
  7. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js +48 -42
  8. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -1
  9. package/lib/commonjs/ui/screens/CreateAccountScreen.js +55 -98
  10. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  11. package/lib/commonjs/ui/screens/FileManagementScreen.js +32 -13
  12. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  13. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +35 -41
  14. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  15. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +14 -6
  16. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  17. package/lib/module/ui/components/FollowButton.js +5 -8
  18. package/lib/module/ui/components/FollowButton.js.map +1 -1
  19. package/lib/module/ui/hooks/useSurfaceHeader.js +2 -2
  20. package/lib/module/ui/hooks/useSurfaceHeader.js.map +1 -1
  21. package/lib/module/ui/navigation/surfaceNavStack.js +8 -7
  22. package/lib/module/ui/navigation/surfaceNavStack.js.map +1 -1
  23. package/lib/module/ui/screens/ChangeAvatarScreen.js +48 -42
  24. package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -1
  25. package/lib/module/ui/screens/CreateAccountScreen.js +58 -101
  26. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  27. package/lib/module/ui/screens/FileManagementScreen.js +32 -13
  28. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  29. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +35 -41
  30. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  31. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +14 -6
  32. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  33. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/ui/navigation/surfaceNavStack.d.ts +7 -4
  36. package/lib/typescript/commonjs/ui/navigation/surfaceNavStack.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts +9 -5
  38. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts +1 -1
  41. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
  42. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +3 -1
  44. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/ui/types/fileManagement.d.ts +10 -0
  46. package/lib/typescript/commonjs/ui/types/fileManagement.d.ts.map +1 -1
  47. package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
  48. package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
  49. package/lib/typescript/module/ui/navigation/surfaceNavStack.d.ts +7 -4
  50. package/lib/typescript/module/ui/navigation/surfaceNavStack.d.ts.map +1 -1
  51. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts +9 -5
  52. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  53. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  54. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts +1 -1
  55. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
  56. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  57. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +3 -1
  58. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  59. package/lib/typescript/module/ui/types/fileManagement.d.ts +10 -0
  60. package/lib/typescript/module/ui/types/fileManagement.d.ts.map +1 -1
  61. package/package.json +4 -4
  62. package/src/ui/components/FollowButton.tsx +5 -8
  63. package/src/ui/hooks/useSurfaceHeader.ts +2 -17
  64. package/src/ui/navigation/__tests__/surfaceNavStack.test.ts +49 -17
  65. package/src/ui/navigation/surfaceNavStack.ts +15 -11
  66. package/src/ui/screens/ChangeAvatarScreen.tsx +46 -39
  67. package/src/ui/screens/CreateAccountScreen.tsx +123 -167
  68. package/src/ui/screens/FileManagementScreen.tsx +28 -12
  69. package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +35 -44
  70. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +16 -6
  71. package/src/ui/types/fileManagement.ts +10 -0
@@ -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 (bottom-sheet on
78
- * narrow, centered card on `md+`). The Dialog owns the shared gradient nav bar
79
- * (Cancel / "Choose Photo" / Upload, wired by `FileManagementScreen` through
80
- * `useSurfaceHeader`) the picker no longer paints a bar of its own. In this mode
81
- * the Dialog reserves a fixed nav-bar-height SPACER above the picker's content and
82
- * its gradient overlay fades a little further into the content; the grid's own
83
- * top padding just clears that fade so the first row reads cleanly. Two fixed
84
- * layout constants mirror Bloom internals:
85
- * - `DIALOG_NAV_BAR_SPACER` mirrors Bloom's `DIALOG_NAV_BAR_HEIGHT` (not exported
86
- * from `@oxyhq/bloom/dialog`) the spacer the Dialog inserts above header-mode
87
- * `scrollable:false` content. The picker's own height is reduced by it so the
88
- * spacer + grid together fill the `frameSize` morph target (and never overflow
89
- * the panel's `maxHeightRatio: 0.9` cap, which would clip the last row).
90
- * - `HEADER_GRADIENT_FADE` is the gradient tail that bleeds past the bar into the
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 DIALOG_NAV_BAR_SPACER = 52;
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 MINUS the Dialog's
387
- // nav-bar spacer (which the Dialog inserts ABOVE this content in header mode).
388
- // spacer + root then exactly fill the morph target and stay within the panel's
389
- // `maxHeightRatio: 0.9` cap without the subtraction the grid's last row would
390
- // clip. A `flex: 1` root can't fill (no definite bound propagates through the
391
- // sheet's flex chain at rest, where the panel is height:auto), so the fixed
392
- // height is required; the FlatList scrolls within it. Keep the 0.9 ratio in
393
- // sync with the picker's `frameSize` in `surfaceRegistry.ts`.
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 - GRID_CONTENT_PADDING_TOP);
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), cleared below the nav bar's gradient fade.
535
- Retry re-runs the list query. */
536
- <View className="flex-1 items-center justify-center pt-[24px]">
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)`), cleared below the
552
- nav bar's gradient fade. `flexWrap` breaks rows exactly at
553
- `columns` because the last tile in each row carries no right
554
- margin (a full row + gutters fits the measured width by
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 pt-[24px]">
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="pt-[24px] pb-space-24"
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={GRID_CONTENT_PADDING_TOP}
618
+ progressViewOffset={FLOATING_NAV_BAR_HEIGHT}
628
619
  />
629
620
  }
630
621
  onEndReached={handleEndReached}
@@ -58,6 +58,8 @@ export interface UseFileUploadStateParams {
58
58
  multiSelect: boolean;
59
59
  afterSelect: 'close' | 'back' | 'none';
60
60
  onSelect?: (file: FileMetadata) => void;
61
+ /** Forward-navigation picker: see {@link FileManagementScreenProps.onPicked}. */
62
+ onPicked?: (file: FileMetadata) => void;
61
63
  goBack?: () => void;
62
64
  onClose?: () => void;
63
65
  selectedIds: Set<string>;
@@ -94,6 +96,7 @@ export const useFileUploadState = ({
94
96
  multiSelect,
95
97
  afterSelect,
96
98
  onSelect,
99
+ onPicked,
97
100
  goBack,
98
101
  onClose,
99
102
  selectedIds,
@@ -335,12 +338,19 @@ export const useFileUploadState = ({
335
338
  setTimeout(() => {
336
339
  const fileToSelect = uploadedFiles[0];
337
340
  if (!multiSelect && fileToSelect) {
338
- // Single select mode - directly call onSelect callback
339
- onSelect?.(fileToSelect);
340
- if (afterSelect === 'back') {
341
- goBack?.();
342
- } else if (afterSelect === 'close') {
343
- onClose?.();
341
+ if (onPicked) {
342
+ // Forward-navigation picker (avatar flow): hand the
343
+ // freshly-uploaded file onward (to the cropper). No
344
+ // dismiss/close — the picker frame stays below.
345
+ onPicked(fileToSelect);
346
+ } else {
347
+ // Single select mode - directly call onSelect callback
348
+ onSelect?.(fileToSelect);
349
+ if (afterSelect === 'back') {
350
+ goBack?.();
351
+ } else if (afterSelect === 'close') {
352
+ onClose?.();
353
+ }
344
354
  }
345
355
  } else if (multiSelect) {
346
356
  // Multi-select mode - add all uploaded files to selection
@@ -14,6 +14,16 @@ export interface FileManagementScreenProps extends BaseScreenProps {
14
14
  multiSelect?: boolean;
15
15
  // Callback when a file is selected (single select mode)
16
16
  onSelect?: (file: FileMetadata) => void;
17
+ /**
18
+ * Forward-navigation picker: called with the picked file in single-select
19
+ * mode when the picker is one FRAME of a larger in-surface flow. Unlike
20
+ * {@link onSelect}, the caller is expected to NAVIGATE the surface onward
21
+ * (e.g. the avatar flow pushes the cropper on top of this picker) — so on
22
+ * select the picker neither dismisses nor closes the session, and its
23
+ * Cancel/back pops THIS frame (returning to the frame that opened the picker)
24
+ * rather than closing. Takes precedence over `onSelect`/`dismiss` when set.
25
+ */
26
+ onPicked?: (file: FileMetadata) => void;
17
27
  // Callback when confirm pressed in multi-select mode
18
28
  onConfirmSelection?: OnConfirmFileSelection;
19
29
  // Initial selected file IDs for multi-select