@oxyhq/services 22.9.0 → 22.11.0

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 (584) hide show
  1. package/lib/commonjs/index.js +24 -5
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/AvatarCameraBadge.js +42 -0
  4. package/lib/commonjs/ui/components/AvatarCameraBadge.js.map +1 -0
  5. package/lib/commonjs/ui/components/{OxyAccountDialog.js → OxyAccountDialogScreen.js} +55 -133
  6. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -0
  7. package/lib/commonjs/ui/components/OxyAuthChooser.js +657 -88
  8. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  9. package/lib/commonjs/ui/components/OxyConsentScreen.js +1 -1
  10. package/lib/commonjs/ui/components/ProfileButton.js +15 -1
  11. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  12. package/lib/commonjs/ui/components/ProfileSummaryCard.js +3 -23
  13. package/lib/commonjs/ui/components/ProfileSummaryCard.js.map +1 -1
  14. package/lib/commonjs/ui/components/RequireOxyAuth.js +1 -1
  15. package/lib/commonjs/ui/components/SurfaceHeaderAction.js +63 -0
  16. package/lib/commonjs/ui/components/SurfaceHeaderAction.js.map +1 -0
  17. package/lib/commonjs/ui/components/SurfaceScreen.js +91 -8
  18. package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
  19. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js +46 -29
  20. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  21. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +47 -23
  22. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
  23. package/lib/commonjs/ui/components/oxyAuthChooserStyles.js +81 -19
  24. package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +1 -1
  25. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js +83 -23
  26. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  27. package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js +83 -0
  28. package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js.map +1 -0
  29. package/lib/commonjs/ui/context/OxyContext.js +79 -29
  30. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  31. package/lib/commonjs/ui/hooks/queries/queryKeys.js +18 -1
  32. package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
  33. package/lib/commonjs/ui/hooks/queries/useFileQueries.js +129 -0
  34. package/lib/commonjs/ui/hooks/queries/useFileQueries.js.map +1 -0
  35. package/lib/commonjs/ui/hooks/useAvatarPicker.js +38 -55
  36. package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
  37. package/lib/commonjs/ui/hooks/useReduceMotion.js +57 -0
  38. package/lib/commonjs/ui/hooks/useReduceMotion.js.map +1 -0
  39. package/lib/commonjs/ui/hooks/useSurfaceHeader.js +46 -0
  40. package/lib/commonjs/ui/hooks/useSurfaceHeader.js.map +1 -0
  41. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +1 -1
  42. package/lib/commonjs/ui/navigation/accountDialogManager.js +36 -2
  43. package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -1
  44. package/lib/commonjs/ui/navigation/bottomSheetManager.js +5 -6
  45. package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
  46. package/lib/commonjs/ui/navigation/routes.js +4 -3
  47. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  48. package/lib/commonjs/ui/navigation/surfaceBackBridge.js +68 -0
  49. package/lib/commonjs/ui/navigation/surfaceBackBridge.js.map +1 -0
  50. package/lib/commonjs/ui/navigation/surfaceNavStack.js +62 -15
  51. package/lib/commonjs/ui/navigation/surfaceNavStack.js.map +1 -1
  52. package/lib/commonjs/ui/navigation/surfaceRegistry.js +84 -19
  53. package/lib/commonjs/ui/navigation/surfaceRegistry.js.map +1 -1
  54. package/lib/commonjs/ui/navigation/surfaces.js +139 -25
  55. package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
  56. package/lib/commonjs/ui/screens/AccountMembersScreen.js +214 -283
  57. package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
  58. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +88 -118
  59. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  60. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +22 -36
  61. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  62. package/lib/commonjs/ui/screens/AppInfoScreen.js +219 -226
  63. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  64. package/lib/commonjs/ui/screens/AvatarCropScreen.js +287 -289
  65. package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
  66. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js +301 -0
  67. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -0
  68. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +24 -39
  69. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  70. package/lib/commonjs/ui/screens/CreateAccountScreen.js +136 -158
  71. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  72. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +29 -36
  73. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  74. package/lib/commonjs/ui/screens/EditProfileScreen.js +101 -109
  75. package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -1
  76. package/lib/commonjs/ui/screens/FAQScreen.js +10 -15
  77. package/lib/commonjs/ui/screens/FAQScreen.js.map +1 -1
  78. package/lib/commonjs/ui/screens/FeedbackScreen.js +8 -16
  79. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  80. package/lib/commonjs/ui/screens/FileManagementScreen.js +403 -627
  81. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  82. package/lib/commonjs/ui/screens/HelpSupportScreen.js +62 -69
  83. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
  84. package/lib/commonjs/ui/screens/HistoryViewScreen.js +63 -79
  85. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  86. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +69 -98
  87. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  88. package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js +8 -14
  89. package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
  90. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +77 -94
  91. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
  92. package/lib/commonjs/ui/screens/ManageAccountScreen.js +99 -134
  93. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  94. package/lib/commonjs/ui/screens/NotificationsScreen.js +69 -76
  95. package/lib/commonjs/ui/screens/NotificationsScreen.js.map +1 -1
  96. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +3 -8
  97. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  98. package/lib/commonjs/ui/screens/PreferencesScreen.js +67 -74
  99. package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
  100. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +55 -91
  101. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  102. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +291 -304
  103. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  104. package/lib/commonjs/ui/screens/ProfileScreen.js +100 -114
  105. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  106. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +38 -45
  107. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
  108. package/lib/commonjs/ui/screens/SearchSettingsScreen.js +44 -57
  109. package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
  110. package/lib/commonjs/ui/screens/UserLinksScreen.js +24 -31
  111. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
  112. package/lib/commonjs/ui/screens/UserListScreen.js +14 -24
  113. package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
  114. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +16 -48
  115. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  116. package/lib/commonjs/ui/screens/fileManagement/FileLibraryError.js +63 -0
  117. package/lib/commonjs/ui/screens/fileManagement/FileLibraryError.js.map +1 -0
  118. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +54 -41
  119. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  120. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +44 -81
  121. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  122. package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js +66 -75
  123. package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -1
  124. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +111 -139
  125. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  126. package/lib/commonjs/ui/screens/trust/TrustFAQScreen.js +13 -20
  127. package/lib/commonjs/ui/screens/trust/TrustFAQScreen.js.map +1 -1
  128. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +14 -27
  129. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  130. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +68 -85
  131. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  132. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +28 -36
  133. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  134. package/lib/commonjs/ui/stores/resetSessionScopedStores.js +8 -3
  135. package/lib/commonjs/ui/stores/resetSessionScopedStores.js.map +1 -1
  136. package/lib/commonjs/ui/utils/avatarUtils.js +11 -2
  137. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  138. package/lib/commonjs/ui/utils/fileManagement.js +0 -79
  139. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  140. package/lib/module/index.js +12 -5
  141. package/lib/module/index.js.map +1 -1
  142. package/lib/module/ui/components/AvatarCameraBadge.js +40 -0
  143. package/lib/module/ui/components/AvatarCameraBadge.js.map +1 -0
  144. package/lib/module/ui/components/{OxyAccountDialog.js → OxyAccountDialogScreen.js} +57 -135
  145. package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -0
  146. package/lib/module/ui/components/OxyAuthChooser.js +659 -91
  147. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  148. package/lib/module/ui/components/OxyConsentScreen.js +1 -1
  149. package/lib/module/ui/components/ProfileButton.js +16 -2
  150. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  151. package/lib/module/ui/components/ProfileSummaryCard.js +2 -23
  152. package/lib/module/ui/components/ProfileSummaryCard.js.map +1 -1
  153. package/lib/module/ui/components/RequireOxyAuth.js +1 -1
  154. package/lib/module/ui/components/SurfaceHeaderAction.js +58 -0
  155. package/lib/module/ui/components/SurfaceHeaderAction.js.map +1 -0
  156. package/lib/module/ui/components/SurfaceScreen.js +93 -10
  157. package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
  158. package/lib/module/ui/components/fileManagement/FileDetailsModal.js +46 -29
  159. package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  160. package/lib/module/ui/components/modals/DeleteAccountModal.js +46 -23
  161. package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
  162. package/lib/module/ui/components/oxyAuthChooserStyles.js +81 -19
  163. package/lib/module/ui/components/oxyAuthChooserStyles.js.map +1 -1
  164. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js +83 -25
  165. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  166. package/lib/module/ui/components/surfaces/ActionSheetSurface.js +78 -0
  167. package/lib/module/ui/components/surfaces/ActionSheetSurface.js.map +1 -0
  168. package/lib/module/ui/context/OxyContext.js +80 -30
  169. package/lib/module/ui/context/OxyContext.js.map +1 -1
  170. package/lib/module/ui/hooks/queries/queryKeys.js +16 -0
  171. package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
  172. package/lib/module/ui/hooks/queries/useFileQueries.js +120 -0
  173. package/lib/module/ui/hooks/queries/useFileQueries.js.map +1 -0
  174. package/lib/module/ui/hooks/useAvatarPicker.js +38 -55
  175. package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
  176. package/lib/module/ui/hooks/useReduceMotion.js +53 -0
  177. package/lib/module/ui/hooks/useReduceMotion.js.map +1 -0
  178. package/lib/module/ui/hooks/useSurfaceHeader.js +42 -0
  179. package/lib/module/ui/hooks/useSurfaceHeader.js.map +1 -0
  180. package/lib/module/ui/hooks/useSwitchableAccounts.js +1 -1
  181. package/lib/module/ui/navigation/accountDialogManager.js +34 -2
  182. package/lib/module/ui/navigation/accountDialogManager.js.map +1 -1
  183. package/lib/module/ui/navigation/bottomSheetManager.js +6 -7
  184. package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
  185. package/lib/module/ui/navigation/routes.js +4 -3
  186. package/lib/module/ui/navigation/routes.js.map +1 -1
  187. package/lib/module/ui/navigation/surfaceBackBridge.js +63 -0
  188. package/lib/module/ui/navigation/surfaceBackBridge.js.map +1 -0
  189. package/lib/module/ui/navigation/surfaceNavStack.js +62 -15
  190. package/lib/module/ui/navigation/surfaceNavStack.js.map +1 -1
  191. package/lib/module/ui/navigation/surfaceRegistry.js +84 -19
  192. package/lib/module/ui/navigation/surfaceRegistry.js.map +1 -1
  193. package/lib/module/ui/navigation/surfaces.js +138 -26
  194. package/lib/module/ui/navigation/surfaces.js.map +1 -1
  195. package/lib/module/ui/screens/AccountMembersScreen.js +218 -286
  196. package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
  197. package/lib/module/ui/screens/AccountSettingsScreen.js +92 -121
  198. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  199. package/lib/module/ui/screens/AccountVerificationScreen.js +25 -38
  200. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  201. package/lib/module/ui/screens/AppInfoScreen.js +220 -227
  202. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  203. package/lib/module/ui/screens/AvatarCropScreen.js +289 -291
  204. package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
  205. package/lib/module/ui/screens/ChangeAvatarScreen.js +298 -0
  206. package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -0
  207. package/lib/module/ui/screens/ConnectedAppsScreen.js +26 -40
  208. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  209. package/lib/module/ui/screens/CreateAccountScreen.js +139 -160
  210. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  211. package/lib/module/ui/screens/EditProfileFieldScreen.js +32 -38
  212. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  213. package/lib/module/ui/screens/EditProfileScreen.js +102 -110
  214. package/lib/module/ui/screens/EditProfileScreen.js.map +1 -1
  215. package/lib/module/ui/screens/FAQScreen.js +11 -15
  216. package/lib/module/ui/screens/FAQScreen.js.map +1 -1
  217. package/lib/module/ui/screens/FeedbackScreen.js +10 -17
  218. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  219. package/lib/module/ui/screens/FileManagementScreen.js +408 -631
  220. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  221. package/lib/module/ui/screens/HelpSupportScreen.js +64 -70
  222. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
  223. package/lib/module/ui/screens/HistoryViewScreen.js +65 -81
  224. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  225. package/lib/module/ui/screens/LanguageSelectorScreen.js +71 -99
  226. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  227. package/lib/module/ui/screens/LearnMoreUsernamesScreen.js +10 -15
  228. package/lib/module/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
  229. package/lib/module/ui/screens/LegalDocumentsScreen.js +78 -94
  230. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
  231. package/lib/module/ui/screens/ManageAccountScreen.js +102 -137
  232. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  233. package/lib/module/ui/screens/NotificationsScreen.js +71 -77
  234. package/lib/module/ui/screens/NotificationsScreen.js.map +1 -1
  235. package/lib/module/ui/screens/PaymentGatewayScreen.js +4 -9
  236. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  237. package/lib/module/ui/screens/PreferencesScreen.js +69 -75
  238. package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
  239. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +58 -93
  240. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  241. package/lib/module/ui/screens/PrivacySettingsScreen.js +293 -305
  242. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  243. package/lib/module/ui/screens/ProfileScreen.js +102 -116
  244. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  245. package/lib/module/ui/screens/SavesCollectionsScreen.js +40 -46
  246. package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
  247. package/lib/module/ui/screens/SearchSettingsScreen.js +46 -58
  248. package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
  249. package/lib/module/ui/screens/UserLinksScreen.js +26 -32
  250. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
  251. package/lib/module/ui/screens/UserListScreen.js +14 -24
  252. package/lib/module/ui/screens/UserListScreen.js.map +1 -1
  253. package/lib/module/ui/screens/WelcomeNewUserScreen.js +17 -49
  254. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  255. package/lib/module/ui/screens/fileManagement/FileLibraryError.js +59 -0
  256. package/lib/module/ui/screens/fileManagement/FileLibraryError.js.map +1 -0
  257. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +54 -42
  258. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  259. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +45 -81
  260. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  261. package/lib/module/ui/screens/trust/TrustAboutScreen.js +67 -75
  262. package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -1
  263. package/lib/module/ui/screens/trust/TrustCenterScreen.js +112 -139
  264. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  265. package/lib/module/ui/screens/trust/TrustFAQScreen.js +15 -21
  266. package/lib/module/ui/screens/trust/TrustFAQScreen.js.map +1 -1
  267. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +14 -26
  268. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  269. package/lib/module/ui/screens/trust/TrustRewardsScreen.js +69 -85
  270. package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  271. package/lib/module/ui/screens/trust/TrustRulesScreen.js +30 -37
  272. package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  273. package/lib/module/ui/stores/resetSessionScopedStores.js +8 -3
  274. package/lib/module/ui/stores/resetSessionScopedStores.js.map +1 -1
  275. package/lib/module/ui/utils/avatarUtils.js +11 -2
  276. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  277. package/lib/module/ui/utils/fileManagement.js +0 -78
  278. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  279. package/lib/typescript/commonjs/index.d.ts +2 -0
  280. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  281. package/lib/typescript/commonjs/ui/components/AvatarCameraBadge.d.ts +4 -0
  282. package/lib/typescript/commonjs/ui/components/AvatarCameraBadge.d.ts.map +1 -0
  283. package/lib/typescript/{module/ui/components/OxyAccountDialog.d.ts → commonjs/ui/components/OxyAccountDialogScreen.d.ts} +5 -4
  284. package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -0
  285. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +3 -3
  286. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  287. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +2 -2
  288. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  289. package/lib/typescript/commonjs/ui/components/ProfileSummaryCard.d.ts.map +1 -1
  290. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts +1 -1
  291. package/lib/typescript/commonjs/ui/components/SurfaceHeaderAction.d.ts +16 -0
  292. package/lib/typescript/commonjs/ui/components/SurfaceHeaderAction.d.ts.map +1 -0
  293. package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts +3 -4
  294. package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
  295. package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts +14 -4
  296. package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  297. package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts +17 -2
  298. package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  299. package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts +75 -13
  300. package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +1 -1
  301. package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts +10 -8
  302. package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  303. package/lib/typescript/commonjs/ui/components/surfaces/ActionSheetSurface.d.ts +38 -0
  304. package/lib/typescript/commonjs/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -0
  305. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  306. package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts +9 -0
  307. package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  308. package/lib/typescript/commonjs/ui/hooks/queries/useFileQueries.d.ts +50 -0
  309. package/lib/typescript/commonjs/ui/hooks/queries/useFileQueries.d.ts.map +1 -0
  310. package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts +17 -16
  311. package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts.map +1 -1
  312. package/lib/typescript/commonjs/ui/hooks/useReduceMotion.d.ts +14 -0
  313. package/lib/typescript/commonjs/ui/hooks/useReduceMotion.d.ts.map +1 -0
  314. package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts +31 -0
  315. package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts.map +1 -0
  316. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +1 -1
  317. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +27 -2
  318. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -1
  319. package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts +4 -5
  320. package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts.map +1 -1
  321. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  322. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  323. package/lib/typescript/commonjs/ui/navigation/surfaceBackBridge.d.ts +16 -0
  324. package/lib/typescript/commonjs/ui/navigation/surfaceBackBridge.d.ts.map +1 -0
  325. package/lib/typescript/commonjs/ui/navigation/surfaceNavStack.d.ts +21 -0
  326. package/lib/typescript/commonjs/ui/navigation/surfaceNavStack.d.ts.map +1 -1
  327. package/lib/typescript/commonjs/ui/navigation/surfaceRegistry.d.ts +101 -23
  328. package/lib/typescript/commonjs/ui/navigation/surfaceRegistry.d.ts.map +1 -1
  329. package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts +42 -11
  330. package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
  331. package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  332. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  333. package/lib/typescript/commonjs/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  334. package/lib/typescript/commonjs/ui/screens/AppInfoScreen.d.ts.map +1 -1
  335. package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts +18 -9
  336. package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts.map +1 -1
  337. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts +34 -0
  338. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -0
  339. package/lib/typescript/commonjs/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  340. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  341. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  342. package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -1
  343. package/lib/typescript/commonjs/ui/screens/FAQScreen.d.ts.map +1 -1
  344. package/lib/typescript/commonjs/ui/screens/FeedbackScreen.d.ts.map +1 -1
  345. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
  346. package/lib/typescript/commonjs/ui/screens/HelpSupportScreen.d.ts.map +1 -1
  347. package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  348. package/lib/typescript/commonjs/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  349. package/lib/typescript/commonjs/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
  350. package/lib/typescript/commonjs/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
  351. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
  352. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  353. package/lib/typescript/commonjs/ui/screens/NotificationsScreen.d.ts.map +1 -1
  354. package/lib/typescript/commonjs/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  355. package/lib/typescript/commonjs/ui/screens/PreferencesScreen.d.ts.map +1 -1
  356. package/lib/typescript/commonjs/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  357. package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  358. package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
  359. package/lib/typescript/commonjs/ui/screens/SavesCollectionsScreen.d.ts.map +1 -1
  360. package/lib/typescript/commonjs/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  361. package/lib/typescript/commonjs/ui/screens/UserLinksScreen.d.ts.map +1 -1
  362. package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts.map +1 -1
  363. package/lib/typescript/commonjs/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  364. package/lib/typescript/commonjs/ui/screens/fileManagement/FileLibraryError.d.ts +23 -0
  365. package/lib/typescript/commonjs/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -0
  366. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts +4 -0
  367. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  368. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +10 -2
  369. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  370. package/lib/typescript/commonjs/ui/screens/trust/TrustAboutScreen.d.ts.map +1 -1
  371. package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -1
  372. package/lib/typescript/commonjs/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -1
  373. package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  374. package/lib/typescript/commonjs/ui/screens/trust/TrustRewardsScreen.d.ts.map +1 -1
  375. package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
  376. package/lib/typescript/commonjs/ui/stores/resetSessionScopedStores.d.ts +8 -1
  377. package/lib/typescript/commonjs/ui/stores/resetSessionScopedStores.d.ts.map +1 -1
  378. package/lib/typescript/commonjs/ui/types/navigation.d.ts +10 -0
  379. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  380. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
  381. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +0 -48
  382. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
  383. package/lib/typescript/module/index.d.ts +2 -0
  384. package/lib/typescript/module/index.d.ts.map +1 -1
  385. package/lib/typescript/module/ui/components/AvatarCameraBadge.d.ts +4 -0
  386. package/lib/typescript/module/ui/components/AvatarCameraBadge.d.ts.map +1 -0
  387. package/lib/typescript/{commonjs/ui/components/OxyAccountDialog.d.ts → module/ui/components/OxyAccountDialogScreen.d.ts} +5 -4
  388. package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -0
  389. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +3 -3
  390. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  391. package/lib/typescript/module/ui/components/ProfileButton.d.ts +2 -2
  392. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  393. package/lib/typescript/module/ui/components/ProfileSummaryCard.d.ts.map +1 -1
  394. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts +1 -1
  395. package/lib/typescript/module/ui/components/SurfaceHeaderAction.d.ts +16 -0
  396. package/lib/typescript/module/ui/components/SurfaceHeaderAction.d.ts.map +1 -0
  397. package/lib/typescript/module/ui/components/SurfaceScreen.d.ts +3 -4
  398. package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
  399. package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts +14 -4
  400. package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  401. package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts +17 -2
  402. package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  403. package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts +75 -13
  404. package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +1 -1
  405. package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts +10 -8
  406. package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  407. package/lib/typescript/module/ui/components/surfaces/ActionSheetSurface.d.ts +38 -0
  408. package/lib/typescript/module/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -0
  409. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  410. package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts +9 -0
  411. package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  412. package/lib/typescript/module/ui/hooks/queries/useFileQueries.d.ts +50 -0
  413. package/lib/typescript/module/ui/hooks/queries/useFileQueries.d.ts.map +1 -0
  414. package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts +17 -16
  415. package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts.map +1 -1
  416. package/lib/typescript/module/ui/hooks/useReduceMotion.d.ts +14 -0
  417. package/lib/typescript/module/ui/hooks/useReduceMotion.d.ts.map +1 -0
  418. package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts +31 -0
  419. package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts.map +1 -0
  420. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +1 -1
  421. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +27 -2
  422. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -1
  423. package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts +4 -5
  424. package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts.map +1 -1
  425. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  426. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  427. package/lib/typescript/module/ui/navigation/surfaceBackBridge.d.ts +16 -0
  428. package/lib/typescript/module/ui/navigation/surfaceBackBridge.d.ts.map +1 -0
  429. package/lib/typescript/module/ui/navigation/surfaceNavStack.d.ts +21 -0
  430. package/lib/typescript/module/ui/navigation/surfaceNavStack.d.ts.map +1 -1
  431. package/lib/typescript/module/ui/navigation/surfaceRegistry.d.ts +101 -23
  432. package/lib/typescript/module/ui/navigation/surfaceRegistry.d.ts.map +1 -1
  433. package/lib/typescript/module/ui/navigation/surfaces.d.ts +42 -11
  434. package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
  435. package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  436. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  437. package/lib/typescript/module/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  438. package/lib/typescript/module/ui/screens/AppInfoScreen.d.ts.map +1 -1
  439. package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts +18 -9
  440. package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts.map +1 -1
  441. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts +34 -0
  442. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -0
  443. package/lib/typescript/module/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  444. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  445. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  446. package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -1
  447. package/lib/typescript/module/ui/screens/FAQScreen.d.ts.map +1 -1
  448. package/lib/typescript/module/ui/screens/FeedbackScreen.d.ts.map +1 -1
  449. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
  450. package/lib/typescript/module/ui/screens/HelpSupportScreen.d.ts.map +1 -1
  451. package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  452. package/lib/typescript/module/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  453. package/lib/typescript/module/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
  454. package/lib/typescript/module/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
  455. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +1 -1
  456. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  457. package/lib/typescript/module/ui/screens/NotificationsScreen.d.ts.map +1 -1
  458. package/lib/typescript/module/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  459. package/lib/typescript/module/ui/screens/PreferencesScreen.d.ts.map +1 -1
  460. package/lib/typescript/module/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  461. package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  462. package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
  463. package/lib/typescript/module/ui/screens/SavesCollectionsScreen.d.ts.map +1 -1
  464. package/lib/typescript/module/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  465. package/lib/typescript/module/ui/screens/UserLinksScreen.d.ts.map +1 -1
  466. package/lib/typescript/module/ui/screens/UserListScreen.d.ts.map +1 -1
  467. package/lib/typescript/module/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  468. package/lib/typescript/module/ui/screens/fileManagement/FileLibraryError.d.ts +23 -0
  469. package/lib/typescript/module/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -0
  470. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts +4 -0
  471. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  472. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +10 -2
  473. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  474. package/lib/typescript/module/ui/screens/trust/TrustAboutScreen.d.ts.map +1 -1
  475. package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -1
  476. package/lib/typescript/module/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -1
  477. package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  478. package/lib/typescript/module/ui/screens/trust/TrustRewardsScreen.d.ts.map +1 -1
  479. package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
  480. package/lib/typescript/module/ui/stores/resetSessionScopedStores.d.ts +8 -1
  481. package/lib/typescript/module/ui/stores/resetSessionScopedStores.d.ts.map +1 -1
  482. package/lib/typescript/module/ui/types/navigation.d.ts +10 -0
  483. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  484. package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
  485. package/lib/typescript/module/ui/utils/fileManagement.d.ts +0 -48
  486. package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
  487. package/package.json +9 -5
  488. package/src/index.ts +12 -5
  489. package/src/ui/components/AvatarCameraBadge.tsx +35 -0
  490. package/src/ui/components/{OxyAccountDialog.tsx → OxyAccountDialogScreen.tsx} +50 -116
  491. package/src/ui/components/OxyAuthChooser.tsx +743 -86
  492. package/src/ui/components/OxyConsentScreen.tsx +1 -1
  493. package/src/ui/components/ProfileButton.tsx +18 -3
  494. package/src/ui/components/ProfileSummaryCard.tsx +2 -25
  495. package/src/ui/components/RequireOxyAuth.tsx +1 -1
  496. package/src/ui/components/SurfaceHeaderAction.tsx +60 -0
  497. package/src/ui/components/SurfaceScreen.tsx +108 -14
  498. package/src/ui/components/fileManagement/FileDetailsModal.tsx +50 -36
  499. package/src/ui/components/modals/DeleteAccountModal.tsx +47 -26
  500. package/src/ui/components/oxyAuthChooserStyles.ts +78 -16
  501. package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +158 -84
  502. package/src/ui/components/photogrid/__tests__/chunkPhotos.test.ts +51 -0
  503. package/src/ui/components/surfaces/ActionSheetSurface.tsx +98 -0
  504. package/src/ui/context/OxyContext.tsx +79 -25
  505. package/src/ui/hooks/queries/queryKeys.ts +15 -0
  506. package/src/ui/hooks/queries/useFileQueries.ts +162 -0
  507. package/src/ui/hooks/useAvatarPicker.ts +46 -74
  508. package/src/ui/hooks/useReduceMotion.ts +58 -0
  509. package/src/ui/hooks/useSurfaceHeader.ts +57 -0
  510. package/src/ui/hooks/useSwitchableAccounts.ts +1 -1
  511. package/src/ui/navigation/__tests__/getSurfaceConfig.test.ts +55 -7
  512. package/src/ui/navigation/__tests__/surfaceNavStack.test.ts +149 -0
  513. package/src/ui/navigation/accountDialogManager.ts +42 -2
  514. package/src/ui/navigation/bottomSheetManager.ts +6 -7
  515. package/src/ui/navigation/routes.ts +6 -4
  516. package/src/ui/navigation/surfaceBackBridge.ts +69 -0
  517. package/src/ui/navigation/surfaceNavStack.ts +72 -9
  518. package/src/ui/navigation/surfaceRegistry.ts +164 -24
  519. package/src/ui/navigation/surfaces.ts +137 -30
  520. package/src/ui/screens/AccountMembersScreen.tsx +39 -100
  521. package/src/ui/screens/AccountSettingsScreen.tsx +32 -54
  522. package/src/ui/screens/AccountVerificationScreen.tsx +23 -51
  523. package/src/ui/screens/AppInfoScreen.tsx +6 -13
  524. package/src/ui/screens/AvatarCropScreen.tsx +340 -311
  525. package/src/ui/screens/ChangeAvatarScreen.tsx +359 -0
  526. package/src/ui/screens/ConnectedAppsScreen.tsx +39 -60
  527. package/src/ui/screens/CreateAccountScreen.tsx +13 -39
  528. package/src/ui/screens/EditProfileFieldScreen.tsx +30 -48
  529. package/src/ui/screens/EditProfileScreen.tsx +7 -16
  530. package/src/ui/screens/FAQScreen.tsx +9 -13
  531. package/src/ui/screens/FeedbackScreen.tsx +8 -22
  532. package/src/ui/screens/FileManagementScreen.tsx +370 -610
  533. package/src/ui/screens/HelpSupportScreen.tsx +7 -13
  534. package/src/ui/screens/HistoryViewScreen.tsx +26 -31
  535. package/src/ui/screens/LanguageSelectorScreen.tsx +12 -46
  536. package/src/ui/screens/LearnMoreUsernamesScreen.tsx +8 -16
  537. package/src/ui/screens/LegalDocumentsScreen.tsx +7 -22
  538. package/src/ui/screens/ManageAccountScreen.tsx +96 -165
  539. package/src/ui/screens/NotificationsScreen.tsx +7 -13
  540. package/src/ui/screens/PaymentGatewayScreen.tsx +3 -10
  541. package/src/ui/screens/PreferencesScreen.tsx +7 -13
  542. package/src/ui/screens/PremiumSubscriptionScreen.tsx +52 -90
  543. package/src/ui/screens/PrivacySettingsScreen.tsx +9 -21
  544. package/src/ui/screens/ProfileScreen.tsx +9 -11
  545. package/src/ui/screens/SavesCollectionsScreen.tsx +7 -13
  546. package/src/ui/screens/SearchSettingsScreen.tsx +9 -21
  547. package/src/ui/screens/UserLinksScreen.tsx +7 -14
  548. package/src/ui/screens/UserListScreen.tsx +2 -5
  549. package/src/ui/screens/WelcomeNewUserScreen.tsx +18 -48
  550. package/src/ui/screens/fileManagement/FileLibraryError.tsx +56 -0
  551. package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +56 -46
  552. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +55 -71
  553. package/src/ui/screens/trust/TrustAboutScreen.tsx +10 -17
  554. package/src/ui/screens/trust/TrustCenterScreen.tsx +9 -28
  555. package/src/ui/screens/trust/TrustFAQScreen.tsx +15 -18
  556. package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +3 -5
  557. package/src/ui/screens/trust/TrustRewardsScreen.tsx +11 -26
  558. package/src/ui/screens/trust/TrustRulesScreen.tsx +10 -13
  559. package/src/ui/stores/resetSessionScopedStores.ts +8 -3
  560. package/src/ui/types/navigation.ts +10 -0
  561. package/src/ui/utils/avatarUtils.ts +11 -2
  562. package/src/ui/utils/fileManagement.ts +1 -101
  563. package/lib/commonjs/ui/components/Header.js +0 -305
  564. package/lib/commonjs/ui/components/Header.js.map +0 -1
  565. package/lib/commonjs/ui/components/OxyAccountDialog.js.map +0 -1
  566. package/lib/commonjs/ui/stores/fileStore.js +0 -153
  567. package/lib/commonjs/ui/stores/fileStore.js.map +0 -1
  568. package/lib/module/ui/components/Header.js +0 -299
  569. package/lib/module/ui/components/Header.js.map +0 -1
  570. package/lib/module/ui/components/OxyAccountDialog.js.map +0 -1
  571. package/lib/module/ui/stores/fileStore.js +0 -145
  572. package/lib/module/ui/stores/fileStore.js.map +0 -1
  573. package/lib/typescript/commonjs/ui/components/Header.d.ts +0 -33
  574. package/lib/typescript/commonjs/ui/components/Header.d.ts.map +0 -1
  575. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +0 -1
  576. package/lib/typescript/commonjs/ui/stores/fileStore.d.ts +0 -31
  577. package/lib/typescript/commonjs/ui/stores/fileStore.d.ts.map +0 -1
  578. package/lib/typescript/module/ui/components/Header.d.ts +0 -33
  579. package/lib/typescript/module/ui/components/Header.d.ts.map +0 -1
  580. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +0 -1
  581. package/lib/typescript/module/ui/stores/fileStore.d.ts +0 -31
  582. package/lib/typescript/module/ui/stores/fileStore.d.ts.map +0 -1
  583. package/src/ui/components/Header.tsx +0 -224
  584. package/src/ui/stores/fileStore.ts +0 -118
@@ -5,11 +5,17 @@
5
5
  * but before the avatar is uploaded. Inspired by iOS Photos, Google Photos and
6
6
  * Instagram crop tools.
7
7
  *
8
+ * Reached ONLY by navigating within a `ChangeAvatar` surface, so the panel
9
+ * morphs from the source list into the editor. The surface owns the chrome: this
10
+ * screen declares its title and its "Use photo" action through `useSurfaceHeader`
11
+ * and renders no bar of its own — the nav bar's back arrow is Cancel, and it
12
+ * returns to the source list.
13
+ *
8
14
  * Architecture:
9
- * - Full-bleed black canvas, independent of theme, so photos always read well.
10
- * - Translucent top bar with Cancel / Title / Done (primary CTA).
11
- * - Circular viewport with white ring, outer 50% black mask, and a 3x3
12
- * rule-of-thirds grid that appears during gestures and fades after 800ms.
15
+ * - A dark crop stage, independent of theme, so photos always read well, inset
16
+ * inside the surface's own themed panel.
17
+ * - Circular viewport with white ring and a 3x3 rule-of-thirds grid that
18
+ * appears during gestures and fades after 800ms.
13
19
  * - Floating zoom chip during pinch.
14
20
  * - Pan + pinch via Gesture Handler, transform driven by Reanimated.
15
21
  * - Reduced-motion aware entrance animation.
@@ -21,13 +27,12 @@
21
27
  */
22
28
 
23
29
  import type React from 'react';
24
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
30
+ import { useCallback, useMemo, useRef, useState } from 'react';
25
31
  import {
26
32
  View,
27
33
  Text,
28
34
  StyleSheet,
29
35
  ActivityIndicator,
30
- TouchableOpacity,
31
36
  Image,
32
37
  Platform,
33
38
  AccessibilityInfo,
@@ -43,10 +48,14 @@ import Animated, {
43
48
  withSpring,
44
49
  withTiming,
45
50
  } from 'react-native-reanimated';
46
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
51
+ import { useQuery, keepPreviousData } from '@tanstack/react-query';
47
52
  import { useTheme } from '@oxyhq/bloom/theme';
53
+ import { Button } from '@oxyhq/bloom/button';
48
54
  import { logger } from '@oxyhq/core';
55
+ import { useOxy } from '../context/OxyContext';
49
56
  import { useI18n } from '../hooks/useI18n';
57
+ import { useReduceMotion } from '../hooks/useReduceMotion';
58
+ import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
50
59
  import { toast } from '@oxyhq/bloom';
51
60
  import type { BaseScreenProps } from '../types/navigation';
52
61
 
@@ -66,16 +75,19 @@ export interface AvatarCropResult {
66
75
  }
67
76
 
68
77
  export interface AvatarCropScreenProps extends BaseScreenProps {
69
- /** URI of the source image to crop. */
78
+ /** URI of the source image to crop (device / camera sources pass this). */
70
79
  imageUri?: string;
80
+ /**
81
+ * ID of an existing Oxy file to crop (the "My Oxy files" source passes this
82
+ * instead of a URL). Its download URL is usually private, so it is resolved
83
+ * HERE — showing this screen's own loading state on the dark canvas — rather
84
+ * than in the source list, which would otherwise linger for the round-trip.
85
+ */
86
+ imageFileId?: string;
71
87
  /** Natural width of the source image (optional — measured if absent). */
72
88
  sourceWidth?: number;
73
89
  /** Natural height of the source image (optional — measured if absent). */
74
90
  sourceHeight?: number;
75
- /** Called with the cropped file once the user confirms. */
76
- onConfirm?: (result: AvatarCropResult) => void | Promise<void>;
77
- /** Called if the user cancels without confirming. */
78
- onCancel?: () => void;
79
91
  }
80
92
 
81
93
  /** Side length (in dp) of the crop viewport. The output is always 512x512px. */
@@ -83,14 +95,20 @@ const VIEWPORT_SIZE = 320;
83
95
  const OUTPUT_SIZE = 512;
84
96
  const MIN_SCALE = 1;
85
97
  const MAX_SCALE = 4;
98
+ /** Zoom increment per +/- button press (web, where there is no pinch). */
99
+ const ZOOM_STEP = 0.5;
100
+ /** Tween duration for a button-driven zoom (collapses to 0 under reduce-motion). */
101
+ const ZOOM_BUTTON_DURATION_MS = 180;
86
102
  /** Duration (ms) that the rule-of-thirds grid lingers after a gesture ends. */
87
103
  const GRID_FADE_DELAY_MS = 800;
88
104
  const GRID_FADE_DURATION_MS = 220;
89
105
  /** Duration the zoom chip stays visible after a pinch ends. */
90
106
  const ZOOM_CHIP_FADE_DELAY_MS = 600;
91
107
  const ZOOM_CHIP_FADE_DURATION_MS = 200;
92
- /** Backdrop color is fixed independent of theme so the photo always reads well. */
108
+ /** Stage color is fixed independent of theme so the photo always reads well. */
93
109
  const CANVAS_BG = '#000000';
110
+ /** Corner radius of the dark stage block inside the surface's themed panel. */
111
+ const STAGE_RADIUS = 24;
94
112
  const RING_COLOR = '#ffffff';
95
113
  const RING_WIDTH = 2;
96
114
  const REMOTE_HTTP_URI_PATTERN = /^https?:\/\//i;
@@ -123,6 +141,28 @@ interface ImageNaturalSize {
123
141
  height: number;
124
142
  }
125
143
 
144
+ /**
145
+ * Measure an image's intrinsic size via `Image.getSize`, wrapped as a Promise so
146
+ * it can be a React Query `queryFn` (no component state / effects). Rejects on a
147
+ * platform failure or degenerate dimensions so the query surfaces `isError`.
148
+ * Mirrors the `measurePhotoDimensions` helper the photo grid uses.
149
+ */
150
+ function measureImageSize(uri: string): Promise<ImageNaturalSize> {
151
+ return new Promise<ImageNaturalSize>((resolve, reject) => {
152
+ Image.getSize(
153
+ uri,
154
+ (width, height) => {
155
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
156
+ reject(new Error('Image reported invalid dimensions'));
157
+ return;
158
+ }
159
+ resolve({ width, height });
160
+ },
161
+ () => reject(new Error('Image measurement failed')),
162
+ );
163
+ });
164
+ }
165
+
126
166
  /**
127
167
  * Lazy-loaded reference to the expo-image-manipulator module. The module is an
128
168
  * optional peer dep, so we resolve it on demand and surface a clean error
@@ -296,28 +336,84 @@ async function hapticSelection(): Promise<void> {
296
336
  }
297
337
 
298
338
  const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
339
+ dismiss,
299
340
  goBack,
300
- onClose,
301
341
  imageUri,
342
+ imageFileId,
302
343
  sourceWidth,
303
344
  sourceHeight,
304
- onConfirm,
305
- onCancel,
306
345
  }) => {
307
346
  const theme = useTheme();
308
347
  const { t } = useI18n();
309
- const insets = useSafeAreaInsets();
348
+ const { oxyServices } = useOxy();
349
+ // Reactive OS reduce-motion preference (useSyncExternalStore, no effect). Pins
350
+ // the entrance spring off. Declared before `entrance` so its initial rest value
351
+ // is correct under reduce-motion.
352
+ const reduceMotion = useReduceMotion();
353
+
354
+ // The "My Oxy files" source hands us a file ID instead of a ready URL. Its
355
+ // download URL is usually private, so it must be resolved through the SDK
356
+ // (which throws on failure). Resolving it HERE — not in the source list — is
357
+ // what keeps the list from lingering on screen for the round-trip: this screen
358
+ // shows its own spinner on the dark canvas while the URL resolves. A React
359
+ // Query read (not an effect) so it stays declarative and cached.
360
+ const shouldResolveFile = !imageUri && !!imageFileId;
361
+ const sourceQuery = useQuery({
362
+ queryKey: ['avatarCropSource', imageFileId],
363
+ enabled: shouldResolveFile,
364
+ staleTime: Number.POSITIVE_INFINITY,
365
+ retry: false,
366
+ queryFn: async () => {
367
+ if (!imageFileId) {
368
+ throw new Error('No file id to resolve for cropping');
369
+ }
370
+ const resolved = await oxyServices.assetGetUrl(imageFileId);
371
+ if (!resolved?.url) {
372
+ throw new Error('No download URL returned for the selected image');
373
+ }
374
+ return resolved.url;
375
+ },
376
+ });
377
+
378
+ // The URL the cropper actually loads: the passed `imageUri`, else the URL the
379
+ // file-ID query resolved. While it resolves, `effectiveUri` is undefined and
380
+ // the stage shows its own spinner (never the empty state).
381
+ const effectiveUri = imageUri ?? sourceQuery.data;
382
+ const isResolvingSource = shouldResolveFile && !sourceQuery.data && !sourceQuery.isError;
383
+ const sourceErrorMessage = sourceQuery.isError
384
+ ? t('changeAvatar.errors.loadImageFailed') || 'Could not load the selected image'
385
+ : null;
386
+
387
+ // Natural size of the source image. Known up front (device/camera sources pass
388
+ // the dimensions) OR measured lazily from `effectiveUri` via a React Query read
389
+ // (no effect) — the same Image.getSize→useQuery pattern the photo grid uses.
390
+ const hasPassedDimensions = !!(sourceWidth && sourceHeight);
391
+ const measureQuery = useQuery({
392
+ queryKey: ['avatarCropMeasure', effectiveUri],
393
+ enabled: !!effectiveUri && !hasPassedDimensions,
394
+ queryFn: () => {
395
+ if (!effectiveUri) {
396
+ throw new Error('No image to measure');
397
+ }
398
+ return measureImageSize(effectiveUri);
399
+ },
400
+ staleTime: Number.POSITIVE_INFINITY,
401
+ retry: false,
402
+ placeholderData: keepPreviousData,
403
+ });
404
+ const naturalSize = useMemo<ImageNaturalSize | null>(() => {
405
+ if (sourceWidth && sourceHeight) return { width: sourceWidth, height: sourceHeight };
406
+ return measureQuery.data ?? null;
407
+ }, [sourceWidth, sourceHeight, measureQuery.data]);
408
+ /** Measurement failed (bad dimensions / getSize error) — surfaced via the empty UI. */
409
+ const measureErrorMessage = measureQuery.isError
410
+ ? t('editProfile.toasts.cropMeasureFailed') || 'Could not measure the image'
411
+ : null;
310
412
 
311
- // Natural size of the source image. May be known up front (passed in) or
312
- // measured lazily via Image.getSize once the image loads.
313
- const [naturalSize, setNaturalSize] = useState<ImageNaturalSize | null>(
314
- sourceWidth && sourceHeight ? { width: sourceWidth, height: sourceHeight } : null,
315
- );
316
413
  const [isProcessing, setIsProcessing] = useState(false);
317
414
  const [zoomLabel, setZoomLabel] = useState('1.0');
318
415
  /** True when scale != MIN_SCALE OR translate != 0 — used to reveal the reset link. */
319
416
  const [isModified, setIsModified] = useState(false);
320
- const [reduceMotion, setReduceMotion] = useState(false);
321
417
 
322
418
  // Shared values for the active gesture transform.
323
419
  const scale = useSharedValue(MIN_SCALE);
@@ -361,112 +457,24 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
361
457
  return { width: VIEWPORT_SIZE, height: VIEWPORT_SIZE / ratio };
362
458
  }, [naturalSize]);
363
459
 
364
- // Track in-flight measurement to avoid duplicate Image.getSize calls when
365
- // the component re-renders before the previous getSize callback fires.
366
- const measuringUriRef = useRef<string | null>(null);
367
- /** Failure state for measurement, surfaced to the user via the empty UI. */
368
- const [measureError, setMeasureError] = useState<string | null>(null);
369
-
370
- const handleImageMeasured = useCallback(
371
- (uri: string) => {
372
- if (measuringUriRef.current === uri) return;
373
- measuringUriRef.current = uri;
374
- // `logger.debug` is dev-gated upstream (no-op in production).
375
- // We deliberately don't log the full file URI in any production
376
- // path — only in debug builds — to avoid leaking on-device file
377
- // paths into logcat / Sentry breadcrumbs.
378
- logger.debug('Measuring image', { component: LOG_COMPONENT });
379
- Image.getSize(
380
- uri,
381
- (w, h) => {
382
- logger.debug('Image measured', { component: LOG_COMPONENT }, { width: w, height: h });
383
- if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
384
- const message = t('editProfile.toasts.cropMeasureFailed') ||
385
- 'Could not measure the image';
386
- setMeasureError(message);
387
- toast.error(message);
388
- measuringUriRef.current = null;
389
- return;
390
- }
391
- setMeasureError(null);
392
- setNaturalSize({ width: w, height: h });
393
- },
394
- () => {
395
- logger.error(
396
- 'Image.getSize failed',
397
- new Error('Image measurement failed'),
398
- { component: LOG_COMPONENT },
399
- );
400
- const message = t('editProfile.toasts.cropMeasureFailed') ||
401
- 'Could not measure the image';
402
- setMeasureError(message);
403
- toast.error(message);
404
- measuringUriRef.current = null;
405
- },
406
- );
460
+ // Entrance pulse, driven from the crop frame's ref callback (fires on mount)
461
+ // rather than an effect the media-cleanup `startEntrance` pattern. Reduce
462
+ // motion pins the frame at rest; otherwise it springs in from 0.95→1. The
463
+ // callback depends on `reduceMotion`, so if that preference resolves/flips the
464
+ // callback re-runs with the node and re-pins (snap to 1) — matching the old
465
+ // effect that snapped the entrance to rest when reduce-motion turned on.
466
+ const startEntrance = useCallback(
467
+ (node: unknown): void => {
468
+ if (!node) return; // unmount
469
+ if (reduceMotion) {
470
+ entrance.value = 1;
471
+ return;
472
+ }
473
+ entrance.value = withSpring(1, { damping: 14, stiffness: 180, mass: 0.9 });
407
474
  },
408
- [t],
475
+ [entrance, reduceMotion],
409
476
  );
410
477
 
411
- // Kick off measurement once per imageUri. Using useEffect (not a render-body
412
- // side effect) so the call is scheduled exactly when the URI changes,
413
- // rather than being re-fired on every parent re-render.
414
- useEffect(() => {
415
- if (!imageUri) return;
416
- if (sourceWidth && sourceHeight) return;
417
- if (naturalSize) return;
418
- handleImageMeasured(imageUri);
419
- }, [handleImageMeasured, imageUri, naturalSize, sourceHeight, sourceWidth]);
420
-
421
- // Dev-only one-time mount breadcrumb. `logger.debug` is dev-gated so
422
- // this is a no-op in production releases; we additionally avoid logging
423
- // the full `imageUri` to prevent leaking on-device file paths into any
424
- // breadcrumb sink that picks up debug output.
425
- useEffect(() => {
426
- logger.debug(
427
- 'mount',
428
- { component: LOG_COMPONENT },
429
- {
430
- hasImageUri: !!imageUri,
431
- hasSourceDimensions: !!(sourceWidth && sourceHeight),
432
- },
433
- );
434
- }, [imageUri, sourceHeight, sourceWidth]);
435
-
436
- // Detect reduce-motion preference once on mount + subscribe to changes.
437
- useEffect(() => {
438
- let cancelled = false;
439
- AccessibilityInfo.isReduceMotionEnabled()
440
- .then((enabled) => {
441
- if (cancelled) return;
442
- setReduceMotion(enabled);
443
- if (enabled) {
444
- entrance.value = 1;
445
- }
446
- })
447
- .catch(() => {
448
- // Best-effort — fall back to motion enabled.
449
- });
450
- const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', (enabled) => {
451
- setReduceMotion(enabled);
452
- if (enabled) entrance.value = 1;
453
- });
454
- return () => {
455
- cancelled = true;
456
- sub.remove();
457
- };
458
- }, [entrance]);
459
-
460
- // Play the entrance pulse exactly once when motion is allowed.
461
- useEffect(() => {
462
- if (reduceMotion) return;
463
- entrance.value = withSpring(1, {
464
- damping: 14,
465
- stiffness: 180,
466
- mass: 0.9,
467
- });
468
- }, [entrance, reduceMotion]);
469
-
470
478
  const commitTransform = useCallback((s: number, tx: number, ty: number) => {
471
479
  committedScale.current = s;
472
480
  committedTranslateX.current = tx;
@@ -635,17 +643,29 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
635
643
  [panGesture, pinchGesture],
636
644
  );
637
645
 
638
- const imageAnimatedStyle = useAnimatedStyle(() => ({
639
- transform: [
640
- { translateX: translateX.value },
641
- { translateY: translateY.value },
642
- { scale: scale.value },
643
- ],
644
- }));
646
+ // Deps list every shared value the mapper reads so it re-runs on web (RN-Web
647
+ // has no worklets plugin — a mapper omitting its driver SVs freezes at the
648
+ // first frame, which would leave the button-driven zoom stuck). Native
649
+ // auto-tracks and ignores the extra deps, so this is safe cross-platform.
650
+ const imageAnimatedStyle = useAnimatedStyle(
651
+ () => ({
652
+ transform: [
653
+ { translateX: translateX.value },
654
+ { translateY: translateY.value },
655
+ { scale: scale.value },
656
+ ],
657
+ }),
658
+ [translateX, translateY, scale],
659
+ );
645
660
 
646
- const cropFrameAnimatedStyle = useAnimatedStyle(() => ({
647
- transform: [{ scale: entrance.value }],
648
- }));
661
+ // `[entrance]` in deps so the ref-callback-driven entrance spring ticks on
662
+ // RN-Web (no worklets plugin); native auto-tracks and ignores the extra dep.
663
+ const cropFrameAnimatedStyle = useAnimatedStyle(
664
+ () => ({
665
+ transform: [{ scale: entrance.value }],
666
+ }),
667
+ [entrance],
668
+ );
649
669
 
650
670
  const gridAnimatedStyle = useAnimatedStyle(() => ({
651
671
  opacity: gridOpacity.value,
@@ -667,6 +687,61 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
667
687
  );
668
688
  }, [commitTransform, reduceMotion, scale, t, translateX, translateY]);
669
689
 
690
+ /**
691
+ * Web zoom controls. A desktop pointer has no pinch, so the +/- buttons drive
692
+ * the SAME `scale` shared value the pinch gesture does — clamped to the same
693
+ * bounds, with translation re-clamped through the shared {@link clampTranslation}
694
+ * helper so the image never leaves the viewport. Native keeps pinch (the
695
+ * buttons are hidden there). Reduced motion collapses the tween to an instant
696
+ * set. Reads the committed transform (refs) at press time, never during render.
697
+ */
698
+ const applyZoom = useCallback(
699
+ (direction: 1 | -1) => {
700
+ if (!baseFit || isProcessing) return;
701
+ const current = committedScale.current;
702
+ const target = Math.min(
703
+ MAX_SCALE,
704
+ Math.max(MIN_SCALE, current + direction * ZOOM_STEP),
705
+ );
706
+ if (Math.abs(target - current) < 0.001) {
707
+ // Already at the bound — a soft selection tick, no transform change.
708
+ void hapticSelection();
709
+ return;
710
+ }
711
+ const clamped = clampTranslation(
712
+ committedTranslateX.current,
713
+ committedTranslateY.current,
714
+ target,
715
+ baseFit.width,
716
+ baseFit.height,
717
+ );
718
+ const duration = reduceMotion ? 0 : ZOOM_BUTTON_DURATION_MS;
719
+ scale.value = withTiming(target, { duration });
720
+ translateX.value = withTiming(clamped.tx, { duration });
721
+ translateY.value = withTiming(clamped.ty, { duration });
722
+ commitTransform(target, clamped.tx, clamped.ty);
723
+ void hapticImpact('light');
724
+ // Mirror the pinch's transient chrome so the buttons feel identical.
725
+ showGrid();
726
+ hideGrid();
727
+ showZoomChip();
728
+ hideZoomChip();
729
+ },
730
+ [
731
+ baseFit,
732
+ commitTransform,
733
+ hideGrid,
734
+ hideZoomChip,
735
+ isProcessing,
736
+ reduceMotion,
737
+ scale,
738
+ showGrid,
739
+ showZoomChip,
740
+ translateX,
741
+ translateY,
742
+ ],
743
+ );
744
+
670
745
  /**
671
746
  * Convert the on-screen transform into pixel-space crop coordinates
672
747
  * relative to the source image, then invoke expo-image-manipulator to
@@ -686,7 +761,7 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
686
761
  committedTranslateY: committedTranslateY.current,
687
762
  },
688
763
  );
689
- if (!imageUri || !baseFit || !naturalSize) {
764
+ if (!effectiveUri || !baseFit || !naturalSize) {
690
765
  toast.error(
691
766
  t('editProfile.toasts.cropNotReady') || 'Image not ready yet',
692
767
  );
@@ -738,7 +813,7 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
738
813
  },
739
814
  );
740
815
 
741
- const preparedImage = await prepareImageForManipulator(imageUri);
816
+ const preparedImage = await prepareImageForManipulator(effectiveUri);
742
817
  preparedImageCleanup = preparedImage.cleanup;
743
818
  const result = await manipulateAsync(
744
819
  preparedImage.uri,
@@ -765,16 +840,15 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
765
840
 
766
841
  void hapticNotification('success');
767
842
 
768
- await onConfirm?.({
843
+ // Resolve the surface's present() promise with the cropped JPEG and
844
+ // close it. The awaiting caller (`useAvatarPicker`) handles the
845
+ // upload + any success toast.
846
+ dismiss?.({
769
847
  uri: result.uri,
770
848
  width: result.width,
771
849
  height: result.height,
772
850
  mime: 'image/jpeg',
773
851
  });
774
-
775
- // Close the sheet on success. The caller is responsible for any
776
- // success toast (uploads typically toast their own outcome).
777
- onClose?.();
778
852
  } catch (err) {
779
853
  const normalizedError = normalizeCropError(err);
780
854
  logger.error('handleConfirm failed', normalizedError, { component: LOG_COMPONENT });
@@ -786,87 +860,59 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
786
860
  preparedImageCleanup?.();
787
861
  setIsProcessing(false);
788
862
  }
789
- }, [baseFit, imageUri, naturalSize, onClose, onConfirm, t]);
863
+ }, [baseFit, effectiveUri, naturalSize, dismiss, t]);
790
864
 
791
- const handleCancel = useCallback(() => {
792
- onCancel?.();
793
- goBack?.();
794
- }, [goBack, onCancel]);
865
+ /**
866
+ * The surface's primary action, mounted into the Dialog nav bar's right slot
867
+ * (which is why it must stay referentially stable). While processing it keeps
868
+ * its width and shows a spinner; the a11y label carries the "Saving…" state
869
+ * the visible label can no longer show.
870
+ */
871
+ const doneAction = useMemo(
872
+ () => (
873
+ <Button
874
+ variant="primary"
875
+ size="small"
876
+ onPress={() => void handleConfirm()}
877
+ disabled={isProcessing || !baseFit}
878
+ loading={isProcessing}
879
+ accessibilityLabel={
880
+ isProcessing ? t('editProfile.crop.saving') : t('editProfile.crop.confirm')
881
+ }
882
+ >
883
+ {t('editProfile.crop.confirm')}
884
+ </Button>
885
+ ),
886
+ [baseFit, handleConfirm, isProcessing, t],
887
+ );
795
888
 
796
- const topInset = Platform.OS === 'ios' ? Math.max(insets.top, 12) : Math.max(insets.top, 16);
797
- const bottomInset = Math.max(insets.bottom, 16);
889
+ // No bar of our own: the surface owns the chrome. `goBack` pops back to the
890
+ // ChangeAvatar source list (and dismisses the surface if this is somehow the
891
+ // root frame), which is exactly Cancel.
892
+ useSurfaceHeader({
893
+ title: t('editProfile.crop.title'),
894
+ largeTitle: false,
895
+ right: doneAction,
896
+ onBack: goBack,
897
+ });
798
898
 
799
899
  const styles = useMemo(
800
900
  () =>
801
901
  StyleSheet.create({
802
902
  container: {
803
- flex: 1,
804
- backgroundColor: CANVAS_BG,
805
- },
806
- topBar: {
807
- paddingTop: topInset,
808
- paddingHorizontal: 12,
809
- paddingBottom: 10,
810
- flexDirection: 'row',
811
- alignItems: 'center',
812
- justifyContent: 'space-between',
813
- backgroundColor: 'rgba(0,0,0,0.6)',
814
- zIndex: 10,
815
- },
816
- topBarTitleWrap: {
817
- flex: 1,
818
- alignItems: 'center',
819
- justifyContent: 'center',
820
- paddingHorizontal: 4,
821
- },
822
- topBarTitle: {
823
- color: '#ffffff',
824
- fontSize: 17,
825
- letterSpacing: -0.2,
826
- ...(Platform.OS === 'web' ? { fontWeight: '600' as const } : null),
827
- },
828
- cancelBtn: {
829
- minWidth: 64,
830
- paddingHorizontal: 10,
831
- paddingVertical: 8,
832
- borderRadius: 18,
833
- alignItems: 'flex-start',
834
- justifyContent: 'center',
835
- },
836
- cancelLabel: {
837
- color: '#ffffff',
838
- fontSize: 15,
839
- opacity: 0.85,
840
- },
841
- doneBtn: {
842
- minWidth: 76,
843
- paddingHorizontal: 14,
844
- paddingVertical: 8,
845
- borderRadius: 18,
846
- alignItems: 'center',
847
- justifyContent: 'center',
848
- backgroundColor: theme.colors.primary,
849
- },
850
- doneBtnDisabled: {
851
- opacity: 0.5,
852
- },
853
- doneLabel: {
854
- color: '#ffffff',
855
- fontSize: 15,
856
- letterSpacing: -0.1,
857
- ...(Platform.OS === 'web' ? { fontWeight: '600' as const } : null),
858
- },
859
- doneLabelLoading: {
860
- flexDirection: 'row',
861
- alignItems: 'center',
862
- justifyContent: 'center',
863
- gap: 8,
903
+ backgroundColor: theme.colors.background,
864
904
  },
865
905
  stage: {
866
- flex: 1,
906
+ backgroundColor: CANVAS_BG,
907
+ borderRadius: STAGE_RADIUS,
908
+ overflow: 'hidden',
909
+ // Top padding clears the zoom chip, which floats 44dp above
910
+ // the crop frame and would otherwise be clipped by `overflow`.
911
+ paddingTop: 60,
912
+ paddingBottom: 28,
913
+ paddingHorizontal: 16,
867
914
  alignItems: 'center',
868
915
  justifyContent: 'center',
869
- paddingHorizontal: 16,
870
916
  },
871
917
  cropFrame: {
872
918
  width: VIEWPORT_SIZE,
@@ -951,17 +997,14 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
951
997
  letterSpacing: 0.2,
952
998
  },
953
999
  helperBlock: {
954
- paddingHorizontal: 24,
955
- paddingTop: 24,
956
- paddingBottom: bottomInset,
1000
+ paddingTop: 16,
957
1001
  alignItems: 'center',
958
- justifyContent: 'flex-end',
959
- gap: 10,
1002
+ gap: 8,
960
1003
  },
961
1004
  helper: {
962
1005
  fontSize: 13,
963
1006
  lineHeight: 18,
964
- color: 'rgba(255,255,255,0.6)',
1007
+ color: theme.colors.textSecondary,
965
1008
  textAlign: 'center',
966
1009
  maxWidth: 320,
967
1010
  },
@@ -971,55 +1014,50 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
971
1014
  },
972
1015
  resetLinkText: {
973
1016
  fontSize: 13,
974
- color: '#ffffff',
975
- opacity: 0.85,
1017
+ color: theme.colors.primary,
976
1018
  textDecorationLine: 'underline',
977
1019
  },
978
1020
  emptyState: {
979
- flex: 1,
980
1021
  alignItems: 'center',
981
1022
  justifyContent: 'center',
982
- padding: 24,
1023
+ paddingVertical: 48,
983
1024
  },
984
1025
  emptyLabel: {
985
1026
  fontSize: 14,
986
- color: 'rgba(255,255,255,0.7)',
1027
+ color: theme.colors.textSecondary,
987
1028
  textAlign: 'center',
988
1029
  },
989
1030
  }),
990
- [baseFit, bottomInset, theme, topInset],
1031
+ [baseFit, theme],
991
1032
  );
992
1033
 
993
1034
  // Reset link reveal — only show when the image is not at the default
994
1035
  // transform. Use derived display state to avoid mounting/unmounting jank.
995
1036
  const resetLinkOpacity = isModified ? 1 : 0;
996
1037
 
997
- // No image supplied OR measurement failed render a minimal empty state.
998
- if (!imageUri || measureError) {
999
- const emptyMessage = !imageUri
1000
- ? (t('editProfile.crop.noImage') || 'No image to crop')
1001
- : measureError;
1038
+ // Zoom-button state is derived from `zoomLabel` (reactive state, set on every
1039
+ // commit) rather than the committed-scale ref, so it stays correct across
1040
+ // renders without reading mutable state during render. The +/- pair is web-only
1041
+ // (native has pinch).
1042
+ const currentZoom = Number.parseFloat(zoomLabel) || MIN_SCALE;
1043
+ const canZoomOut = currentZoom > MIN_SCALE + 0.001;
1044
+ const canZoomIn = currentZoom < MAX_SCALE - 0.001;
1045
+ const showZoomControls = Platform.OS === 'web';
1046
+ const zoomOutDisabled = !canZoomOut || !baseFit || isProcessing;
1047
+ const zoomInDisabled = !canZoomIn || !baseFit || isProcessing;
1048
+
1049
+ // No source at all (and none resolving), a resolution failure, or a
1050
+ // measurement failure — render a minimal empty state. While a file ID is still
1051
+ // resolving, fall THROUGH to the stage so its own spinner shows, not this.
1052
+ // The surface's nav bar (back = cancel) is the only chrome needed here.
1053
+ if ((!effectiveUri && !isResolvingSource) || sourceErrorMessage || measureErrorMessage) {
1054
+ const emptyMessage = sourceErrorMessage
1055
+ ? sourceErrorMessage
1056
+ : measureErrorMessage
1057
+ ? measureErrorMessage
1058
+ : t('editProfile.crop.noImage');
1002
1059
  return (
1003
- <View style={styles.container}>
1004
- <View style={styles.topBar}>
1005
- <TouchableOpacity
1006
- accessibilityLabel={t('editProfile.crop.cancel') || 'Cancel'}
1007
- accessibilityRole="button"
1008
- style={styles.cancelBtn}
1009
- onPress={handleCancel}
1010
- activeOpacity={0.6}
1011
- >
1012
- <Text style={styles.cancelLabel}>
1013
- {t('editProfile.crop.cancel') || 'Cancel'}
1014
- </Text>
1015
- </TouchableOpacity>
1016
- <View style={styles.topBarTitleWrap}>
1017
- <Text style={styles.topBarTitle}>
1018
- {t('editProfile.crop.title') || 'Crop avatar'}
1019
- </Text>
1020
- </View>
1021
- <View style={styles.cancelBtn} />
1022
- </View>
1060
+ <View style={styles.container} className="px-screen-margin pb-space-24">
1023
1061
  <View style={styles.emptyState}>
1024
1062
  <Text style={styles.emptyLabel}>{emptyMessage}</Text>
1025
1063
  </View>
@@ -1028,67 +1066,20 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
1028
1066
  }
1029
1067
 
1030
1068
  return (
1031
- <View style={styles.container}>
1032
- <View style={styles.topBar}>
1033
- <TouchableOpacity
1034
- accessibilityLabel={t('editProfile.crop.cancel') || 'Cancel'}
1035
- accessibilityRole="button"
1036
- style={styles.cancelBtn}
1037
- onPress={handleCancel}
1038
- disabled={isProcessing}
1039
- activeOpacity={0.6}
1040
- >
1041
- <Text style={styles.cancelLabel}>
1042
- {t('editProfile.crop.cancel') || 'Cancel'}
1043
- </Text>
1044
- </TouchableOpacity>
1045
- <View style={styles.topBarTitleWrap}>
1046
- <Text style={styles.topBarTitle} numberOfLines={1}>
1047
- {t('editProfile.crop.title') || 'Crop avatar'}
1048
- </Text>
1049
- </View>
1050
- <TouchableOpacity
1051
- accessibilityLabel={t('editProfile.crop.confirm') || 'Use photo'}
1052
- accessibilityRole="button"
1053
- accessibilityState={{ disabled: isProcessing || !baseFit, busy: isProcessing }}
1054
- style={[
1055
- styles.doneBtn,
1056
- (isProcessing || !baseFit) && styles.doneBtnDisabled,
1057
- ]}
1058
- onPress={handleConfirm}
1059
- disabled={isProcessing || !baseFit}
1060
- activeOpacity={0.85}
1061
- >
1062
- {isProcessing ? (
1063
- <View style={styles.doneLabelLoading}>
1064
- <ActivityIndicator size="small" color="#ffffff" />
1065
- <Text style={styles.doneLabel}>
1066
- {t('editProfile.crop.saving') || 'Saving…'}
1067
- </Text>
1068
- </View>
1069
- ) : (
1070
- <Text style={styles.doneLabel}>
1071
- {t('editProfile.crop.confirm') || 'Use photo'}
1072
- </Text>
1073
- )}
1074
- </TouchableOpacity>
1075
- </View>
1076
-
1069
+ <View style={styles.container} className="px-screen-margin pb-space-24">
1077
1070
  <View style={styles.stage}>
1078
1071
  <Animated.View
1072
+ ref={startEntrance}
1079
1073
  style={[styles.cropFrame, cropFrameAnimatedStyle]}
1080
1074
  accessible
1081
1075
  accessibilityRole="image"
1082
- accessibilityLabel={
1083
- t('editProfile.crop.a11yImage') ||
1084
- 'Crop preview. Pinch to zoom and drag to reposition the image.'
1085
- }
1076
+ accessibilityLabel={t('editProfile.crop.a11yImage')}
1086
1077
  >
1087
1078
  <GestureDetector gesture={composedGesture}>
1088
1079
  <View style={styles.viewport}>
1089
- {baseFit ? (
1080
+ {baseFit && effectiveUri ? (
1090
1081
  <Animated.Image
1091
- source={{ uri: imageUri }}
1082
+ source={{ uri: effectiveUri }}
1092
1083
  style={[styles.image, imageAnimatedStyle]}
1093
1084
  resizeMode="cover"
1094
1085
  />
@@ -1118,28 +1109,66 @@ const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
1118
1109
  style={[styles.zoomChip, zoomChipAnimatedStyle]}
1119
1110
  >
1120
1111
  <Text style={styles.zoomChipText}>
1121
- {t('editProfile.crop.zoom', { value: zoomLabel }) || `${zoomLabel}×`}
1112
+ {t('editProfile.crop.zoom', { value: zoomLabel })}
1122
1113
  </Text>
1123
1114
  </Animated.View>
1124
1115
  </Animated.View>
1116
+
1117
+ {/* Web-only +/- zoom controls (a desktop pointer has no pinch).
1118
+ Styled with NativeWind to match the translucent zoom-chip
1119
+ aesthetic; the full-width row is `box-none` so only the pill
1120
+ takes pointer events and the rest of the circle stays pannable. */}
1121
+ {showZoomControls ? (
1122
+ <View
1123
+ className="absolute bottom-4 left-0 right-0 items-center"
1124
+ pointerEvents="box-none"
1125
+ >
1126
+ <View className="flex-row overflow-hidden rounded-full bg-black/70">
1127
+ <Pressable
1128
+ accessibilityRole="button"
1129
+ accessibilityLabel={t('editProfile.crop.zoomOut')}
1130
+ accessibilityState={{ disabled: zoomOutDisabled }}
1131
+ disabled={zoomOutDisabled}
1132
+ onPress={() => applyZoom(-1)}
1133
+ className={`h-10 w-[52px] items-center justify-center active:bg-white/10${
1134
+ zoomOutDisabled ? ' opacity-40' : ''
1135
+ }`}
1136
+ >
1137
+ <Text className="text-white text-[22px] font-medium leading-[24px]">
1138
+ {'−'}
1139
+ </Text>
1140
+ </Pressable>
1141
+ <View className="w-px self-stretch bg-white/25" />
1142
+ <Pressable
1143
+ accessibilityRole="button"
1144
+ accessibilityLabel={t('editProfile.crop.zoomIn')}
1145
+ accessibilityState={{ disabled: zoomInDisabled }}
1146
+ disabled={zoomInDisabled}
1147
+ onPress={() => applyZoom(1)}
1148
+ className={`h-10 w-[52px] items-center justify-center active:bg-white/10${
1149
+ zoomInDisabled ? ' opacity-40' : ''
1150
+ }`}
1151
+ >
1152
+ <Text className="text-white text-[22px] font-medium leading-[24px]">
1153
+ {'+'}
1154
+ </Text>
1155
+ </Pressable>
1156
+ </View>
1157
+ </View>
1158
+ ) : null}
1125
1159
  </View>
1126
1160
 
1127
1161
  <View style={styles.helperBlock}>
1128
- <Text style={styles.helper}>
1129
- {t('editProfile.crop.helper') ||
1130
- 'The cropped circle is what will appear on your profile. Pinch to zoom, drag to position.'}
1131
- </Text>
1162
+ <Text style={styles.helper}>{t('editProfile.crop.helper')}</Text>
1132
1163
  <Pressable
1133
- accessibilityLabel={t('editProfile.crop.a11yReset') || 'Reset crop to default position'}
1164
+ accessibilityLabel={t('editProfile.crop.a11yReset')}
1134
1165
  accessibilityRole="button"
1135
1166
  accessibilityState={{ disabled: !isModified || isProcessing }}
1136
1167
  onPress={resetTransform}
1137
1168
  disabled={!isModified || isProcessing}
1138
1169
  style={[styles.resetLink, { opacity: isProcessing ? 0.3 : resetLinkOpacity }]}
1139
1170
  >
1140
- <Text style={styles.resetLinkText}>
1141
- {t('editProfile.crop.resetToCenter') || 'Reset to center'}
1142
- </Text>
1171
+ <Text style={styles.resetLinkText}>{t('editProfile.crop.resetToCenter')}</Text>
1143
1172
  </Pressable>
1144
1173
  </View>
1145
1174
  </View>