@oxy.so/services 12.1.0 → 12.2.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.
Files changed (126) hide show
  1. package/lib/commonjs/assets/assets/one/floral.jpg.d.ts +1 -1
  2. package/lib/commonjs/index.js +4 -3
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/registerPlatform.js +20 -0
  5. package/lib/commonjs/registerPlatform.js.map +1 -0
  6. package/lib/commonjs/ui/client.js +15 -0
  7. package/lib/commonjs/ui/client.js.map +1 -1
  8. package/lib/commonjs/ui/components/OxyProvider.js +6 -5
  9. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  10. package/lib/commonjs/ui/components/SafeAreaRoot.js +139 -0
  11. package/lib/commonjs/ui/components/SafeAreaRoot.js.map +1 -0
  12. package/lib/commonjs/ui/components/fileManagement/FileViewer.js +17 -14
  13. package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
  14. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js +10 -17
  15. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  16. package/lib/commonjs/ui/index.js +1 -37
  17. package/lib/commonjs/ui/index.js.map +1 -1
  18. package/lib/commonjs/ui/navigation/surfaces.js +2 -2
  19. package/lib/commonjs/ui/one-video.js +3 -2
  20. package/lib/commonjs/ui/one-video.js.map +1 -1
  21. package/lib/commonjs/ui/screens/AvatarCropScreen.js +39 -25
  22. package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
  23. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +4 -2
  24. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  25. package/lib/commonjs/ui/screens/OxyOneScreen.js +3 -2
  26. package/lib/commonjs/ui/screens/OxyOneScreen.js.map +1 -1
  27. package/lib/commonjs/ui/utils/imageSize.js +31 -0
  28. package/lib/commonjs/ui/utils/imageSize.js.map +1 -0
  29. package/lib/commonjs/ui/utils/imageSize.native.js +31 -0
  30. package/lib/commonjs/ui/utils/imageSize.native.js.map +1 -0
  31. package/lib/module/assets/assets/one/floral.jpg.d.ts +1 -1
  32. package/lib/module/index.js +2 -3
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/registerPlatform.js +19 -0
  35. package/lib/module/registerPlatform.js.map +1 -0
  36. package/lib/module/ui/client.js +7 -0
  37. package/lib/module/ui/client.js.map +1 -1
  38. package/lib/module/ui/components/OxyProvider.js +6 -5
  39. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  40. package/lib/module/ui/components/SafeAreaRoot.js +135 -0
  41. package/lib/module/ui/components/SafeAreaRoot.js.map +1 -0
  42. package/lib/module/ui/components/fileManagement/FileViewer.js +18 -15
  43. package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
  44. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js +11 -18
  45. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  46. package/lib/module/ui/index.js +8 -3
  47. package/lib/module/ui/index.js.map +1 -1
  48. package/lib/module/ui/navigation/surfaces.js +2 -2
  49. package/lib/module/ui/one-video.js +4 -3
  50. package/lib/module/ui/one-video.js.map +1 -1
  51. package/lib/module/ui/screens/AvatarCropScreen.js +41 -25
  52. package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
  53. package/lib/module/ui/screens/EditProfileFieldScreen.js +5 -3
  54. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  55. package/lib/module/ui/screens/OxyOneScreen.js +4 -3
  56. package/lib/module/ui/screens/OxyOneScreen.js.map +1 -1
  57. package/lib/module/ui/utils/imageSize.js +26 -0
  58. package/lib/module/ui/utils/imageSize.js.map +1 -0
  59. package/lib/module/ui/utils/imageSize.native.js +26 -0
  60. package/lib/module/ui/utils/imageSize.native.js.map +1 -0
  61. package/lib/typescript/assets/one/floral.jpg.d.ts +1 -1
  62. package/lib/typescript/commonjs/index.d.ts +1 -0
  63. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/registerPlatform.d.ts +2 -0
  65. package/lib/typescript/commonjs/registerPlatform.d.ts.map +1 -0
  66. package/lib/typescript/commonjs/ui/client.d.ts +3 -0
  67. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts +3 -2
  69. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/ui/components/SafeAreaRoot.d.ts +28 -0
  71. package/lib/typescript/commonjs/ui/components/SafeAreaRoot.d.ts.map +1 -0
  72. package/lib/typescript/commonjs/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  75. package/lib/typescript/commonjs/ui/index.d.ts +1 -0
  76. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/ui/one-video.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/ui/screens/OxyOneScreen.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/ui/utils/imageSize.d.ts +6 -0
  82. package/lib/typescript/commonjs/ui/utils/imageSize.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/ui/utils/imageSize.native.d.ts +6 -0
  84. package/lib/typescript/commonjs/ui/utils/imageSize.native.d.ts.map +1 -0
  85. package/lib/typescript/module/index.d.ts +1 -0
  86. package/lib/typescript/module/index.d.ts.map +1 -1
  87. package/lib/typescript/module/registerPlatform.d.ts +2 -0
  88. package/lib/typescript/module/registerPlatform.d.ts.map +1 -0
  89. package/lib/typescript/module/ui/client.d.ts +3 -0
  90. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  91. package/lib/typescript/module/ui/components/OxyProvider.d.ts +3 -2
  92. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  93. package/lib/typescript/module/ui/components/SafeAreaRoot.d.ts +28 -0
  94. package/lib/typescript/module/ui/components/SafeAreaRoot.d.ts.map +1 -0
  95. package/lib/typescript/module/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
  96. package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  97. package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  98. package/lib/typescript/module/ui/index.d.ts +1 -0
  99. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  100. package/lib/typescript/module/ui/one-video.d.ts.map +1 -1
  101. package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts.map +1 -1
  102. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  103. package/lib/typescript/module/ui/screens/OxyOneScreen.d.ts.map +1 -1
  104. package/lib/typescript/module/ui/utils/imageSize.d.ts +6 -0
  105. package/lib/typescript/module/ui/utils/imageSize.d.ts.map +1 -0
  106. package/lib/typescript/module/ui/utils/imageSize.native.d.ts +6 -0
  107. package/lib/typescript/module/ui/utils/imageSize.native.d.ts.map +1 -0
  108. package/package.json +1 -1
  109. package/src/assets/one/floral.jpg.d.ts +1 -1
  110. package/src/index.ts +2 -3
  111. package/src/registerPlatform.ts +17 -0
  112. package/src/ui/client.ts +7 -0
  113. package/src/ui/components/OxyProvider.tsx +7 -6
  114. package/src/ui/components/SafeAreaRoot.tsx +123 -0
  115. package/src/ui/components/__tests__/SafeAreaRoot.test.tsx +162 -0
  116. package/src/ui/components/fileManagement/FileViewer.tsx +18 -13
  117. package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +13 -24
  118. package/src/ui/index.ts +7 -3
  119. package/src/ui/navigation/surfaces.ts +2 -2
  120. package/src/ui/one-video.tsx +3 -2
  121. package/src/ui/screens/AvatarCropScreen.tsx +30 -25
  122. package/src/ui/screens/EditProfileFieldScreen.tsx +7 -2
  123. package/src/ui/screens/OxyOneScreen.tsx +4 -3
  124. package/src/ui/utils/__tests__/imageSize.test.ts +61 -0
  125. package/src/ui/utils/imageSize.native.ts +26 -0
  126. package/src/ui/utils/imageSize.ts +26 -0
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/ui/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/ui/client.ts"],"names":[],"mappings":"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"}
@@ -4,8 +4,9 @@ import type { OxyProviderProps } from '../types/navigation.js';
4
4
  * OxyProvider - Universal provider for Expo apps (native + web)
5
5
  *
6
6
  * Provides authentication, session management, query client, and UI overlays.
7
- * Wraps its own overlay stack in SafeAreaProvider and GestureHandlerRootView so
8
- * the shared Bloom SURFACE STACK (`<SurfaceProvider>` / `<SurfaceHost>`) — which
7
+ * Wraps its own overlay stack in a safe-area root (`SafeAreaRoot`: the native
8
+ * SafeAreaProvider on a device, the window's metrics on web) and
9
+ * GestureHandlerRootView so the shared Bloom SURFACE STACK (`<SurfaceProvider>` / `<SurfaceHost>`) — which
9
10
  * hosts every SDK surface (the ~35 bottom-sheet routes + the AccountDialog) — can
10
11
  * safely render even when a consuming app has not mounted those providers yet.
11
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"OxyProvider.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwC,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAItE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAqB,CAAC;AAqC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,QAAA,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAyNrC,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"OxyProvider.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwC,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAqB,CAAC;AAsC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,QAAA,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAyNrC,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The safe-area root `OxyProvider` mounts around everything it renders.
3
+ *
4
+ * On iOS and Android it is react-native-safe-area-context's own
5
+ * `SafeAreaProvider`, which measures the native root view.
6
+ *
7
+ * On web it does NOT measure. From react-native-safe-area-context 5.9, the web
8
+ * provider measures its own element and reports
9
+ * `windowInsets.bottom - (innerHeight - rect.bottom)` as the bottom inset with
10
+ * no upper clamp (likewise `top - rect.top`). `OxyProvider` wraps the app's
11
+ * whole document, and on a page that scrolls that element is taller than the
12
+ * viewport, so everything below the fold became bottom inset: an 844px phone
13
+ * viewport with a 2690px page reported a ~1850px inset, and the mobile account
14
+ * dialog laid itself out empty. Web therefore gets the WINDOW's metrics — what
15
+ * every version before 5.9 reported for a root provider:
16
+ *
17
+ * - insets: CSS `env(safe-area-inset-*)`, read from one fixed, hidden probe
18
+ * after mount (zero during server rendering and hydration);
19
+ * - frame: the viewport (`useWindowDimensions`), never the document's height.
20
+ *
21
+ * Bloom raises its own web insets to the same `env()` values
22
+ * (`useCssSafeAreaInsets`), so nothing pads twice.
23
+ */
24
+ import type { ReactNode } from 'react';
25
+ export declare function SafeAreaRoot({ children }: {
26
+ children?: ReactNode;
27
+ }): import("react").JSX.Element;
28
+ //# sourceMappingURL=SafeAreaRoot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SafeAreaRoot.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/SafeAreaRoot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAcvC,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAGlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"FileViewer.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/fileManagement/FileViewer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,UAAU,eAAe;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,iFAAiF;IACjF,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AA0BD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuWhD,CAAC"}
1
+ {"version":3,"file":"FileViewer.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/fileManagement/FileViewer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKjD,UAAU,eAAe;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,iFAAiF;IACjF,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AA0BD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2WhD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"JustifiedPhotoGrid.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/photogrid/JustifiedPhotoGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA4CjD,6EAA6E;AAC7E,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,EAAE,CAMpF;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,GAAG,SAAS,CAAC;IACzD,wBAAwB,EAAE,CACxB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,KACZ,KAAK,CAAC,YAAY,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;;AA6KD,wBAA8C"}
1
+ {"version":3,"file":"JustifiedPhotoGrid.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/photogrid/JustifiedPhotoGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAiCjD,6EAA6E;AAC7E,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,EAAE,CAMpF;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,GAAG,SAAS,CAAC;IACzD,wBAAwB,EAAE,CACxB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,KACZ,KAAK,CAAC,YAAY,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;;AA6KD,wBAA8C"}
@@ -1,4 +1,4 @@
1
1
  /** Glyphs are decorative: hidden from assistive technology (see `decorativeIconSet`). */
2
- declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "devices" | "at" | "security" | "account" | "message" | "email" | "bio" | "phone" | "network" | "web" | "logout" | "label" | "history" | "wallet" | "link" | "text" | "translate" | "scale" | "solid" | "wrap" | "key" | "image" | "alert" | "radio" | "switch" | "grid" | "numeric" | "loading" | "decimal" | "contacts" | "ellipse" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "leaf" | "library" | "mail" | "pencil-outline" | "play" | "pulse" | "receipt-outline" | "resize" | "rocket" | "school-outline" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet-outline" | "watch" | "wifi" | "android" | "delete" | "forward" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "check-all" | "check-circle" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete-outline" | "dots-horizontal" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "image-multiple-outline" | "inbox" | "link-variant" | "lock-outline" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message-outline" | "minus-circle" | "motion" | "music-note-outline" | "null" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "trash-can-outline" | "tray-arrow-down" | "update" | "upload" | "video-outline" | "wifi-off" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
2
+ declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "android" | "web" | "devices" | "at" | "security" | "account" | "message" | "email" | "bio" | "phone" | "network" | "logout" | "label" | "history" | "wallet" | "link" | "text" | "translate" | "scale" | "solid" | "wrap" | "key" | "image" | "alert" | "radio" | "switch" | "grid" | "numeric" | "loading" | "decimal" | "contacts" | "ellipse" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "leaf" | "library" | "mail" | "pencil-outline" | "play" | "pulse" | "receipt-outline" | "resize" | "rocket" | "school-outline" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet-outline" | "watch" | "wifi" | "delete" | "forward" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "check-all" | "check-circle" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete-outline" | "dots-horizontal" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "image-multiple-outline" | "inbox" | "link-variant" | "lock-outline" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message-outline" | "minus-circle" | "motion" | "music-note-outline" | "null" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "trash-can-outline" | "tray-arrow-down" | "update" | "upload" | "video-outline" | "wifi-off" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
3
3
  export default MaterialCommunityIcons;
4
4
  //# sourceMappingURL=MaterialCommunityIcons.d.ts.map
@@ -11,6 +11,7 @@
11
11
  * removed because it (a) violated the dual CJS/ESM build rule (ESM bundles
12
12
  * cannot contain `require()` per CLAUDE.md) and (b) defeated tree-shaking.
13
13
  */
14
+ import '../registerPlatform.js';
14
15
  export { default as OxyProvider } from './components/OxyProvider.js';
15
16
  export { default as OxySignInButton } from './components/OxySignInButton.js';
16
17
  export { default as OxyAuthPrompt } from './components/OxyAuthPrompt.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,6BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,iCAA8B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,+BAA4B,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA4B,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA4B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA4B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAA6B,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gCAA6B,CAAC;AAE7F,YAAY,EAAE,oBAAoB,EAAE,MAAM,oCAAiC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,8BAA2B,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,8BAA2B,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA2B,CAAC;AACnE,8CAA8C;AAC9C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,8BAA2B,CAAC;AACpE,mDAAmD;AACnD,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA2B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,+BAA4B,CAAC;AAGtE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,yBAAsB,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAoC,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,uCAAoC,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAiB,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAiB,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,sBAAmB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAoB,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAM9E,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAoB,CAAC;AAGlD,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,0BAAuB,CAAC;AAC/B,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAAuB,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,wBAAqB,CAAC;AAS7B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,6BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,iCAA8B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,+BAA4B,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA4B,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA4B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA4B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAA6B,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gCAA6B,CAAC;AAE7F,YAAY,EAAE,oBAAoB,EAAE,MAAM,oCAAiC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,8BAA2B,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,8BAA2B,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA2B,CAAC;AACnE,8CAA8C;AAC9C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,8BAA2B,CAAC;AACpE,mDAAmD;AACnD,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA2B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,+BAA4B,CAAC;AAGtE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,yBAAsB,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAoC,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,uCAAoC,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAiB,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAiB,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,sBAAmB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAoB,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAM9E,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAoB,CAAC;AAGlD,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,0BAAuB,CAAC;AAC/B,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAAuB,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mCAAgC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"one-video.d.ts","sourceRoot":"","sources":["../../../../src/ui/one-video.tsx"],"names":[],"mappings":"AASA,oFAAoF;AACpF,wBAAgB,WAAW,gCAiC1B"}
1
+ {"version":3,"file":"one-video.d.ts","sourceRoot":"","sources":["../../../../src/ui/one-video.tsx"],"names":[],"mappings":"AAUA,oFAAoF;AACpF,wBAAgB,WAAW,gCAiC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarCropScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/AvatarCropScreen.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsB/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAK3D,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAuPD,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAsxBrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"AvatarCropScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/AvatarCropScreen.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuB/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAM3D,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAsPD,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA0xBrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"EditProfileFieldScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/EditProfileFieldScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAG9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAuB3D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,UAAU,GACV,KAAK,GACL,OAAO,GACP,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,GACX,OAAO,CAAC;AA4BZ,UAAU,2BAA4B,SAAQ,eAAe;IAC3D,6BAA6B;IAC7B,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;;AAqoBD,wBAAkD"}
1
+ {"version":3,"file":"EditProfileFieldScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/EditProfileFieldScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAuB3D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,UAAU,GACV,KAAK,GACL,OAAO,GACP,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,GACX,OAAO,CAAC;AA4BZ,UAAU,2BAA4B,SAAQ,eAAe;IAC3D,6BAA6B;IAC7B,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;;AAyoBD,wBAAkD"}
@@ -1 +1 @@
1
- {"version":3,"file":"OxyOneScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/OxyOneScreen.tsx"],"names":[],"mappings":"AAgCA;sEACsE;AACtE,MAAM,CAAC,OAAO,UAAU,YAAY,gCA4LnC"}
1
+ {"version":3,"file":"OxyOneScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/OxyOneScreen.tsx"],"names":[],"mappings":"AAiCA;sEACsE;AACtE,MAAM,CAAC,OAAO,UAAU,YAAY,gCA4LnC"}
@@ -0,0 +1,6 @@
1
+ export interface ImageSize {
2
+ width: number;
3
+ height: number;
4
+ }
5
+ export declare function getImageSize(uri: string): Promise<ImageSize>;
6
+ //# sourceMappingURL=imageSize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageSize.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/imageSize.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAI5D"}
@@ -0,0 +1,6 @@
1
+ export interface ImageSize {
2
+ width: number;
3
+ height: number;
4
+ }
5
+ export declare function getImageSize(uri: string): Promise<ImageSize>;
6
+ //# sourceMappingURL=imageSize.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageSize.native.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/imageSize.native.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAUlE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/services",
3
- "version": "12.1.0",
3
+ "version": "12.2.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",
@@ -1,2 +1,2 @@
1
- declare const source: import('react-native').ImageSourcePropType;
1
+ declare const source: number;
2
2
  export default source;
package/src/index.ts CHANGED
@@ -24,9 +24,8 @@
24
24
  /// <reference path="./types/react-native-classname.d.ts" />
25
25
  /// <reference path="./types/react-native-web-style.d.ts" />
26
26
 
27
- import { setPlatformOS, type PlatformOS } from '@oxy.so/core';
28
- import { Platform } from 'react-native';
29
- setPlatformOS(Platform.OS as PlatformOS);
27
+ // Must stay the first runtime import: core reads the platform from it.
28
+ import './registerPlatform';
30
29
 
31
30
  import { registerScreens } from './ui/navigation/routes';
32
31
  import { screenComponents } from './ui/navigation/screenComponents';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tells `@oxy.so/core` which platform it runs on.
3
+ *
4
+ * Core never imports `react-native`, so on iOS and Android it cannot detect the
5
+ * platform by itself (`navigator.product === 'ReactNative'` only says "native"
6
+ * and it answers `'unknown'`). Until this runs, `isNative()`, `isIOS()` and
7
+ * `isAndroid()` are false on a phone: the shared identity key is not read, the
8
+ * Commons session fallback is off, and Android-only identity paths are skipped.
9
+ *
10
+ * Every client entry imports this module FIRST (`index.ts`, `ui/index.ts`,
11
+ * `ui/client.ts`), so an app that keeps to a lean entry gets the same platform
12
+ * as one that imports the root barrel. `__tests__/boot/cryptoPolyfillOrder.test.ts` pins it.
13
+ */
14
+ import { setPlatformOS, type PlatformOS } from '@oxy.so/core';
15
+ import { Platform } from 'react-native';
16
+
17
+ setPlatformOS(Platform.OS as PlatformOS);
package/src/ui/client.ts CHANGED
@@ -12,6 +12,9 @@
12
12
  * import { OxyProvider, useOxy, LogoIcon } from '@oxy.so/services/ui/client';
13
13
  */
14
14
 
15
+ // Must stay the first runtime import: core reads the platform from it.
16
+ import '../registerPlatform';
17
+
15
18
  // Components
16
19
  export { default as OxyProvider } from './components/OxyProvider';
17
20
  export { default as OxySignInButton } from './components/OxySignInButton';
@@ -63,6 +66,10 @@ export {
63
66
  useUserByUsername,
64
67
  } from './hooks/queries/useAccountQueries';
65
68
  export { useStorage } from './hooks/useStorage';
69
+ // Realtime events and the profile mutation, which startup code (a socket hook,
70
+ // a locale provider) needs without the root barrel's account screens.
71
+ export { useOxyEvent } from './hooks/useOxyEvent';
72
+ export { useUpdateProfile } from './hooks/mutations/useAccountMutations';
66
73
  export type { UseStorageOptions, UseStorageResult } from './hooks/useStorage';
67
74
 
68
75
  // Route screens live at `@oxy.so/services/screens` so client imports preserve
@@ -3,7 +3,6 @@ import { loadScreens } from '../navigation/routes';
3
3
  import { useEffect, useRef, useState, useMemo, type FC } from 'react';
4
4
  import { AppState, Platform, StyleSheet } from 'react-native';
5
5
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
6
- import { SafeAreaProvider } from 'react-native-safe-area-context';
7
6
  import type { OxyProviderProps } from '../types/navigation';
8
7
  import { OxyRuntimeProvider, type OxyRuntimeProviderProps } from '../context/OxyContext';
9
8
  import { QueryClientProvider, focusManager, onlineManager } from '@tanstack/react-query';
@@ -19,6 +18,7 @@ import {
19
18
  } from '../utils/storageHelpers';
20
19
  import { isNetConnectivityOnline } from '../utils/netConnectivity';
21
20
  import { KeyboardBoundary } from './KeyboardBoundary';
21
+ import { SafeAreaRoot } from './SafeAreaRoot';
22
22
  import { ProductAnalyticsObserver } from '../analytics/productAnalytics';
23
23
  import { LanguageBridge } from './LanguageBridge';
24
24
  import { BloomLocaleBridge } from './BloomLocaleBridge';
@@ -45,8 +45,9 @@ const isWeb = Platform.OS === 'web';
45
45
  * OxyProvider - Universal provider for Expo apps (native + web)
46
46
  *
47
47
  * Provides authentication, session management, query client, and UI overlays.
48
- * Wraps its own overlay stack in SafeAreaProvider and GestureHandlerRootView so
49
- * the shared Bloom SURFACE STACK (`<SurfaceProvider>` / `<SurfaceHost>`) — which
48
+ * Wraps its own overlay stack in a safe-area root (`SafeAreaRoot`: the native
49
+ * SafeAreaProvider on a device, the window's metrics on web) and
50
+ * GestureHandlerRootView so the shared Bloom SURFACE STACK (`<SurfaceProvider>` / `<SurfaceHost>`) — which
50
51
  * hosts every SDK surface (the ~35 bottom-sheet routes + the AccountDialog) — can
51
52
  * safely render even when a consuming app has not mounted those providers yet.
52
53
  *
@@ -118,7 +119,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
118
119
  [onAskAlia, documentationUrl, aliaAvatar, oxyOneArtwork],
119
120
  );
120
121
  useEffect(() => {
121
- // Full entries are already registered; lean entries warm their screen chunk.
122
+ // The root entry registers the screens itself; `ui` and `ui/client` warm their chunk here.
122
123
  void loadScreens().catch(() => undefined);
123
124
  }, []);
124
125
  // Storage + persistence wiring.
@@ -302,9 +303,9 @@ const OxyProvider: FC<OxyProviderProps> = ({
302
303
 
303
304
  return (
304
305
  <GestureHandlerRootView style={bootStyles.providerRoot}>
305
- <SafeAreaProvider>
306
+ <SafeAreaRoot>
306
307
  <KeyboardBoundary>{coreContent}</KeyboardBoundary>
307
- </SafeAreaProvider>
308
+ </SafeAreaRoot>
308
309
  </GestureHandlerRootView>
309
310
  );
310
311
  };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The safe-area root `OxyProvider` mounts around everything it renders.
3
+ *
4
+ * On iOS and Android it is react-native-safe-area-context's own
5
+ * `SafeAreaProvider`, which measures the native root view.
6
+ *
7
+ * On web it does NOT measure. From react-native-safe-area-context 5.9, the web
8
+ * provider measures its own element and reports
9
+ * `windowInsets.bottom - (innerHeight - rect.bottom)` as the bottom inset with
10
+ * no upper clamp (likewise `top - rect.top`). `OxyProvider` wraps the app's
11
+ * whole document, and on a page that scrolls that element is taller than the
12
+ * viewport, so everything below the fold became bottom inset: an 844px phone
13
+ * viewport with a 2690px page reported a ~1850px inset, and the mobile account
14
+ * dialog laid itself out empty. Web therefore gets the WINDOW's metrics — what
15
+ * every version before 5.9 reported for a root provider:
16
+ *
17
+ * - insets: CSS `env(safe-area-inset-*)`, read from one fixed, hidden probe
18
+ * after mount (zero during server rendering and hydration);
19
+ * - frame: the viewport (`useWindowDimensions`), never the document's height.
20
+ *
21
+ * Bloom raises its own web insets to the same `env()` values
22
+ * (`useCssSafeAreaInsets`), so nothing pads twice.
23
+ */
24
+ import type { ReactNode } from 'react';
25
+ import { useMemo, useSyncExternalStore } from 'react';
26
+ import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
27
+ import {
28
+ type EdgeInsets,
29
+ SafeAreaFrameContext,
30
+ SafeAreaInsetsContext,
31
+ SafeAreaProvider,
32
+ } from 'react-native-safe-area-context';
33
+
34
+ const styles = StyleSheet.create({
35
+ fill: { flex: 1 },
36
+ });
37
+
38
+ export function SafeAreaRoot({ children }: { children?: ReactNode }) {
39
+ if (Platform.OS === 'web') return <WindowSafeAreaProvider>{children}</WindowSafeAreaProvider>;
40
+ return <SafeAreaProvider>{children}</SafeAreaProvider>;
41
+ }
42
+
43
+ /** Web: window metrics, provided through the library's own contexts. */
44
+ function WindowSafeAreaProvider({ children }: { children?: ReactNode }) {
45
+ const insets = useSyncExternalStore(subscribeWindowInsets, readWindowInsets, readServerInsets);
46
+ const { width, height } = useWindowDimensions();
47
+ const frame = useMemo(() => ({ x: 0, y: 0, width, height }), [width, height]);
48
+ // The same flex-filling view `SafeAreaProvider` renders, so layout below is unchanged.
49
+ return (
50
+ <View style={styles.fill}>
51
+ <SafeAreaFrameContext.Provider value={frame}>
52
+ <SafeAreaInsetsContext.Provider value={insets}>{children}</SafeAreaInsetsContext.Provider>
53
+ </SafeAreaFrameContext.Provider>
54
+ </View>
55
+ );
56
+ }
57
+
58
+ const ZERO: EdgeInsets = Object.freeze({ top: 0, right: 0, bottom: 0, left: 0 });
59
+
60
+ let probe: HTMLElement | null = null;
61
+ let snapshot: EdgeInsets = ZERO;
62
+ const listeners = new Set<() => void>();
63
+
64
+ function readServerInsets(): EdgeInsets {
65
+ return ZERO;
66
+ }
67
+
68
+ function readWindowInsets(): EdgeInsets {
69
+ return snapshot;
70
+ }
71
+
72
+ function measure(): void {
73
+ if (!probe) return;
74
+ const style = getComputedStyle(probe);
75
+ const next = {
76
+ top: Number.parseFloat(style.paddingTop) || 0,
77
+ right: Number.parseFloat(style.paddingRight) || 0,
78
+ bottom: Number.parseFloat(style.paddingBottom) || 0,
79
+ left: Number.parseFloat(style.paddingLeft) || 0,
80
+ };
81
+ if (
82
+ next.top === snapshot.top &&
83
+ next.right === snapshot.right &&
84
+ next.bottom === snapshot.bottom &&
85
+ next.left === snapshot.left
86
+ ) {
87
+ return;
88
+ }
89
+ snapshot = next.top || next.right || next.bottom || next.left ? next : ZERO;
90
+ for (const listener of listeners) listener();
91
+ }
92
+
93
+ function subscribeWindowInsets(listener: () => void): () => void {
94
+ if (typeof document === 'undefined' || !document.body) return () => {};
95
+ listeners.add(listener);
96
+ if (!probe) {
97
+ probe = document.createElement('div');
98
+ probe.setAttribute('aria-hidden', 'true');
99
+ // Through the CSSOM, which CSP `style-src` does not block.
100
+ probe.style.position = 'fixed';
101
+ probe.style.visibility = 'hidden';
102
+ probe.style.pointerEvents = 'none';
103
+ probe.style.top = '0';
104
+ probe.style.left = '0';
105
+ probe.style.setProperty('padding-top', 'env(safe-area-inset-top, 0px)');
106
+ probe.style.setProperty('padding-right', 'env(safe-area-inset-right, 0px)');
107
+ probe.style.setProperty('padding-bottom', 'env(safe-area-inset-bottom, 0px)');
108
+ probe.style.setProperty('padding-left', 'env(safe-area-inset-left, 0px)');
109
+ document.body.appendChild(probe);
110
+ window.addEventListener('resize', measure);
111
+ window.addEventListener('orientationchange', measure);
112
+ }
113
+ measure();
114
+ return () => {
115
+ listeners.delete(listener);
116
+ if (listeners.size > 0 || !probe) return;
117
+ window.removeEventListener('resize', measure);
118
+ window.removeEventListener('orientationchange', measure);
119
+ probe.remove();
120
+ probe = null;
121
+ snapshot = ZERO;
122
+ };
123
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * `OxyProvider`'s safe-area root on web reports the WINDOW's metrics and never
3
+ * mounts react-native-safe-area-context's measuring web provider.
4
+ *
5
+ * From 5.9 that provider measures its own element and reports
6
+ * `windowInsets.bottom - (innerHeight - rect.bottom)` unclamped, so a provider
7
+ * around a page that scrolls turns everything below the fold into bottom inset.
8
+ * OxyHQ/website measured it: an 844px viewport over a 2690px page gave the mobile
9
+ * account dialog a 2690px layout and an empty body (844px on 5.8.1). Here every
10
+ * element measures as that page, so any provider that consulted it would fail.
11
+ */
12
+ import type { ReactNode } from 'react';
13
+ import { act, render } from '@testing-library/react';
14
+ import { Platform } from 'react-native';
15
+ import type { EdgeInsets, Rect } from 'react-native-safe-area-context';
16
+
17
+ const mockMeasuringProvider = jest.fn();
18
+
19
+ // The library's own contexts and hooks, with its measuring provider recorded.
20
+ // (Its real entry loads a codegen native component this jest setup cannot.)
21
+ jest.mock('react-native-safe-area-context', () => {
22
+ const React = jest.requireActual<typeof import('react')>('react');
23
+ const SafeAreaInsetsContext = React.createContext<unknown>(null);
24
+ const SafeAreaFrameContext = React.createContext<unknown>(null);
25
+ return {
26
+ __esModule: true,
27
+ SafeAreaInsetsContext,
28
+ SafeAreaFrameContext,
29
+ SafeAreaProvider: ({ children }: { children?: ReactNode }) => {
30
+ mockMeasuringProvider();
31
+ return children;
32
+ },
33
+ useSafeAreaInsets: () => React.useContext(SafeAreaInsetsContext),
34
+ useSafeAreaFrame: () => React.useContext(SafeAreaFrameContext),
35
+ };
36
+ });
37
+
38
+ import { useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context';
39
+ import { SafeAreaRoot } from '../SafeAreaRoot';
40
+
41
+ const PAGE_HEIGHT = 2690;
42
+ const HOME_INDICATOR = 34;
43
+
44
+ const originalOS = Platform.OS;
45
+ const originalGetComputedStyle = window.getComputedStyle;
46
+ const originalRect = HTMLElement.prototype.getBoundingClientRect;
47
+
48
+ beforeEach(() => {
49
+ mockMeasuringProvider.mockClear();
50
+ // jsdom has no env(): the fixed probe padded with env(safe-area-inset-*)
51
+ // reads an iPhone's home-indicator inset at the bottom.
52
+ window.getComputedStyle = ((element: Element) => {
53
+ const style = originalGetComputedStyle(element);
54
+ const probe = (element as HTMLElement).style?.position === 'fixed';
55
+ return new Proxy(style, {
56
+ get(target, key) {
57
+ if (key === 'paddingBottom') return probe ? `${HOME_INDICATOR}px` : '0px';
58
+ if (key === 'paddingTop' || key === 'paddingLeft' || key === 'paddingRight') return '0px';
59
+ const value = Reflect.get(target, key);
60
+ return typeof value === 'function' ? value.bind(target) : value;
61
+ },
62
+ });
63
+ }) as typeof window.getComputedStyle;
64
+ HTMLElement.prototype.getBoundingClientRect = () =>
65
+ ({
66
+ x: 0,
67
+ y: 0,
68
+ top: 0,
69
+ left: 0,
70
+ width: 375,
71
+ height: PAGE_HEIGHT,
72
+ right: 375,
73
+ bottom: PAGE_HEIGHT,
74
+ toJSON: () => ({}),
75
+ }) as DOMRect;
76
+ });
77
+
78
+ afterEach(() => {
79
+ Platform.OS = originalOS;
80
+ window.getComputedStyle = originalGetComputedStyle;
81
+ HTMLElement.prototype.getBoundingClientRect = originalRect;
82
+ });
83
+
84
+ function renderMetrics() {
85
+ const seen: { insets?: EdgeInsets; frame?: Rect } = {};
86
+ function Probe() {
87
+ seen.insets = useSafeAreaInsets();
88
+ seen.frame = useSafeAreaFrame();
89
+ return null;
90
+ }
91
+ const view = render(
92
+ <SafeAreaRoot>
93
+ <Probe />
94
+ </SafeAreaRoot>,
95
+ );
96
+ return { seen, view };
97
+ }
98
+
99
+ describe('SafeAreaRoot', () => {
100
+ it('on web reports env() insets and the viewport frame, not the page', async () => {
101
+ Platform.OS = 'web';
102
+ const { seen, view } = renderMetrics();
103
+ await act(async () => {});
104
+ expect(mockMeasuringProvider).not.toHaveBeenCalled();
105
+ expect(seen.insets).toEqual({ top: 0, right: 0, bottom: HOME_INDICATOR, left: 0 });
106
+ // The react-native mock's window (useWindowDimensions), never PAGE_HEIGHT.
107
+ expect(seen.frame).toEqual({ x: 0, y: 0, width: 375, height: 667 });
108
+ view.unmount();
109
+ expect(document.querySelectorAll('div[aria-hidden="true"]')).toHaveLength(0);
110
+ });
111
+
112
+ it('on web provides zero insets until the browser answers', () => {
113
+ Platform.OS = 'web';
114
+ window.getComputedStyle = originalGetComputedStyle;
115
+ const { seen } = renderMetrics();
116
+ expect(seen.insets).toEqual({ top: 0, right: 0, bottom: 0, left: 0 });
117
+ });
118
+
119
+ it.each(['ios', 'android'] as const)('on %s mounts the native SafeAreaProvider', (os) => {
120
+ Platform.OS = os;
121
+ renderMetrics();
122
+ expect(mockMeasuringProvider).toHaveBeenCalled();
123
+ });
124
+ });
125
+
126
+ describe('the SDK mounts safe-area providers only through SafeAreaRoot', () => {
127
+ const fs = jest.requireActual<typeof import('node:fs')>('node:fs');
128
+ const path = jest.requireActual<typeof import('node:path')>('node:path');
129
+ const srcRoot = path.resolve(__dirname, '../../..');
130
+
131
+ function sourceFiles(dir: string): string[] {
132
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
133
+ const full = path.join(dir, entry.name);
134
+ if (entry.isDirectory()) return entry.name === '__tests__' ? [] : sourceFiles(full);
135
+ return /\.tsx?$/.test(entry.name) && !entry.name.endsWith('.d.ts') ? [full] : [];
136
+ });
137
+ }
138
+
139
+ it('no other source file names SafeAreaProvider', () => {
140
+ const files = sourceFiles(srcRoot);
141
+ expect(files.length).toBeGreaterThan(100);
142
+ const offenders = files
143
+ .filter((file) => /\bSafeAreaProvider\b/.test(fs.readFileSync(file, 'utf8')))
144
+ .map((file) => path.relative(srcRoot, file))
145
+ .filter((rel) => rel !== path.join('ui', 'components', 'SafeAreaRoot.tsx'))
146
+ // OxyProvider's usage doc shows an APP mounting its own provider.
147
+ .filter((rel) => {
148
+ const code = fs
149
+ .readFileSync(path.join(srcRoot, rel), 'utf8')
150
+ .replace(/\/\*[\s\S]*?\*\//g, '')
151
+ .replace(/(^|[^:])\/\/.*$/gm, '$1');
152
+ return /\bSafeAreaProvider\b/.test(code);
153
+ });
154
+ expect(offenders).toEqual([]);
155
+ });
156
+
157
+ it('OxyProvider wraps its tree in SafeAreaRoot', () => {
158
+ const provider = fs.readFileSync(path.join(srcRoot, 'ui/components/OxyProvider.tsx'), 'utf8');
159
+ expect(provider).toMatch(/import \{ SafeAreaRoot \} from '\.\/SafeAreaRoot'/);
160
+ expect(provider).toMatch(/<SafeAreaRoot>/);
161
+ });
162
+ });
@@ -1,7 +1,7 @@
1
1
  import { SpinnerIcon } from '@oxy.so/bloom/loading';
2
2
  import type React from 'react';
3
3
  import { useMemo, useState, useEffect } from 'react';
4
- import { View, ScrollView, Image, StyleSheet } from 'react-native';
4
+ import { View, ScrollView, StyleSheet } from 'react-native';
5
5
  import { Button } from '@oxy.so/bloom/button';
6
6
  import { Text } from '@oxy.so/bloom/typography';
7
7
  import { Image as ExpoImage } from 'expo-image';
@@ -9,6 +9,7 @@ import MaterialCommunityIcons from '../../icons/MaterialCommunityIcons';
9
9
  import { useTheme } from '@oxy.so/bloom/theme';
10
10
  import type { FileMetadata } from '@oxy.so/core';
11
11
  import { formatFileSize } from '../../utils/fileManagement';
12
+ import { getImageSize } from '../../utils/imageSize';
12
13
  import { GroupedSection, GroupedSectionItem } from '@oxy.so/bloom/grouped-section';
13
14
 
14
15
  interface FileViewerProps {
@@ -81,20 +82,24 @@ export const FileViewer: React.FC<FileViewerProps> = ({
81
82
 
82
83
  // Load image dimensions when image content is available
83
84
  useEffect(() => {
84
- if (isImage && fileContent) {
85
- Image.getSize(
86
- fileContent,
87
- (width, height) => {
88
- setImageDimensions({ width, height });
89
- },
90
- () => {
91
- // Fallback if dimensions can't be loaded
92
- setImageDimensions({ width: 400, height: 400 });
93
- },
94
- );
95
- } else {
85
+ if (!isImage || !fileContent) {
96
86
  setImageDimensions(null);
87
+ return;
97
88
  }
89
+ // A newer file may replace this one before its size arrives.
90
+ let current = true;
91
+ getImageSize(fileContent).then(
92
+ (size) => {
93
+ if (current) setImageDimensions(size);
94
+ },
95
+ () => {
96
+ // Fallback if dimensions can't be loaded
97
+ if (current) setImageDimensions({ width: 400, height: 400 });
98
+ },
99
+ );
100
+ return () => {
101
+ current = false;
102
+ };
98
103
  }, [isImage, fileContent]);
99
104
 
100
105
  // Calculate display size based on natural dimensions and max constraints