@oxy.so/services 11.2.0-oxy-one.3 → 11.2.0-oxy-one.5

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 (1047) hide show
  1. package/lib/commonjs/assets/assets/icons/OxyServices.tsx +42 -40
  2. package/lib/commonjs/assets/assets/illustrations/HighFive.tsx +58 -31
  3. package/lib/commonjs/assets/icons/OxyServices.js.map +1 -1
  4. package/lib/commonjs/assets/illustrations/HighFive.js.map +1 -1
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/notifications/deviceNotifications.js +8 -4
  7. package/lib/commonjs/notifications/deviceNotifications.js.map +1 -1
  8. package/lib/commonjs/ui/analytics/productAnalytics.js.map +1 -1
  9. package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
  10. package/lib/commonjs/ui/components/BenefitList.js.map +1 -1
  11. package/lib/commonjs/ui/components/BloomLocaleBridge.js.map +1 -1
  12. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  13. package/lib/commonjs/ui/components/FollowTargetButton.js +1 -1
  14. package/lib/commonjs/ui/components/FollowTargetButton.js.map +1 -1
  15. package/lib/commonjs/ui/components/LanguageBridge.js.map +1 -1
  16. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  17. package/lib/commonjs/ui/components/OxyAuthPrompt.js.map +1 -1
  18. package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
  19. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  20. package/lib/commonjs/ui/components/OxySignInButton.js +1 -1
  21. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  22. package/lib/commonjs/ui/components/OxySignInRequestSurface.js.map +1 -1
  23. package/lib/commonjs/ui/components/PeableButton.js.map +1 -1
  24. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  25. package/lib/commonjs/ui/components/ProfileSummaryCard.js +1 -1
  26. package/lib/commonjs/ui/components/ProfileSummaryCard.js.map +1 -1
  27. package/lib/commonjs/ui/components/RequireOxyAuth.js.map +1 -1
  28. package/lib/commonjs/ui/components/SettingsIcon.js.map +1 -1
  29. package/lib/commonjs/ui/components/SurfaceHeaderAction.js.map +1 -1
  30. package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
  31. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  32. package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
  33. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
  34. package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
  35. package/lib/commonjs/ui/components/feedback/constants.js.map +1 -1
  36. package/lib/commonjs/ui/components/feedback/useFeedbackForm.js.map +1 -1
  37. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  38. package/lib/commonjs/ui/components/fileManagement/FileViewer.js +1 -1
  39. package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
  40. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
  41. package/lib/commonjs/ui/components/followButtonAccessibility.js.map +1 -1
  42. package/lib/commonjs/ui/components/followRules.js.map +1 -1
  43. package/lib/commonjs/ui/components/icon/FAIRWalletIcon.js.map +1 -1
  44. package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -1
  45. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
  46. package/lib/commonjs/ui/components/oauthNavigation.js.map +1 -1
  47. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  48. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js.map +1 -1
  49. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -1
  50. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -1
  51. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +1 -1
  52. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  53. package/lib/commonjs/ui/components/payment/constants.js.map +1 -1
  54. package/lib/commonjs/ui/components/payment/paymentStyles.js.map +1 -1
  55. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  56. package/lib/commonjs/ui/components/signIn/InlineCommonsQr.js.map +1 -1
  57. package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js.map +1 -1
  58. package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
  59. package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
  60. package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
  61. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  62. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  63. package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
  64. package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js.map +1 -1
  65. package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
  66. package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
  67. package/lib/commonjs/ui/components/signIn/artwork.js.map +1 -1
  68. package/lib/commonjs/ui/components/signIn/signInFlowStore.js.map +1 -1
  69. package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
  70. package/lib/commonjs/ui/components/utils/hasTouchHandler.js.map +1 -1
  71. package/lib/commonjs/ui/components/utils/roundLayoutSize.js.map +1 -1
  72. package/lib/commonjs/ui/components/utils/splitStyles.js.map +1 -1
  73. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  74. package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
  75. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  76. package/lib/commonjs/ui/context/isolatedOAuthSession.js.map +1 -1
  77. package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
  78. package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -1
  79. package/lib/commonjs/ui/hooks/accountQueryPersistence.js.map +1 -1
  80. package/lib/commonjs/ui/hooks/mutations/mutationFactory.js.map +1 -1
  81. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  82. package/lib/commonjs/ui/hooks/persistedQueryCache.js.map +1 -1
  83. package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
  84. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  85. package/lib/commonjs/ui/hooks/queries/useFileQueries.js.map +1 -1
  86. package/lib/commonjs/ui/hooks/queries/usePaymentQueries.js.map +1 -1
  87. package/lib/commonjs/ui/hooks/queries/usePersonalPlans.js.map +1 -1
  88. package/lib/commonjs/ui/hooks/queries/useSecurityQueries.js.map +1 -1
  89. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  90. package/lib/commonjs/ui/hooks/queries/userCache.js +38 -38
  91. package/lib/commonjs/ui/hooks/queries/userCache.js.map +1 -1
  92. package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
  93. package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
  94. package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -1
  95. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  96. package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
  97. package/lib/commonjs/ui/hooks/useDeviceSwitcher.js.map +1 -1
  98. package/lib/commonjs/ui/hooks/useFileFiltering.js.map +1 -1
  99. package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
  100. package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
  101. package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
  102. package/lib/commonjs/ui/hooks/useReduceMotion.js.map +1 -1
  103. package/lib/commonjs/ui/hooks/useResolvedFileUrls.js.map +1 -1
  104. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  105. package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
  106. package/lib/commonjs/ui/hooks/useStorage.js.map +1 -1
  107. package/lib/commonjs/ui/hooks/useSurfaceFrameWidth.js.map +1 -1
  108. package/lib/commonjs/ui/icons/Ionicons.js.map +1 -1
  109. package/lib/commonjs/ui/icons/MaterialCommunityIcons.js.map +1 -1
  110. package/lib/commonjs/ui/icons/decorativeIconSet.js.map +1 -1
  111. package/lib/commonjs/ui/isFrontend.js.map +1 -1
  112. package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
  113. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  114. package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
  115. package/lib/commonjs/ui/oauth/applicationSessionLane.js.map +1 -1
  116. package/lib/commonjs/ui/oauth/browserAuthTransport.js.map +1 -1
  117. package/lib/commonjs/ui/oauth/browserBridge.js.map +1 -1
  118. package/lib/commonjs/ui/oauth/completeOAuthCode.js.map +1 -1
  119. package/lib/commonjs/ui/oauth/nativeAuthTransport.js.map +1 -1
  120. package/lib/commonjs/ui/oauth/oauthHandshake.js.map +1 -1
  121. package/lib/commonjs/ui/oauth/oauthPopup.js.map +1 -1
  122. package/lib/commonjs/ui/oauth/oauthPopupMessages.js.map +1 -1
  123. package/lib/commonjs/ui/runtime/createOxyRuntime.js.map +1 -1
  124. package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
  125. package/lib/commonjs/ui/screens/AccountSecurityPanelScreen.js.map +1 -1
  126. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  127. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  128. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  129. package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
  130. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -1
  131. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  132. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  133. package/lib/commonjs/ui/screens/DomainsScreen.js.map +1 -1
  134. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +1 -1
  135. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  136. package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -1
  137. package/lib/commonjs/ui/screens/FileManagementScreen.js +2 -5
  138. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  139. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
  140. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  141. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  142. package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
  143. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
  144. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  145. package/lib/commonjs/ui/screens/NotificationsScreen.js.map +1 -1
  146. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  147. package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
  148. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +3 -3
  149. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  150. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  151. package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
  152. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
  153. package/lib/commonjs/ui/screens/UserListScreen.js +1 -1
  154. package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
  155. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  156. package/lib/commonjs/ui/screens/fileManagement/FileLibraryError.js.map +1 -1
  157. package/lib/commonjs/ui/screens/fileManagement/FileListSection.js.map +1 -1
  158. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +7 -7
  159. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  160. package/lib/commonjs/ui/screens/fileManagement/UploadBar.js.map +1 -1
  161. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  162. package/lib/commonjs/ui/screens/fileManagement/photoGridLayout.js.map +1 -1
  163. package/lib/commonjs/ui/screens/fileManagement/shared.js.map +1 -1
  164. package/lib/commonjs/ui/screens/linkFormat.js.map +1 -1
  165. package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -1
  166. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +1 -1
  167. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  168. package/lib/commonjs/ui/screens/trust/TrustFAQScreen.js.map +1 -1
  169. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +1 -1
  170. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  171. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  172. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +1 -1
  173. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  174. package/lib/commonjs/ui/server.js.map +1 -1
  175. package/lib/commonjs/ui/session/authStore.js.map +1 -1
  176. package/lib/commonjs/ui/session/backgroundSession.js.map +1 -1
  177. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  178. package/lib/commonjs/ui/session/deviceCredentialTracker.js.map +1 -1
  179. package/lib/commonjs/ui/session/identityBinding.js.map +1 -1
  180. package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js.map +1 -1
  181. package/lib/commonjs/ui/session/tokenLossRecovery.js.map +1 -1
  182. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
  183. package/lib/commonjs/ui/stores/assetStore.js.map +1 -1
  184. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  185. package/lib/commonjs/ui/utils/accountDeletionHandoff.js.map +1 -1
  186. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  187. package/lib/commonjs/ui/utils/clipboard.native.js +23 -6
  188. package/lib/commonjs/ui/utils/clipboard.native.js.map +1 -1
  189. package/lib/commonjs/ui/utils/commonsStoreLinks.js.map +1 -1
  190. package/lib/commonjs/ui/utils/errorHandlers.js.map +1 -1
  191. package/lib/commonjs/ui/utils/fileManagement.js +24 -24
  192. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  193. package/lib/commonjs/ui/utils/iconNames.js.map +1 -1
  194. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  195. package/lib/commonjs/ui/utils/userUtils.js.map +1 -1
  196. package/lib/commonjs/utils/hookUtils.js +2 -4
  197. package/lib/commonjs/utils/hookUtils.js.map +1 -1
  198. package/lib/module/assets/assets/icons/OxyServices.tsx +42 -40
  199. package/lib/module/assets/assets/illustrations/HighFive.tsx +58 -31
  200. package/lib/module/assets/icons/OxyServices.js.map +1 -1
  201. package/lib/module/assets/illustrations/HighFive.js.map +1 -1
  202. package/lib/module/index.js.map +1 -1
  203. package/lib/module/notifications/deviceNotifications.js +8 -4
  204. package/lib/module/notifications/deviceNotifications.js.map +1 -1
  205. package/lib/module/ui/analytics/productAnalytics.js.map +1 -1
  206. package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
  207. package/lib/module/ui/client.js.map +1 -1
  208. package/lib/module/ui/components/BenefitList.js.map +1 -1
  209. package/lib/module/ui/components/BloomLocaleBridge.js.map +1 -1
  210. package/lib/module/ui/components/FollowButton.js.map +1 -1
  211. package/lib/module/ui/components/FollowTargetButton.js +1 -1
  212. package/lib/module/ui/components/FollowTargetButton.js.map +1 -1
  213. package/lib/module/ui/components/LanguageBridge.js.map +1 -1
  214. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  215. package/lib/module/ui/components/OxyAuthPrompt.js.map +1 -1
  216. package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
  217. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  218. package/lib/module/ui/components/OxySignInButton.js +1 -1
  219. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  220. package/lib/module/ui/components/OxySignInRequestSurface.js.map +1 -1
  221. package/lib/module/ui/components/PeableButton.js.map +1 -1
  222. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  223. package/lib/module/ui/components/ProfileSummaryCard.js +1 -1
  224. package/lib/module/ui/components/ProfileSummaryCard.js.map +1 -1
  225. package/lib/module/ui/components/RequireOxyAuth.js.map +1 -1
  226. package/lib/module/ui/components/SettingsIcon.js.map +1 -1
  227. package/lib/module/ui/components/SurfaceHeaderAction.js.map +1 -1
  228. package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
  229. package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  230. package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
  231. package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
  232. package/lib/module/ui/components/authChooser/types.js.map +1 -1
  233. package/lib/module/ui/components/feedback/constants.js.map +1 -1
  234. package/lib/module/ui/components/feedback/useFeedbackForm.js.map +1 -1
  235. package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  236. package/lib/module/ui/components/fileManagement/FileViewer.js +1 -1
  237. package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
  238. package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
  239. package/lib/module/ui/components/followButtonAccessibility.js.map +1 -1
  240. package/lib/module/ui/components/followRules.js.map +1 -1
  241. package/lib/module/ui/components/icon/FAIRWalletIcon.js.map +1 -1
  242. package/lib/module/ui/components/logo/LogoText.js.map +1 -1
  243. package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
  244. package/lib/module/ui/components/oauthNavigation.js.map +1 -1
  245. package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  246. package/lib/module/ui/components/payment/PaymentMethodStep.js.map +1 -1
  247. package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -1
  248. package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -1
  249. package/lib/module/ui/components/payment/PaymentSummaryStep.js +1 -1
  250. package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  251. package/lib/module/ui/components/payment/constants.js.map +1 -1
  252. package/lib/module/ui/components/payment/paymentStyles.js.map +1 -1
  253. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  254. package/lib/module/ui/components/signIn/InlineCommonsQr.js.map +1 -1
  255. package/lib/module/ui/components/signIn/OxyAccountPicker.js.map +1 -1
  256. package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
  257. package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
  258. package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
  259. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  260. package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  261. package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
  262. package/lib/module/ui/components/signIn/OxySignUpPanel.js.map +1 -1
  263. package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
  264. package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
  265. package/lib/module/ui/components/signIn/artwork.js.map +1 -1
  266. package/lib/module/ui/components/signIn/signInFlowStore.js.map +1 -1
  267. package/lib/module/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
  268. package/lib/module/ui/components/utils/hasTouchHandler.js.map +1 -1
  269. package/lib/module/ui/components/utils/roundLayoutSize.js.map +1 -1
  270. package/lib/module/ui/components/utils/splitStyles.js.map +1 -1
  271. package/lib/module/ui/context/OxyContext.js.map +1 -1
  272. package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
  273. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  274. package/lib/module/ui/context/isolatedOAuthSession.js.map +1 -1
  275. package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
  276. package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -1
  277. package/lib/module/ui/hooks/accountQueryPersistence.js.map +1 -1
  278. package/lib/module/ui/hooks/mutations/mutationFactory.js.map +1 -1
  279. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  280. package/lib/module/ui/hooks/persistedQueryCache.js.map +1 -1
  281. package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
  282. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  283. package/lib/module/ui/hooks/queries/useFileQueries.js.map +1 -1
  284. package/lib/module/ui/hooks/queries/usePaymentQueries.js.map +1 -1
  285. package/lib/module/ui/hooks/queries/usePersonalPlans.js.map +1 -1
  286. package/lib/module/ui/hooks/queries/useSecurityQueries.js.map +1 -1
  287. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  288. package/lib/module/ui/hooks/queries/userCache.js +38 -38
  289. package/lib/module/ui/hooks/queries/userCache.js.map +1 -1
  290. package/lib/module/ui/hooks/queryClient.js.map +1 -1
  291. package/lib/module/ui/hooks/useAssets.js.map +1 -1
  292. package/lib/module/ui/hooks/useAsyncAction.js.map +1 -1
  293. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  294. package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
  295. package/lib/module/ui/hooks/useDeviceSwitcher.js.map +1 -1
  296. package/lib/module/ui/hooks/useFileFiltering.js.map +1 -1
  297. package/lib/module/ui/hooks/useFollow.js.map +1 -1
  298. package/lib/module/ui/hooks/useI18n.js.map +1 -1
  299. package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
  300. package/lib/module/ui/hooks/useReduceMotion.js.map +1 -1
  301. package/lib/module/ui/hooks/useResolvedFileUrls.js.map +1 -1
  302. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  303. package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
  304. package/lib/module/ui/hooks/useStorage.js.map +1 -1
  305. package/lib/module/ui/hooks/useSurfaceFrameWidth.js.map +1 -1
  306. package/lib/module/ui/icons/Ionicons.js.map +1 -1
  307. package/lib/module/ui/icons/MaterialCommunityIcons.js.map +1 -1
  308. package/lib/module/ui/icons/decorativeIconSet.js.map +1 -1
  309. package/lib/module/ui/index.js.map +1 -1
  310. package/lib/module/ui/isFrontend.js.map +1 -1
  311. package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
  312. package/lib/module/ui/navigation/routes.js.map +1 -1
  313. package/lib/module/ui/navigation/surfaces.js.map +1 -1
  314. package/lib/module/ui/oauth/applicationSessionLane.js.map +1 -1
  315. package/lib/module/ui/oauth/browserAuthTransport.js.map +1 -1
  316. package/lib/module/ui/oauth/browserBridge.js.map +1 -1
  317. package/lib/module/ui/oauth/completeOAuthCode.js.map +1 -1
  318. package/lib/module/ui/oauth/nativeAuthTransport.js.map +1 -1
  319. package/lib/module/ui/oauth/oauthHandshake.js.map +1 -1
  320. package/lib/module/ui/oauth/oauthPopup.js.map +1 -1
  321. package/lib/module/ui/oauth/oauthPopupMessages.js.map +1 -1
  322. package/lib/module/ui/runtime/createOxyRuntime.js.map +1 -1
  323. package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
  324. package/lib/module/ui/screens/AccountSecurityPanelScreen.js.map +1 -1
  325. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  326. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  327. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  328. package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
  329. package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -1
  330. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  331. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  332. package/lib/module/ui/screens/DomainsScreen.js.map +1 -1
  333. package/lib/module/ui/screens/EditProfileFieldScreen.js +1 -1
  334. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  335. package/lib/module/ui/screens/EditProfileScreen.js.map +1 -1
  336. package/lib/module/ui/screens/FileManagementScreen.js +2 -5
  337. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  338. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
  339. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  340. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  341. package/lib/module/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
  342. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
  343. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  344. package/lib/module/ui/screens/NotificationsScreen.js.map +1 -1
  345. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  346. package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
  347. package/lib/module/ui/screens/PrivacySettingsScreen.js +3 -3
  348. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  349. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  350. package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
  351. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
  352. package/lib/module/ui/screens/UserListScreen.js +1 -1
  353. package/lib/module/ui/screens/UserListScreen.js.map +1 -1
  354. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  355. package/lib/module/ui/screens/fileManagement/FileLibraryError.js.map +1 -1
  356. package/lib/module/ui/screens/fileManagement/FileListSection.js.map +1 -1
  357. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +7 -7
  358. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  359. package/lib/module/ui/screens/fileManagement/UploadBar.js.map +1 -1
  360. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  361. package/lib/module/ui/screens/fileManagement/photoGridLayout.js.map +1 -1
  362. package/lib/module/ui/screens/fileManagement/shared.js.map +1 -1
  363. package/lib/module/ui/screens/linkFormat.js.map +1 -1
  364. package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -1
  365. package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
  366. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  367. package/lib/module/ui/screens/trust/TrustFAQScreen.js.map +1 -1
  368. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +1 -1
  369. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  370. package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  371. package/lib/module/ui/screens/trust/TrustRulesScreen.js +1 -1
  372. package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  373. package/lib/module/ui/server.js.map +1 -1
  374. package/lib/module/ui/session/authStore.js.map +1 -1
  375. package/lib/module/ui/session/backgroundSession.js.map +1 -1
  376. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  377. package/lib/module/ui/session/deviceCredentialTracker.js.map +1 -1
  378. package/lib/module/ui/session/identityBinding.js.map +1 -1
  379. package/lib/module/ui/session/sharedDeviceCredentialStore.js.map +1 -1
  380. package/lib/module/ui/session/tokenLossRecovery.js.map +1 -1
  381. package/lib/module/ui/session/tokenTransport.js.map +1 -1
  382. package/lib/module/ui/stores/assetStore.js.map +1 -1
  383. package/lib/module/ui/stores/followStore.js.map +1 -1
  384. package/lib/module/ui/utils/accountDeletionHandoff.js.map +1 -1
  385. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  386. package/lib/module/ui/utils/clipboard.native.js +23 -6
  387. package/lib/module/ui/utils/clipboard.native.js.map +1 -1
  388. package/lib/module/ui/utils/commonsStoreLinks.js.map +1 -1
  389. package/lib/module/ui/utils/errorHandlers.js.map +1 -1
  390. package/lib/module/ui/utils/fileManagement.js +25 -25
  391. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  392. package/lib/module/ui/utils/iconNames.js.map +1 -1
  393. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  394. package/lib/module/ui/utils/userUtils.js.map +1 -1
  395. package/lib/module/utils/hookUtils.js +2 -4
  396. package/lib/module/utils/hookUtils.js.map +1 -1
  397. package/lib/typescript/commonjs/assets/icons/OxyServices.d.ts.map +1 -1
  398. package/lib/typescript/commonjs/assets/illustrations/HighFive.d.ts.map +1 -1
  399. package/lib/typescript/commonjs/index.d.ts +4 -4
  400. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  401. package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts +8 -4
  402. package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts.map +1 -1
  403. package/lib/typescript/commonjs/ui/analytics/productAnalytics.d.ts.map +1 -1
  404. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  405. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  406. package/lib/typescript/commonjs/ui/components/BenefitList.d.ts.map +1 -1
  407. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
  408. package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts +1 -1
  409. package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts.map +1 -1
  410. package/lib/typescript/commonjs/ui/components/LanguageBridge.d.ts +1 -1
  411. package/lib/typescript/commonjs/ui/components/LanguageBridge.d.ts.map +1 -1
  412. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  413. package/lib/typescript/commonjs/ui/components/OxyAuthPrompt.d.ts.map +1 -1
  414. package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
  415. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  416. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  417. package/lib/typescript/commonjs/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
  418. package/lib/typescript/commonjs/ui/components/PeableButton.d.ts.map +1 -1
  419. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  420. package/lib/typescript/commonjs/ui/components/ProfileSummaryCard.d.ts.map +1 -1
  421. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -1
  422. package/lib/typescript/commonjs/ui/components/SettingsIcon.d.ts.map +1 -1
  423. package/lib/typescript/commonjs/ui/components/SurfaceHeaderAction.d.ts.map +1 -1
  424. package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts +1 -1
  425. package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
  426. package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
  427. package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
  428. package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
  429. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
  430. package/lib/typescript/commonjs/ui/components/feedback/constants.d.ts.map +1 -1
  431. package/lib/typescript/commonjs/ui/components/feedback/types.d.ts.map +1 -1
  432. package/lib/typescript/commonjs/ui/components/feedback/useFeedbackForm.d.ts.map +1 -1
  433. package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  434. package/lib/typescript/commonjs/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
  435. package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  436. package/lib/typescript/commonjs/ui/components/followButtonAccessibility.d.ts.map +1 -1
  437. package/lib/typescript/commonjs/ui/components/followRules.d.ts.map +1 -1
  438. package/lib/typescript/commonjs/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -1
  439. package/lib/typescript/commonjs/ui/components/logo/LogoText.d.ts.map +1 -1
  440. package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  441. package/lib/typescript/commonjs/ui/components/oauthNavigation.d.ts.map +1 -1
  442. package/lib/typescript/commonjs/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
  443. package/lib/typescript/commonjs/ui/components/payment/PaymentMethodStep.d.ts.map +1 -1
  444. package/lib/typescript/commonjs/ui/components/payment/PaymentReviewStep.d.ts.map +1 -1
  445. package/lib/typescript/commonjs/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -1
  446. package/lib/typescript/commonjs/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -1
  447. package/lib/typescript/commonjs/ui/components/payment/constants.d.ts.map +1 -1
  448. package/lib/typescript/commonjs/ui/components/payment/paymentStyles.d.ts.map +1 -1
  449. package/lib/typescript/commonjs/ui/components/payment/types.d.ts +6 -6
  450. package/lib/typescript/commonjs/ui/components/payment/types.d.ts.map +1 -1
  451. package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  452. package/lib/typescript/commonjs/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
  453. package/lib/typescript/commonjs/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
  454. package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
  455. package/lib/typescript/commonjs/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
  456. package/lib/typescript/commonjs/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
  457. package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
  458. package/lib/typescript/commonjs/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
  459. package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
  460. package/lib/typescript/commonjs/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
  461. package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts.map +1 -1
  462. package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
  463. package/lib/typescript/commonjs/ui/components/signIn/artwork.d.ts.map +1 -1
  464. package/lib/typescript/commonjs/ui/components/signIn/signInFlowStore.d.ts.map +1 -1
  465. package/lib/typescript/commonjs/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
  466. package/lib/typescript/commonjs/ui/components/utils/hasTouchHandler.d.ts.map +1 -1
  467. package/lib/typescript/commonjs/ui/components/utils/roundLayoutSize.d.ts.map +1 -1
  468. package/lib/typescript/commonjs/ui/components/utils/splitStyles.d.ts.map +1 -1
  469. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  470. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
  471. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  472. package/lib/typescript/commonjs/ui/context/isolatedOAuthSession.d.ts.map +1 -1
  473. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  474. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  475. package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
  476. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
  477. package/lib/typescript/commonjs/ui/hooks/mutations/mutationFactory.d.ts.map +1 -1
  478. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  479. package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts.map +1 -1
  480. package/lib/typescript/commonjs/ui/hooks/queries/paymentTypes.d.ts +2 -2
  481. package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  482. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  483. package/lib/typescript/commonjs/ui/hooks/queries/useFileQueries.d.ts.map +1 -1
  484. package/lib/typescript/commonjs/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
  485. package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
  486. package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
  487. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  488. package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts +5 -5
  489. package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts.map +1 -1
  490. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
  491. package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts.map +1 -1
  492. package/lib/typescript/commonjs/ui/hooks/useAsyncAction.d.ts.map +1 -1
  493. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  494. package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts.map +1 -1
  495. package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts.map +1 -1
  496. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
  497. package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
  498. package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
  499. package/lib/typescript/commonjs/ui/hooks/useResolvedFileUrls.d.ts.map +1 -1
  500. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  501. package/lib/typescript/commonjs/ui/hooks/useSettingToggle.d.ts.map +1 -1
  502. package/lib/typescript/commonjs/ui/hooks/useStorage.d.ts +1 -1
  503. package/lib/typescript/commonjs/ui/hooks/useStorage.d.ts.map +1 -1
  504. package/lib/typescript/commonjs/ui/hooks/useSurfaceFrameWidth.d.ts.map +1 -1
  505. package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts.map +1 -1
  506. package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
  507. package/lib/typescript/commonjs/ui/icons/decorativeIconSet.d.ts.map +1 -1
  508. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  509. package/lib/typescript/commonjs/ui/isFrontend.d.ts.map +1 -1
  510. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  511. package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
  512. package/lib/typescript/commonjs/ui/oauth/applicationSessionLane.d.ts.map +1 -1
  513. package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts.map +1 -1
  514. package/lib/typescript/commonjs/ui/oauth/browserBridge.d.ts.map +1 -1
  515. package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts.map +1 -1
  516. package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts.map +1 -1
  517. package/lib/typescript/commonjs/ui/oauth/oauthHandshake.d.ts.map +1 -1
  518. package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts.map +1 -1
  519. package/lib/typescript/commonjs/ui/oauth/oauthPopupMessages.d.ts.map +1 -1
  520. package/lib/typescript/commonjs/ui/oauth/types.d.ts.map +1 -1
  521. package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts.map +1 -1
  522. package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  523. package/lib/typescript/commonjs/ui/screens/AccountSecurityPanelScreen.d.ts.map +1 -1
  524. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  525. package/lib/typescript/commonjs/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  526. package/lib/typescript/commonjs/ui/screens/AppInfoScreen.d.ts.map +1 -1
  527. package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts.map +1 -1
  528. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  529. package/lib/typescript/commonjs/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  530. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  531. package/lib/typescript/commonjs/ui/screens/DomainsScreen.d.ts.map +1 -1
  532. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  533. package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -1
  534. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
  535. package/lib/typescript/commonjs/ui/screens/HelpSupportScreen.d.ts.map +1 -1
  536. package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  537. package/lib/typescript/commonjs/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  538. package/lib/typescript/commonjs/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
  539. package/lib/typescript/commonjs/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
  540. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  541. package/lib/typescript/commonjs/ui/screens/NotificationsScreen.d.ts.map +1 -1
  542. package/lib/typescript/commonjs/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  543. package/lib/typescript/commonjs/ui/screens/PreferencesScreen.d.ts.map +1 -1
  544. package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  545. package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
  546. package/lib/typescript/commonjs/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  547. package/lib/typescript/commonjs/ui/screens/UserLinksScreen.d.ts.map +1 -1
  548. package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts.map +1 -1
  549. package/lib/typescript/commonjs/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  550. package/lib/typescript/commonjs/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -1
  551. package/lib/typescript/commonjs/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
  552. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  553. package/lib/typescript/commonjs/ui/screens/fileManagement/UploadBar.d.ts.map +1 -1
  554. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  555. package/lib/typescript/commonjs/ui/screens/fileManagement/photoGridLayout.d.ts.map +1 -1
  556. package/lib/typescript/commonjs/ui/screens/fileManagement/shared.d.ts.map +1 -1
  557. package/lib/typescript/commonjs/ui/screens/linkFormat.d.ts.map +1 -1
  558. package/lib/typescript/commonjs/ui/screens/trust/TrustAboutScreen.d.ts.map +1 -1
  559. package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -1
  560. package/lib/typescript/commonjs/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -1
  561. package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  562. package/lib/typescript/commonjs/ui/screens/trust/TrustRewardsScreen.d.ts.map +1 -1
  563. package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
  564. package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
  565. package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
  566. package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts.map +1 -1
  567. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  568. package/lib/typescript/commonjs/ui/session/deviceCredentialTracker.d.ts.map +1 -1
  569. package/lib/typescript/commonjs/ui/session/identityBinding.d.ts.map +1 -1
  570. package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -1
  571. package/lib/typescript/commonjs/ui/session/tokenLossRecovery.d.ts.map +1 -1
  572. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
  573. package/lib/typescript/commonjs/ui/stores/assetStore.d.ts.map +1 -1
  574. package/lib/typescript/commonjs/ui/stores/followStore.d.ts.map +1 -1
  575. package/lib/typescript/commonjs/ui/types/fileManagement.d.ts.map +1 -1
  576. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  577. package/lib/typescript/commonjs/ui/types/surfaceScreen.d.ts.map +1 -1
  578. package/lib/typescript/commonjs/ui/utils/accountDeletionHandoff.d.ts.map +1 -1
  579. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
  580. package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts +16 -5
  581. package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts.map +1 -1
  582. package/lib/typescript/commonjs/ui/utils/commonsStoreLinks.d.ts.map +1 -1
  583. package/lib/typescript/commonjs/ui/utils/errorHandlers.d.ts +1 -1
  584. package/lib/typescript/commonjs/ui/utils/errorHandlers.d.ts.map +1 -1
  585. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +4 -4
  586. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
  587. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +1 -1
  588. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
  589. package/lib/typescript/commonjs/ui/utils/userUtils.d.ts.map +1 -1
  590. package/lib/typescript/commonjs/utils/hookUtils.d.ts.map +1 -1
  591. package/lib/typescript/module/assets/icons/OxyServices.d.ts.map +1 -1
  592. package/lib/typescript/module/assets/illustrations/HighFive.d.ts.map +1 -1
  593. package/lib/typescript/module/index.d.ts +4 -4
  594. package/lib/typescript/module/index.d.ts.map +1 -1
  595. package/lib/typescript/module/notifications/deviceNotifications.d.ts +8 -4
  596. package/lib/typescript/module/notifications/deviceNotifications.d.ts.map +1 -1
  597. package/lib/typescript/module/ui/analytics/productAnalytics.d.ts.map +1 -1
  598. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  599. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  600. package/lib/typescript/module/ui/components/BenefitList.d.ts.map +1 -1
  601. package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
  602. package/lib/typescript/module/ui/components/FollowTargetButton.d.ts +1 -1
  603. package/lib/typescript/module/ui/components/FollowTargetButton.d.ts.map +1 -1
  604. package/lib/typescript/module/ui/components/LanguageBridge.d.ts +1 -1
  605. package/lib/typescript/module/ui/components/LanguageBridge.d.ts.map +1 -1
  606. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  607. package/lib/typescript/module/ui/components/OxyAuthPrompt.d.ts.map +1 -1
  608. package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
  609. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  610. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  611. package/lib/typescript/module/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
  612. package/lib/typescript/module/ui/components/PeableButton.d.ts.map +1 -1
  613. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  614. package/lib/typescript/module/ui/components/ProfileSummaryCard.d.ts.map +1 -1
  615. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts.map +1 -1
  616. package/lib/typescript/module/ui/components/SettingsIcon.d.ts.map +1 -1
  617. package/lib/typescript/module/ui/components/SurfaceHeaderAction.d.ts.map +1 -1
  618. package/lib/typescript/module/ui/components/SurfaceScreen.d.ts +1 -1
  619. package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
  620. package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
  621. package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
  622. package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
  623. package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
  624. package/lib/typescript/module/ui/components/feedback/constants.d.ts.map +1 -1
  625. package/lib/typescript/module/ui/components/feedback/types.d.ts.map +1 -1
  626. package/lib/typescript/module/ui/components/feedback/useFeedbackForm.d.ts.map +1 -1
  627. package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  628. package/lib/typescript/module/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
  629. package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  630. package/lib/typescript/module/ui/components/followButtonAccessibility.d.ts.map +1 -1
  631. package/lib/typescript/module/ui/components/followRules.d.ts.map +1 -1
  632. package/lib/typescript/module/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -1
  633. package/lib/typescript/module/ui/components/logo/LogoText.d.ts.map +1 -1
  634. package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  635. package/lib/typescript/module/ui/components/oauthNavigation.d.ts.map +1 -1
  636. package/lib/typescript/module/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
  637. package/lib/typescript/module/ui/components/payment/PaymentMethodStep.d.ts.map +1 -1
  638. package/lib/typescript/module/ui/components/payment/PaymentReviewStep.d.ts.map +1 -1
  639. package/lib/typescript/module/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -1
  640. package/lib/typescript/module/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -1
  641. package/lib/typescript/module/ui/components/payment/constants.d.ts.map +1 -1
  642. package/lib/typescript/module/ui/components/payment/paymentStyles.d.ts.map +1 -1
  643. package/lib/typescript/module/ui/components/payment/types.d.ts +6 -6
  644. package/lib/typescript/module/ui/components/payment/types.d.ts.map +1 -1
  645. package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  646. package/lib/typescript/module/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
  647. package/lib/typescript/module/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
  648. package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
  649. package/lib/typescript/module/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
  650. package/lib/typescript/module/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
  651. package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
  652. package/lib/typescript/module/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
  653. package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
  654. package/lib/typescript/module/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
  655. package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts.map +1 -1
  656. package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
  657. package/lib/typescript/module/ui/components/signIn/artwork.d.ts.map +1 -1
  658. package/lib/typescript/module/ui/components/signIn/signInFlowStore.d.ts.map +1 -1
  659. package/lib/typescript/module/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
  660. package/lib/typescript/module/ui/components/utils/hasTouchHandler.d.ts.map +1 -1
  661. package/lib/typescript/module/ui/components/utils/roundLayoutSize.d.ts.map +1 -1
  662. package/lib/typescript/module/ui/components/utils/splitStyles.d.ts.map +1 -1
  663. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  664. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
  665. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  666. package/lib/typescript/module/ui/context/isolatedOAuthSession.d.ts.map +1 -1
  667. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  668. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  669. package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
  670. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
  671. package/lib/typescript/module/ui/hooks/mutations/mutationFactory.d.ts.map +1 -1
  672. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  673. package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts.map +1 -1
  674. package/lib/typescript/module/ui/hooks/queries/paymentTypes.d.ts +2 -2
  675. package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  676. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  677. package/lib/typescript/module/ui/hooks/queries/useFileQueries.d.ts.map +1 -1
  678. package/lib/typescript/module/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
  679. package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
  680. package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
  681. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  682. package/lib/typescript/module/ui/hooks/queries/userCache.d.ts +5 -5
  683. package/lib/typescript/module/ui/hooks/queries/userCache.d.ts.map +1 -1
  684. package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
  685. package/lib/typescript/module/ui/hooks/useAssets.d.ts.map +1 -1
  686. package/lib/typescript/module/ui/hooks/useAsyncAction.d.ts.map +1 -1
  687. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  688. package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts.map +1 -1
  689. package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts.map +1 -1
  690. package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
  691. package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
  692. package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
  693. package/lib/typescript/module/ui/hooks/useResolvedFileUrls.d.ts.map +1 -1
  694. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  695. package/lib/typescript/module/ui/hooks/useSettingToggle.d.ts.map +1 -1
  696. package/lib/typescript/module/ui/hooks/useStorage.d.ts +1 -1
  697. package/lib/typescript/module/ui/hooks/useStorage.d.ts.map +1 -1
  698. package/lib/typescript/module/ui/hooks/useSurfaceFrameWidth.d.ts.map +1 -1
  699. package/lib/typescript/module/ui/icons/Ionicons.d.ts.map +1 -1
  700. package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
  701. package/lib/typescript/module/ui/icons/decorativeIconSet.d.ts.map +1 -1
  702. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  703. package/lib/typescript/module/ui/isFrontend.d.ts.map +1 -1
  704. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  705. package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
  706. package/lib/typescript/module/ui/oauth/applicationSessionLane.d.ts.map +1 -1
  707. package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts.map +1 -1
  708. package/lib/typescript/module/ui/oauth/browserBridge.d.ts.map +1 -1
  709. package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts.map +1 -1
  710. package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts.map +1 -1
  711. package/lib/typescript/module/ui/oauth/oauthHandshake.d.ts.map +1 -1
  712. package/lib/typescript/module/ui/oauth/oauthPopup.d.ts.map +1 -1
  713. package/lib/typescript/module/ui/oauth/oauthPopupMessages.d.ts.map +1 -1
  714. package/lib/typescript/module/ui/oauth/types.d.ts.map +1 -1
  715. package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts.map +1 -1
  716. package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  717. package/lib/typescript/module/ui/screens/AccountSecurityPanelScreen.d.ts.map +1 -1
  718. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  719. package/lib/typescript/module/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  720. package/lib/typescript/module/ui/screens/AppInfoScreen.d.ts.map +1 -1
  721. package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts.map +1 -1
  722. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  723. package/lib/typescript/module/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  724. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  725. package/lib/typescript/module/ui/screens/DomainsScreen.d.ts.map +1 -1
  726. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  727. package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -1
  728. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
  729. package/lib/typescript/module/ui/screens/HelpSupportScreen.d.ts.map +1 -1
  730. package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  731. package/lib/typescript/module/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  732. package/lib/typescript/module/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
  733. package/lib/typescript/module/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
  734. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  735. package/lib/typescript/module/ui/screens/NotificationsScreen.d.ts.map +1 -1
  736. package/lib/typescript/module/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  737. package/lib/typescript/module/ui/screens/PreferencesScreen.d.ts.map +1 -1
  738. package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  739. package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
  740. package/lib/typescript/module/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  741. package/lib/typescript/module/ui/screens/UserLinksScreen.d.ts.map +1 -1
  742. package/lib/typescript/module/ui/screens/UserListScreen.d.ts.map +1 -1
  743. package/lib/typescript/module/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  744. package/lib/typescript/module/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -1
  745. package/lib/typescript/module/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
  746. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  747. package/lib/typescript/module/ui/screens/fileManagement/UploadBar.d.ts.map +1 -1
  748. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  749. package/lib/typescript/module/ui/screens/fileManagement/photoGridLayout.d.ts.map +1 -1
  750. package/lib/typescript/module/ui/screens/fileManagement/shared.d.ts.map +1 -1
  751. package/lib/typescript/module/ui/screens/linkFormat.d.ts.map +1 -1
  752. package/lib/typescript/module/ui/screens/trust/TrustAboutScreen.d.ts.map +1 -1
  753. package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -1
  754. package/lib/typescript/module/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -1
  755. package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  756. package/lib/typescript/module/ui/screens/trust/TrustRewardsScreen.d.ts.map +1 -1
  757. package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
  758. package/lib/typescript/module/ui/server.d.ts.map +1 -1
  759. package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
  760. package/lib/typescript/module/ui/session/backgroundSession.d.ts.map +1 -1
  761. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  762. package/lib/typescript/module/ui/session/deviceCredentialTracker.d.ts.map +1 -1
  763. package/lib/typescript/module/ui/session/identityBinding.d.ts.map +1 -1
  764. package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -1
  765. package/lib/typescript/module/ui/session/tokenLossRecovery.d.ts.map +1 -1
  766. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
  767. package/lib/typescript/module/ui/stores/assetStore.d.ts.map +1 -1
  768. package/lib/typescript/module/ui/stores/followStore.d.ts.map +1 -1
  769. package/lib/typescript/module/ui/types/fileManagement.d.ts.map +1 -1
  770. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  771. package/lib/typescript/module/ui/types/surfaceScreen.d.ts.map +1 -1
  772. package/lib/typescript/module/ui/utils/accountDeletionHandoff.d.ts.map +1 -1
  773. package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
  774. package/lib/typescript/module/ui/utils/clipboard.native.d.ts +16 -5
  775. package/lib/typescript/module/ui/utils/clipboard.native.d.ts.map +1 -1
  776. package/lib/typescript/module/ui/utils/commonsStoreLinks.d.ts.map +1 -1
  777. package/lib/typescript/module/ui/utils/errorHandlers.d.ts +1 -1
  778. package/lib/typescript/module/ui/utils/errorHandlers.d.ts.map +1 -1
  779. package/lib/typescript/module/ui/utils/fileManagement.d.ts +4 -4
  780. package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
  781. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +1 -1
  782. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
  783. package/lib/typescript/module/ui/utils/userUtils.d.ts.map +1 -1
  784. package/lib/typescript/module/utils/hookUtils.d.ts.map +1 -1
  785. package/lib/typescript/types/expo-crypto.d.ts +2 -4
  786. package/lib/typescript/types/expo-document-picker.d.ts +27 -28
  787. package/lib/typescript/types/expo-image-picker.d.ts +27 -27
  788. package/lib/typescript/types/expo-secure-store.d.ts +11 -4
  789. package/lib/typescript/types/express.d.ts +22 -7
  790. package/package.json +4 -5
  791. package/src/assets/icons/OxyServices.tsx +42 -40
  792. package/src/assets/illustrations/HighFive.tsx +58 -31
  793. package/src/index.ts +99 -86
  794. package/src/notifications/deviceNotifications.ts +9 -7
  795. package/src/types/expo-crypto.d.ts +2 -4
  796. package/src/types/expo-document-picker.d.ts +27 -28
  797. package/src/types/expo-image-picker.d.ts +27 -27
  798. package/src/types/expo-secure-store.d.ts +11 -4
  799. package/src/types/express.d.ts +22 -7
  800. package/src/ui/analytics/productAnalytics.tsx +5 -5
  801. package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +91 -29
  802. package/src/ui/boot/runProviderColdBoot.ts +12 -3
  803. package/src/ui/client.ts +31 -31
  804. package/src/ui/components/BenefitList.tsx +78 -77
  805. package/src/ui/components/BloomLocaleBridge.tsx +3 -3
  806. package/src/ui/components/FollowButton.tsx +88 -84
  807. package/src/ui/components/FollowTargetButton.tsx +15 -6
  808. package/src/ui/components/LanguageBridge.tsx +24 -19
  809. package/src/ui/components/OxyAuthChooser.tsx +17 -3
  810. package/src/ui/components/OxyAuthPrompt.tsx +79 -98
  811. package/src/ui/components/OxyConsentScreen.tsx +51 -12
  812. package/src/ui/components/OxyProvider.tsx +189 -182
  813. package/src/ui/components/OxySignInButton.tsx +415 -405
  814. package/src/ui/components/OxySignInRequestSurface.tsx +7 -1
  815. package/src/ui/components/PeableButton.tsx +57 -49
  816. package/src/ui/components/ProfileButton.tsx +158 -109
  817. package/src/ui/components/ProfileSummaryCard.tsx +64 -64
  818. package/src/ui/components/RequireOxyAuth.tsx +15 -3
  819. package/src/ui/components/SettingsIcon.tsx +24 -19
  820. package/src/ui/components/SurfaceHeaderAction.tsx +6 -1
  821. package/src/ui/components/SurfaceScreen.tsx +21 -16
  822. package/src/ui/components/__tests__/BloomLocaleBridge.test.tsx +1 -1
  823. package/src/ui/components/__tests__/LanguageBridge.test.tsx +15 -5
  824. package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +21 -11
  825. package/src/ui/components/__tests__/followButtonAccessibility.test.tsx +42 -15
  826. package/src/ui/components/__tests__/followTargetButton.test.ts +23 -20
  827. package/src/ui/components/authChooser/AccountsMenuView.tsx +70 -56
  828. package/src/ui/components/authChooser/TroubleDisclosure.tsx +6 -1
  829. package/src/ui/components/authChooser/requestSurfaces.tsx +3 -1
  830. package/src/ui/components/authChooser/types.ts +6 -1
  831. package/src/ui/components/feedback/constants.ts +36 -12
  832. package/src/ui/components/feedback/types.ts +30 -30
  833. package/src/ui/components/feedback/useFeedbackForm.ts +38 -35
  834. package/src/ui/components/fileManagement/FileDetailsModal.tsx +86 -92
  835. package/src/ui/components/fileManagement/FileViewer.tsx +362 -338
  836. package/src/ui/components/fileManagement/UploadPreview.tsx +134 -127
  837. package/src/ui/components/followButtonAccessibility.ts +6 -2
  838. package/src/ui/components/followRules.ts +6 -2
  839. package/src/ui/components/icon/FAIRWalletIcon.tsx +47 -35
  840. package/src/ui/components/logo/LogoText.tsx +6 -1
  841. package/src/ui/components/modals/DeleteAccountModal.tsx +89 -83
  842. package/src/ui/components/oauthNavigation.ts +54 -56
  843. package/src/ui/components/payment/PaymentDetailsStep.tsx +269 -201
  844. package/src/ui/components/payment/PaymentMethodStep.tsx +88 -68
  845. package/src/ui/components/payment/PaymentReviewStep.tsx +119 -102
  846. package/src/ui/components/payment/PaymentSuccessStep.tsx +48 -48
  847. package/src/ui/components/payment/PaymentSummaryStep.tsx +167 -124
  848. package/src/ui/components/payment/constants.ts +38 -23
  849. package/src/ui/components/payment/paymentStyles.ts +252 -251
  850. package/src/ui/components/payment/types.ts +24 -24
  851. package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +207 -183
  852. package/src/ui/components/photogrid/__tests__/chunkPhotos.test.ts +32 -33
  853. package/src/ui/components/signIn/InlineCommonsQr.tsx +14 -6
  854. package/src/ui/components/signIn/OxyAccountPicker.tsx +19 -4
  855. package/src/ui/components/signIn/OxyAuthScreen.tsx +21 -11
  856. package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +47 -13
  857. package/src/ui/components/signIn/OxyDeleteAccountPanel.tsx +14 -4
  858. package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +69 -15
  859. package/src/ui/components/signIn/OxyPasswordPanel.tsx +19 -4
  860. package/src/ui/components/signIn/OxySignInPanel.tsx +74 -21
  861. package/src/ui/components/signIn/OxySignUpPanel.tsx +60 -12
  862. package/src/ui/components/signIn/ReauthStep.tsx +26 -5
  863. package/src/ui/components/signIn/accountFlowParts.tsx +45 -16
  864. package/src/ui/components/signIn/artwork.ts +3 -1
  865. package/src/ui/components/signIn/signInFlowStore.ts +5 -1
  866. package/src/ui/components/surfaces/ActionSheetSurface.tsx +51 -61
  867. package/src/ui/components/utils/hasTouchHandler.tsx +3 -13
  868. package/src/ui/components/utils/roundLayoutSize.ts +1 -2
  869. package/src/ui/components/utils/splitStyles.ts +2 -5
  870. package/src/ui/context/OxyContext.tsx +324 -140
  871. package/src/ui/context/__tests__/commitSessionFlow.test.ts +8 -3
  872. package/src/ui/context/__tests__/isolatedOAuthSession.test.ts +11 -3
  873. package/src/ui/context/commitSessionFlow.ts +15 -3
  874. package/src/ui/context/hooks/useAuthOperations.ts +54 -16
  875. package/src/ui/context/isolatedOAuthSession.ts +4 -1
  876. package/src/ui/context/oxyContextTypes.ts +26 -9
  877. package/src/ui/context/useOxyAccountGraph.ts +26 -4
  878. package/src/ui/hooks/accountDialogSnapshot.ts +3 -1
  879. package/src/ui/hooks/accountQueryPersistence.ts +16 -5
  880. package/src/ui/hooks/mutations/mutationFactory.ts +11 -6
  881. package/src/ui/hooks/mutations/useAccountMutations.ts +77 -54
  882. package/src/ui/hooks/mutations/useServicesMutations.ts +10 -11
  883. package/src/ui/hooks/persistedQueryCache.ts +10 -2
  884. package/src/ui/hooks/queries/__tests__/userCache.test.ts +72 -17
  885. package/src/ui/hooks/queries/__tests__/userCacheClear.test.ts +18 -23
  886. package/src/ui/hooks/queries/paymentTypes.ts +2 -2
  887. package/src/ui/hooks/queries/queryKeys.ts +5 -5
  888. package/src/ui/hooks/queries/useAccountQueries.ts +12 -17
  889. package/src/ui/hooks/queries/useFileQueries.ts +88 -92
  890. package/src/ui/hooks/queries/usePaymentQueries.ts +13 -26
  891. package/src/ui/hooks/queries/usePersonalPlans.ts +14 -6
  892. package/src/ui/hooks/queries/useSecurityQueries.ts +16 -29
  893. package/src/ui/hooks/queries/useServicesQueries.ts +17 -23
  894. package/src/ui/hooks/queries/userCache.ts +249 -270
  895. package/src/ui/hooks/queryClient.ts +9 -5
  896. package/src/ui/hooks/useAssets.ts +166 -160
  897. package/src/ui/hooks/useAsyncAction.ts +92 -91
  898. package/src/ui/hooks/useAuth.ts +45 -42
  899. package/src/ui/hooks/useAvatarPicker.ts +2 -5
  900. package/src/ui/hooks/useDeviceManagement.ts +0 -2
  901. package/src/ui/hooks/useDeviceSwitcher.ts +73 -68
  902. package/src/ui/hooks/useFileFiltering.ts +2 -2
  903. package/src/ui/hooks/useFollow.ts +139 -67
  904. package/src/ui/hooks/useFollowTarget.ts +11 -11
  905. package/src/ui/hooks/useI18n.ts +7 -2
  906. package/src/ui/hooks/useProfileEditing.ts +167 -155
  907. package/src/ui/hooks/useQueryClient.ts +2 -3
  908. package/src/ui/hooks/useReduceMotion.ts +21 -21
  909. package/src/ui/hooks/useResolvedFileUrls.ts +5 -1
  910. package/src/ui/hooks/useSessionManagement.ts +3 -1
  911. package/src/ui/hooks/useSettingToggle.ts +122 -114
  912. package/src/ui/hooks/useStorage.ts +1 -4
  913. package/src/ui/hooks/useSurfaceFrameWidth.ts +3 -1
  914. package/src/ui/icons/Ionicons.ts +1 -5
  915. package/src/ui/icons/MaterialCommunityIcons.ts +1 -5
  916. package/src/ui/icons/__tests__/decorativeIconSet.test.tsx +3 -1
  917. package/src/ui/icons/decorativeIconSet.ts +13 -2
  918. package/src/ui/index.ts +4 -4
  919. package/src/ui/isFrontend.ts +3 -2
  920. package/src/ui/navigation/__tests__/getSurfaceConfig.test.ts +83 -83
  921. package/src/ui/navigation/__tests__/surfaceNavStack.test.ts +8 -2
  922. package/src/ui/navigation/bottomSheetManager.ts +1 -1
  923. package/src/ui/navigation/routes.ts +90 -88
  924. package/src/ui/navigation/surfaces.ts +7 -6
  925. package/src/ui/oauth/__tests__/browserAuthTransport.test.ts +18 -11
  926. package/src/ui/oauth/__tests__/browserBridge.test.ts +108 -21
  927. package/src/ui/oauth/__tests__/completeOAuthCode.test.ts +9 -6
  928. package/src/ui/oauth/__tests__/explicitOAuthConsent.test.ts +222 -241
  929. package/src/ui/oauth/__tests__/nativeAuthTransport.test.ts +118 -35
  930. package/src/ui/oauth/__tests__/oauthBearerCommit.http.test.ts +60 -18
  931. package/src/ui/oauth/__tests__/oauthPopup.test.ts +2 -7
  932. package/src/ui/oauth/__tests__/oauthPopupMessages.test.ts +9 -7
  933. package/src/ui/oauth/__tests__/sharedCompletionPath.test.ts +4 -5
  934. package/src/ui/oauth/applicationSessionLane.ts +12 -3
  935. package/src/ui/oauth/browserAuthTransport.ts +15 -7
  936. package/src/ui/oauth/browserBridge.ts +22 -5
  937. package/src/ui/oauth/completeOAuthCode.ts +9 -6
  938. package/src/ui/oauth/nativeAuthTransport.ts +73 -20
  939. package/src/ui/oauth/oauthHandshake.ts +6 -1
  940. package/src/ui/oauth/oauthPopup.ts +6 -4
  941. package/src/ui/oauth/oauthPopupMessages.ts +3 -4
  942. package/src/ui/oauth/types.ts +1 -3
  943. package/src/ui/runtime/createOxyRuntime.ts +21 -4
  944. package/src/ui/screens/AccountMembersScreen.tsx +274 -204
  945. package/src/ui/screens/AccountSecurityPanelScreen.tsx +3 -1
  946. package/src/ui/screens/AccountSettingsScreen.tsx +133 -113
  947. package/src/ui/screens/AccountVerificationScreen.tsx +176 -161
  948. package/src/ui/screens/AppInfoScreen.tsx +360 -304
  949. package/src/ui/screens/AvatarCropScreen.tsx +977 -988
  950. package/src/ui/screens/ChangeAvatarScreen.tsx +257 -253
  951. package/src/ui/screens/ConnectedAppsScreen.tsx +117 -130
  952. package/src/ui/screens/CreateAccountScreen.tsx +173 -129
  953. package/src/ui/screens/DomainsScreen.tsx +157 -140
  954. package/src/ui/screens/EditProfileFieldScreen.tsx +693 -663
  955. package/src/ui/screens/EditProfileScreen.tsx +106 -100
  956. package/src/ui/screens/FileManagementScreen.tsx +1505 -1162
  957. package/src/ui/screens/HelpSupportScreen.tsx +88 -75
  958. package/src/ui/screens/HistoryViewScreen.tsx +143 -102
  959. package/src/ui/screens/LanguageSelectorScreen.tsx +206 -199
  960. package/src/ui/screens/LearnMoreUsernamesScreen.tsx +92 -110
  961. package/src/ui/screens/LegalDocumentsScreen.tsx +134 -193
  962. package/src/ui/screens/ManageAccountScreen.tsx +649 -871
  963. package/src/ui/screens/NotificationsScreen.tsx +111 -143
  964. package/src/ui/screens/PaymentGatewayScreen.tsx +229 -244
  965. package/src/ui/screens/PreferencesScreen.tsx +153 -196
  966. package/src/ui/screens/PrivacySettingsScreen.tsx +509 -351
  967. package/src/ui/screens/ProfileScreen.tsx +416 -319
  968. package/src/ui/screens/SearchSettingsScreen.tsx +100 -98
  969. package/src/ui/screens/UserLinksScreen.tsx +48 -43
  970. package/src/ui/screens/UserListScreen.tsx +53 -30
  971. package/src/ui/screens/WelcomeNewUserScreen.tsx +430 -334
  972. package/src/ui/screens/__tests__/linkFormat.test.ts +58 -62
  973. package/src/ui/screens/__tests__/photoGridLayout.test.ts +43 -47
  974. package/src/ui/screens/fileManagement/FileLibraryError.tsx +33 -27
  975. package/src/ui/screens/fileManagement/FileListSection.tsx +96 -87
  976. package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +508 -475
  977. package/src/ui/screens/fileManagement/UploadBar.tsx +26 -16
  978. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +514 -490
  979. package/src/ui/screens/fileManagement/photoGridLayout.ts +13 -13
  980. package/src/ui/screens/fileManagement/shared.ts +40 -38
  981. package/src/ui/screens/linkFormat.ts +14 -14
  982. package/src/ui/screens/trust/TrustAboutScreen.tsx +53 -51
  983. package/src/ui/screens/trust/TrustCenterScreen.tsx +156 -214
  984. package/src/ui/screens/trust/TrustFAQScreen.tsx +60 -67
  985. package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +183 -183
  986. package/src/ui/screens/trust/TrustRewardsScreen.tsx +495 -494
  987. package/src/ui/screens/trust/TrustRulesScreen.tsx +84 -85
  988. package/src/ui/server.ts +12 -15
  989. package/src/ui/session/__tests__/authStore.test.ts +6 -1
  990. package/src/ui/session/__tests__/createSessionClient.test.ts +22 -8
  991. package/src/ui/session/__tests__/sharedDeviceCredentialStore.test.ts +24 -6
  992. package/src/ui/session/__tests__/tokenLossRecovery.test.ts +3 -1
  993. package/src/ui/session/__tests__/tokenTransport.test.ts +9 -2
  994. package/src/ui/session/authStore.ts +7 -1
  995. package/src/ui/session/backgroundSession.ts +4 -3
  996. package/src/ui/session/createSessionClient.ts +17 -5
  997. package/src/ui/session/deviceCredentialTracker.ts +12 -6
  998. package/src/ui/session/identityBinding.ts +6 -1
  999. package/src/ui/session/sharedDeviceCredentialStore.ts +15 -4
  1000. package/src/ui/session/tokenLossRecovery.ts +24 -10
  1001. package/src/ui/session/tokenTransport.ts +5 -1
  1002. package/src/ui/stores/__tests__/followStore.test.ts +20 -7
  1003. package/src/ui/stores/assetStore.ts +209 -207
  1004. package/src/ui/stores/followStore.ts +80 -39
  1005. package/src/ui/stores/followTargetStore.ts +1 -1
  1006. package/src/ui/types/fileManagement.ts +48 -49
  1007. package/src/ui/types/navigation.ts +168 -168
  1008. package/src/ui/types/surfaceScreen.ts +4 -1
  1009. package/src/ui/utils/__tests__/deviceSessionTitle.test.ts +4 -2
  1010. package/src/ui/utils/accountDeletionHandoff.ts +14 -3
  1011. package/src/ui/utils/avatarUtils.ts +7 -6
  1012. package/src/ui/utils/clipboard.native.ts +23 -6
  1013. package/src/ui/utils/commonsStoreLinks.ts +2 -1
  1014. package/src/ui/utils/errorHandlers.ts +7 -12
  1015. package/src/ui/utils/fileManagement.ts +92 -103
  1016. package/src/ui/utils/iconNames.ts +60 -60
  1017. package/src/ui/utils/sessionHelpers.ts +1 -6
  1018. package/src/ui/utils/storageHelpers.ts +0 -1
  1019. package/src/ui/utils/userUtils.ts +2 -6
  1020. package/src/utils/hookUtils.ts +77 -65
  1021. package/lib/commonjs/ui/components/styles/overlay.js +0 -83
  1022. package/lib/commonjs/ui/components/styles/overlay.js.map +0 -1
  1023. package/lib/commonjs/ui/components/styles/shadow.js +0 -127
  1024. package/lib/commonjs/ui/components/styles/shadow.js.map +0 -1
  1025. package/lib/commonjs/ui/constants/spacing.js +0 -50
  1026. package/lib/commonjs/ui/constants/spacing.js.map +0 -1
  1027. package/lib/module/ui/components/styles/overlay.js +0 -80
  1028. package/lib/module/ui/components/styles/overlay.js.map +0 -1
  1029. package/lib/module/ui/components/styles/shadow.js +0 -123
  1030. package/lib/module/ui/components/styles/shadow.js.map +0 -1
  1031. package/lib/module/ui/constants/spacing.js +0 -45
  1032. package/lib/module/ui/constants/spacing.js.map +0 -1
  1033. package/lib/typescript/commonjs/ui/components/styles/overlay.d.ts +0 -6
  1034. package/lib/typescript/commonjs/ui/components/styles/overlay.d.ts.map +0 -1
  1035. package/lib/typescript/commonjs/ui/components/styles/shadow.d.ts +0 -3
  1036. package/lib/typescript/commonjs/ui/components/styles/shadow.d.ts.map +0 -1
  1037. package/lib/typescript/commonjs/ui/constants/spacing.d.ts +0 -33
  1038. package/lib/typescript/commonjs/ui/constants/spacing.d.ts.map +0 -1
  1039. package/lib/typescript/module/ui/components/styles/overlay.d.ts +0 -6
  1040. package/lib/typescript/module/ui/components/styles/overlay.d.ts.map +0 -1
  1041. package/lib/typescript/module/ui/components/styles/shadow.d.ts +0 -3
  1042. package/lib/typescript/module/ui/components/styles/shadow.d.ts.map +0 -1
  1043. package/lib/typescript/module/ui/constants/spacing.d.ts +0 -33
  1044. package/lib/typescript/module/ui/constants/spacing.d.ts.map +0 -1
  1045. package/src/ui/components/styles/overlay.tsx +0 -97
  1046. package/src/ui/components/styles/shadow.tsx +0 -131
  1047. package/src/ui/constants/spacing.ts +0 -45
@@ -28,17 +28,25 @@
28
28
 
29
29
  import type React from 'react';
30
30
  import { useCallback, useMemo, useRef, useState } from 'react';
31
- import { View, StyleSheet, ActivityIndicator, Image, Platform, AccessibilityInfo, Pressable } from 'react-native';
31
+ import {
32
+ View,
33
+ StyleSheet,
34
+ ActivityIndicator,
35
+ Image,
36
+ Platform,
37
+ AccessibilityInfo,
38
+ Pressable,
39
+ } from 'react-native';
32
40
  import { Text } from '@oxy.so/bloom/typography';
33
41
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
34
42
  import Animated, {
35
- Easing,
36
- runOnJS,
37
- useAnimatedStyle,
38
- useSharedValue,
39
- withDelay,
40
- withSpring,
41
- withTiming,
43
+ Easing,
44
+ runOnJS,
45
+ useAnimatedStyle,
46
+ useSharedValue,
47
+ withDelay,
48
+ withSpring,
49
+ withTiming,
42
50
  } from 'react-native-reanimated';
43
51
  import { useQuery, keepPreviousData } from '@tanstack/react-query';
44
52
  import { useTheme } from '@oxy.so/bloom/theme';
@@ -55,30 +63,30 @@ const LOG_COMPONENT = 'AvatarCropScreen';
55
63
 
56
64
  /** Final crop result handed back to the caller. */
57
65
  export interface AvatarCropResult {
58
- /** File URI to the cropped 512x512 JPEG on disk. */
59
- uri: string;
60
- /** Output width (always 512). */
61
- width: number;
62
- /** Output height (always 512). */
63
- height: number;
64
- /** MIME type (always `image/jpeg`). */
65
- mime: 'image/jpeg';
66
+ /** File URI to the cropped 512x512 JPEG on disk. */
67
+ uri: string;
68
+ /** Output width (always 512). */
69
+ width: number;
70
+ /** Output height (always 512). */
71
+ height: number;
72
+ /** MIME type (always `image/jpeg`). */
73
+ mime: 'image/jpeg';
66
74
  }
67
75
 
68
76
  export interface AvatarCropScreenProps extends BaseScreenProps {
69
- /** URI of the source image to crop (device / camera sources pass this). */
70
- imageUri?: string;
71
- /**
72
- * ID of an existing Oxy file to crop (the "My Oxy files" source passes this
73
- * instead of a URL). Its download URL is usually private, so it is resolved
74
- * HERE — showing this screen's own loading state on the dark canvas — rather
75
- * than in the source list, which would otherwise linger for the round-trip.
76
- */
77
- imageFileId?: string;
78
- /** Natural width of the source image (optional — measured if absent). */
79
- sourceWidth?: number;
80
- /** Natural height of the source image (optional — measured if absent). */
81
- sourceHeight?: number;
77
+ /** URI of the source image to crop (device / camera sources pass this). */
78
+ imageUri?: string;
79
+ /**
80
+ * ID of an existing Oxy file to crop (the "My Oxy files" source passes this
81
+ * instead of a URL). Its download URL is usually private, so it is resolved
82
+ * HERE — showing this screen's own loading state on the dark canvas — rather
83
+ * than in the source list, which would otherwise linger for the round-trip.
84
+ */
85
+ imageFileId?: string;
86
+ /** Natural width of the source image (optional — measured if absent). */
87
+ sourceWidth?: number;
88
+ /** Natural height of the source image (optional — measured if absent). */
89
+ sourceHeight?: number;
82
90
  }
83
91
 
84
92
  /** Side length (in dp) of the crop viewport. The output is always 512x512px. */
@@ -110,26 +118,26 @@ const REMOTE_HTTP_URI_PATTERN = /^https?:\/\//i;
110
118
  * gesture handlers can reuse it without re-creating the closure each render.
111
119
  */
112
120
  function clampTranslation(
113
- tx: number,
114
- ty: number,
115
- s: number,
116
- baseW: number,
117
- baseH: number,
121
+ tx: number,
122
+ ty: number,
123
+ s: number,
124
+ baseW: number,
125
+ baseH: number,
118
126
  ): { tx: number; ty: number } {
119
- 'worklet';
120
- const scaledW = baseW * s;
121
- const scaledH = baseH * s;
122
- const maxX = Math.max(0, (scaledW - VIEWPORT_SIZE) / 2);
123
- const maxY = Math.max(0, (scaledH - VIEWPORT_SIZE) / 2);
124
- return {
125
- tx: Math.min(Math.max(tx, -maxX), maxX),
126
- ty: Math.min(Math.max(ty, -maxY), maxY),
127
- };
127
+ 'worklet';
128
+ const scaledW = baseW * s;
129
+ const scaledH = baseH * s;
130
+ const maxX = Math.max(0, (scaledW - VIEWPORT_SIZE) / 2);
131
+ const maxY = Math.max(0, (scaledH - VIEWPORT_SIZE) / 2);
132
+ return {
133
+ tx: Math.min(Math.max(tx, -maxX), maxX),
134
+ ty: Math.min(Math.max(ty, -maxY), maxY),
135
+ };
128
136
  }
129
137
 
130
138
  interface ImageNaturalSize {
131
- width: number;
132
- height: number;
139
+ width: number;
140
+ height: number;
133
141
  }
134
142
 
135
143
  /**
@@ -139,19 +147,19 @@ interface ImageNaturalSize {
139
147
  * Mirrors the `measurePhotoDimensions` helper the photo grid uses.
140
148
  */
141
149
  function measureImageSize(uri: string): Promise<ImageNaturalSize> {
142
- return new Promise<ImageNaturalSize>((resolve, reject) => {
143
- Image.getSize(
144
- uri,
145
- (width, height) => {
146
- if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
147
- reject(new Error('Image reported invalid dimensions'));
148
- return;
149
- }
150
- resolve({ width, height });
151
- },
152
- () => reject(new Error('Image measurement failed')),
153
- );
154
- });
150
+ return new Promise<ImageNaturalSize>((resolve, reject) => {
151
+ Image.getSize(
152
+ uri,
153
+ (width, height) => {
154
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
155
+ reject(new Error('Image reported invalid dimensions'));
156
+ return;
157
+ }
158
+ resolve({ width, height });
159
+ },
160
+ () => reject(new Error('Image measurement failed')),
161
+ );
162
+ });
155
163
  }
156
164
 
157
165
  /**
@@ -160,97 +168,97 @@ function measureImageSize(uri: string): Promise<ImageNaturalSize> {
160
168
  * upstream if the consuming app has not installed it.
161
169
  */
162
170
  interface ImageManipulatorModule {
163
- manipulateAsync: (
164
- uri: string,
165
- actions: Array<{
166
- crop?: { originX: number; originY: number; width: number; height: number };
167
- resize?: { width?: number; height?: number };
168
- }>,
169
- saveOptions?: { format?: 'jpeg' | 'png' | 'webp'; compress?: number },
170
- ) => Promise<{ uri: string; width: number; height: number }>;
171
- SaveFormat: { JPEG: 'jpeg'; PNG: 'png'; WEBP: 'webp' };
171
+ manipulateAsync: (
172
+ uri: string,
173
+ actions: Array<{
174
+ crop?: { originX: number; originY: number; width: number; height: number };
175
+ resize?: { width?: number; height?: number };
176
+ }>,
177
+ saveOptions?: { format?: 'jpeg' | 'png' | 'webp'; compress?: number },
178
+ ) => Promise<{ uri: string; width: number; height: number }>;
179
+ SaveFormat: { JPEG: 'jpeg'; PNG: 'png'; WEBP: 'webp' };
172
180
  }
173
181
 
174
182
  async function loadImageManipulator(): Promise<ImageManipulatorModule> {
175
- try {
176
- const mod = (await import('expo-image-manipulator')) as unknown as ImageManipulatorModule;
177
- if (!mod || typeof mod.manipulateAsync !== 'function') {
178
- throw new Error('expo-image-manipulator did not export manipulateAsync');
179
- }
180
- return mod;
181
- } catch (err) {
182
- const message = err instanceof Error ? err.message : String(err);
183
- throw new Error(
184
- `expo-image-manipulator is required for avatar cropping but is not installed: ${message}`,
185
- );
183
+ try {
184
+ const mod = (await import('expo-image-manipulator')) as unknown as ImageManipulatorModule;
185
+ if (!mod || typeof mod.manipulateAsync !== 'function') {
186
+ throw new Error('expo-image-manipulator did not export manipulateAsync');
186
187
  }
188
+ return mod;
189
+ } catch (err) {
190
+ const message = err instanceof Error ? err.message : String(err);
191
+ throw new Error(
192
+ `expo-image-manipulator is required for avatar cropping but is not installed: ${message}`,
193
+ );
194
+ }
187
195
  }
188
196
 
189
197
  interface PreparedManipulatorImage {
190
- uri: string;
191
- cleanup?: () => void;
198
+ uri: string;
199
+ cleanup?: () => void;
192
200
  }
193
201
 
194
202
  function isCanvasLoadFailure(err: unknown): boolean {
195
- return (
196
- Platform.OS === 'web' &&
197
- typeof HTMLCanvasElement !== 'undefined' &&
198
- err instanceof HTMLCanvasElement
199
- );
203
+ return (
204
+ Platform.OS === 'web' &&
205
+ typeof HTMLCanvasElement !== 'undefined' &&
206
+ err instanceof HTMLCanvasElement
207
+ );
200
208
  }
201
209
 
202
210
  const SENSITIVE_URI_PATTERN =
203
- /\b(?:file|content|ph|assets-library):\/\/\S+|\/(?:data|var|private|storage|sdcard|tmp)\/\S+/gi;
211
+ /\b(?:file|content|ph|assets-library):\/\/\S+|\/(?:data|var|private|storage|sdcard|tmp)\/\S+/gi;
204
212
 
205
213
  function sanitizeImageErrorMessage(message: string): string {
206
- return message.replace(SENSITIVE_URI_PATTERN, '[redacted-uri]');
214
+ return message.replace(SENSITIVE_URI_PATTERN, '[redacted-uri]');
207
215
  }
208
216
 
209
217
  function normalizeCropError(err: unknown): Error {
210
- if (isCanvasLoadFailure(err)) {
211
- return new Error('Image could not be loaded for cropping');
212
- }
213
- if (err instanceof Error) {
214
- return new Error(sanitizeImageErrorMessage(err.message || 'Failed to crop image'));
215
- }
216
- if (typeof err === 'string' && err.trim()) {
217
- return new Error(sanitizeImageErrorMessage(err.trim()));
218
- }
219
- return new Error('Failed to crop image');
218
+ if (isCanvasLoadFailure(err)) {
219
+ return new Error('Image could not be loaded for cropping');
220
+ }
221
+ if (err instanceof Error) {
222
+ return new Error(sanitizeImageErrorMessage(err.message || 'Failed to crop image'));
223
+ }
224
+ if (typeof err === 'string' && err.trim()) {
225
+ return new Error(sanitizeImageErrorMessage(err.trim()));
226
+ }
227
+ return new Error('Failed to crop image');
220
228
  }
221
229
 
222
230
  async function prepareImageForManipulator(uri: string): Promise<PreparedManipulatorImage> {
223
- if (Platform.OS !== 'web' || !REMOTE_HTTP_URI_PATTERN.test(uri)) {
224
- return { uri };
225
- }
226
-
227
- let response: Response;
228
- try {
229
- response = await fetch(uri);
230
- } catch (err) {
231
- const message = err instanceof Error ? err.message : String(err);
232
- throw new Error(`Image could not be loaded for cropping: ${message}`);
233
- }
234
-
235
- if (!response.ok) {
236
- throw new Error(`Image could not be loaded for cropping (${response.status})`);
237
- }
238
-
239
- const blob = await response.blob();
240
- if (blob.size <= 0) {
241
- throw new Error('Image could not be loaded for cropping');
242
- }
243
-
244
- const contentType = response.headers.get('content-type') || blob.type;
245
- if (contentType && !contentType.toLowerCase().startsWith('image/')) {
246
- throw new Error('Selected file is not an image');
247
- }
248
-
249
- const objectUrl = URL.createObjectURL(blob);
250
- return {
251
- uri: objectUrl,
252
- cleanup: () => URL.revokeObjectURL(objectUrl),
253
- };
231
+ if (Platform.OS !== 'web' || !REMOTE_HTTP_URI_PATTERN.test(uri)) {
232
+ return { uri };
233
+ }
234
+
235
+ let response: Response;
236
+ try {
237
+ response = await fetch(uri);
238
+ } catch (err) {
239
+ const message = err instanceof Error ? err.message : String(err);
240
+ throw new Error(`Image could not be loaded for cropping: ${message}`);
241
+ }
242
+
243
+ if (!response.ok) {
244
+ throw new Error(`Image could not be loaded for cropping (${response.status})`);
245
+ }
246
+
247
+ const blob = await response.blob();
248
+ if (blob.size <= 0) {
249
+ throw new Error('Image could not be loaded for cropping');
250
+ }
251
+
252
+ const contentType = response.headers.get('content-type') || blob.type;
253
+ if (contentType && !contentType.toLowerCase().startsWith('image/')) {
254
+ throw new Error('Selected file is not an image');
255
+ }
256
+
257
+ const objectUrl = URL.createObjectURL(blob);
258
+ return {
259
+ uri: objectUrl,
260
+ cleanup: () => URL.revokeObjectURL(objectUrl),
261
+ };
254
262
  }
255
263
 
256
264
  /**
@@ -261,901 +269,882 @@ async function prepareImageForManipulator(uri: string): Promise<PreparedManipula
261
269
  type HapticImpact = 'light' | 'medium' | 'heavy';
262
270
  type HapticNotification = 'success' | 'warning' | 'error';
263
271
  interface HapticsModule {
264
- impactAsync: (style: unknown) => Promise<void>;
265
- notificationAsync: (type: unknown) => Promise<void>;
266
- selectionAsync: () => Promise<void>;
267
- ImpactFeedbackStyle: { Light: unknown; Medium: unknown; Heavy: unknown };
268
- NotificationFeedbackType: { Success: unknown; Warning: unknown; Error: unknown };
272
+ impactAsync: (style: unknown) => Promise<void>;
273
+ notificationAsync: (type: unknown) => Promise<void>;
274
+ selectionAsync: () => Promise<void>;
275
+ ImpactFeedbackStyle: { Light: unknown; Medium: unknown; Heavy: unknown };
276
+ NotificationFeedbackType: { Success: unknown; Warning: unknown; Error: unknown };
269
277
  }
270
278
 
271
279
  let hapticsModulePromise: Promise<HapticsModule | null> | null = null;
272
280
  function getHaptics(): Promise<HapticsModule | null> {
273
- if (Platform.OS === 'web') return Promise.resolve(null);
274
- if (hapticsModulePromise) return hapticsModulePromise;
275
- hapticsModulePromise = (async () => {
276
- try {
277
- const mod = (await import('expo-haptics')) as unknown as HapticsModule;
278
- if (!mod || typeof mod.impactAsync !== 'function') return null;
279
- return mod;
280
- } catch {
281
- return null;
282
- }
283
- })();
284
- return hapticsModulePromise;
285
- }
286
-
287
- async function hapticImpact(style: HapticImpact): Promise<void> {
288
- const h = await getHaptics();
289
- if (!h) return;
290
- const styleEnum =
291
- style === 'heavy'
292
- ? h.ImpactFeedbackStyle.Heavy
293
- : style === 'medium'
294
- ? h.ImpactFeedbackStyle.Medium
295
- : h.ImpactFeedbackStyle.Light;
281
+ if (Platform.OS === 'web') return Promise.resolve(null);
282
+ if (hapticsModulePromise) return hapticsModulePromise;
283
+ hapticsModulePromise = (async () => {
296
284
  try {
297
- await h.impactAsync(styleEnum);
285
+ const mod = (await import('expo-haptics')) as unknown as HapticsModule;
286
+ if (!mod || typeof mod.impactAsync !== 'function') return null;
287
+ return mod;
298
288
  } catch {
299
- // Silent — haptics are non-critical UX polish.
289
+ return null;
300
290
  }
291
+ })();
292
+ return hapticsModulePromise;
293
+ }
294
+
295
+ async function hapticImpact(style: HapticImpact): Promise<void> {
296
+ const h = await getHaptics();
297
+ if (!h) return;
298
+ const styleEnum =
299
+ style === 'heavy'
300
+ ? h.ImpactFeedbackStyle.Heavy
301
+ : style === 'medium'
302
+ ? h.ImpactFeedbackStyle.Medium
303
+ : h.ImpactFeedbackStyle.Light;
304
+ try {
305
+ await h.impactAsync(styleEnum);
306
+ } catch {
307
+ // Silent — haptics are non-critical UX polish.
308
+ }
301
309
  }
302
310
 
303
311
  async function hapticNotification(type: HapticNotification): Promise<void> {
304
- const h = await getHaptics();
305
- if (!h) return;
306
- const typeEnum =
307
- type === 'error'
308
- ? h.NotificationFeedbackType.Error
309
- : type === 'warning'
310
- ? h.NotificationFeedbackType.Warning
311
- : h.NotificationFeedbackType.Success;
312
- try {
313
- await h.notificationAsync(typeEnum);
314
- } catch {
315
- // Silent.
316
- }
312
+ const h = await getHaptics();
313
+ if (!h) return;
314
+ const typeEnum =
315
+ type === 'error'
316
+ ? h.NotificationFeedbackType.Error
317
+ : type === 'warning'
318
+ ? h.NotificationFeedbackType.Warning
319
+ : h.NotificationFeedbackType.Success;
320
+ try {
321
+ await h.notificationAsync(typeEnum);
322
+ } catch {
323
+ // Silent.
324
+ }
317
325
  }
318
326
 
319
327
  async function hapticSelection(): Promise<void> {
320
- const h = await getHaptics();
321
- if (!h) return;
322
- try {
323
- await h.selectionAsync();
324
- } catch {
325
- // Silent.
326
- }
328
+ const h = await getHaptics();
329
+ if (!h) return;
330
+ try {
331
+ await h.selectionAsync();
332
+ } catch {
333
+ // Silent.
334
+ }
327
335
  }
328
336
 
329
337
  const AvatarCropScreen: React.FC<AvatarCropScreenProps> = ({
330
- dismiss,
331
- goBack,
332
- imageUri,
333
- imageFileId,
334
- sourceWidth,
335
- sourceHeight,
338
+ dismiss,
339
+ goBack,
340
+ imageUri,
341
+ imageFileId,
342
+ sourceWidth,
343
+ sourceHeight,
336
344
  }) => {
337
- const theme = useTheme();
338
- const { t } = useI18n();
339
- const { oxyServices } = useOxy();
340
- // Reactive OS reduce-motion preference (useSyncExternalStore, no effect). Pins
341
- // the entrance spring off. Declared before `entrance` so its initial rest value
342
- // is correct under reduce-motion.
343
- const reduceMotion = useReduceMotion();
344
-
345
- // The "My Oxy files" source hands us a file ID instead of a ready URL. Its
346
- // download URL is usually private, so it must be resolved through the SDK
347
- // (which throws on failure). Resolving it HERE — not in the source list — is
348
- // what keeps the list from lingering on screen for the round-trip: this screen
349
- // shows its own spinner on the dark canvas while the URL resolves. A React
350
- // Query read (not an effect) so it stays declarative and cached.
351
- const shouldResolveFile = !imageUri && !!imageFileId;
352
- const sourceQuery = useQuery({
353
- queryKey: ['avatarCropSource', imageFileId],
354
- enabled: shouldResolveFile,
355
- staleTime: Number.POSITIVE_INFINITY,
356
- retry: false,
357
- queryFn: async () => {
358
- if (!imageFileId) {
359
- throw new Error('No file id to resolve for cropping');
360
- }
361
- const resolved = await oxyServices.assets.url(imageFileId);
362
- if (!resolved) {
363
- throw new Error('No download URL returned for the selected image');
364
- }
365
- return resolved;
366
- },
367
- });
368
-
369
- // The URL the cropper actually loads: the passed `imageUri`, else the URL the
370
- // file-ID query resolved. While it resolves, `effectiveUri` is undefined and
371
- // the stage shows its own spinner (never the empty state).
372
- const effectiveUri = imageUri ?? sourceQuery.data;
373
- const isResolvingSource = shouldResolveFile && !sourceQuery.data && !sourceQuery.isError;
374
- const sourceErrorMessage = sourceQuery.isError
375
- ? t('changeAvatar.errors.loadImageFailed') || 'Could not load the selected image'
376
- : null;
377
-
378
- // Natural size of the source image. Known up front (device/camera sources pass
379
- // the dimensions) OR measured lazily from `effectiveUri` via a React Query read
380
- // (no effect) — the same Image.getSize→useQuery pattern the photo grid uses.
381
- const hasPassedDimensions = !!(sourceWidth && sourceHeight);
382
- const measureQuery = useQuery({
383
- queryKey: ['avatarCropMeasure', effectiveUri],
384
- enabled: !!effectiveUri && !hasPassedDimensions,
385
- queryFn: () => {
386
- if (!effectiveUri) {
387
- throw new Error('No image to measure');
388
- }
389
- return measureImageSize(effectiveUri);
390
- },
391
- staleTime: Number.POSITIVE_INFINITY,
392
- retry: false,
393
- placeholderData: keepPreviousData,
394
- });
395
- const naturalSize = useMemo<ImageNaturalSize | null>(() => {
396
- if (sourceWidth && sourceHeight) return { width: sourceWidth, height: sourceHeight };
397
- return measureQuery.data ?? null;
398
- }, [sourceWidth, sourceHeight, measureQuery.data]);
399
- /** Measurement failed (bad dimensions / getSize error) — surfaced via the empty UI. */
400
- const measureErrorMessage = measureQuery.isError
401
- ? t('editProfile.toasts.cropMeasureFailed') || 'Could not measure the image'
402
- : null;
403
-
404
- const [isProcessing, setIsProcessing] = useState(false);
405
- const [zoomLabel, setZoomLabel] = useState('1.0');
406
- /** True when scale != MIN_SCALE OR translate != 0 — used to reveal the reset link. */
407
- const [isModified, setIsModified] = useState(false);
408
-
409
- // Shared values for the active gesture transform.
410
- const scale = useSharedValue(MIN_SCALE);
411
- const translateX = useSharedValue(0);
412
- const translateY = useSharedValue(0);
413
-
414
- // Entrance scale of the crop circle (pulse-in on mount).
415
- const entrance = useSharedValue(reduceMotion ? 1 : 0.95);
416
- /** 0..1 opacity for the rule-of-thirds grid and zoom chip. */
417
- const gridOpacity = useSharedValue(0);
418
- const zoomChipOpacity = useSharedValue(0);
419
-
420
- // Refs that mirror the latest committed shared values so the JS-side
421
- // confirm handler can read them without an extra `useSharedValue → react`
422
- // bridge. Updated by `runOnJS(commit*)` from the gesture worklets.
423
- const committedScale = useRef(MIN_SCALE);
424
- const committedTranslateX = useRef(0);
425
- const committedTranslateY = useRef(0);
426
-
427
- // Saved start values for relative gesture math.
428
- const savedScale = useSharedValue(MIN_SCALE);
429
- const savedTranslateX = useSharedValue(0);
430
- const savedTranslateY = useSharedValue(0);
431
-
432
- // Track whether we've already announced a min/max bound during the current
433
- // pinch so selection haptics don't fire on every frame.
434
- const hitMinRef = useRef(false);
435
- const hitMaxRef = useRef(false);
436
-
437
- // The image is rendered at "cover" relative to the viewport. We compute
438
- // `baseScale` so 1.0x means the image exactly covers the square; everything
439
- // larger than that is user-zoom.
440
- const baseFit = useMemo((): { width: number; height: number } | null => {
441
- if (!naturalSize) return null;
442
- const ratio = naturalSize.width / naturalSize.height;
443
- // Cover: the smaller dimension matches the viewport.
444
- if (ratio >= 1) {
445
- // Landscape — height fills viewport
446
- return { width: VIEWPORT_SIZE * ratio, height: VIEWPORT_SIZE };
447
- }
448
- return { width: VIEWPORT_SIZE, height: VIEWPORT_SIZE / ratio };
449
- }, [naturalSize]);
450
-
451
- // Entrance pulse, driven from the crop frame's ref callback (fires on mount)
452
- // rather than an effect — the media-cleanup `startEntrance` pattern. Reduce
453
- // motion pins the frame at rest; otherwise it springs in from 0.95→1. The
454
- // callback depends on `reduceMotion`, so if that preference resolves/flips the
455
- // callback re-runs with the node and re-pins (snap to 1) — matching the old
456
- // effect that snapped the entrance to rest when reduce-motion turned on.
457
- const startEntrance = useCallback(
458
- (node: unknown): void => {
459
- if (!node) return; // unmount
460
- if (reduceMotion) {
461
- entrance.value = 1;
462
- return;
463
- }
464
- entrance.value = withSpring(1, { damping: 14, stiffness: 180, mass: 0.9 });
465
- },
466
- [entrance, reduceMotion],
467
- );
468
-
469
- const commitTransform = useCallback((s: number, tx: number, ty: number) => {
470
- committedScale.current = s;
471
- committedTranslateX.current = tx;
472
- committedTranslateY.current = ty;
473
- const modified =
474
- Math.abs(s - MIN_SCALE) > 0.001 || Math.abs(tx) > 0.5 || Math.abs(ty) > 0.5;
475
- setIsModified(modified);
476
- setZoomLabel(s.toFixed(1));
477
- }, []);
478
-
479
- /** Show the rule-of-thirds grid; called from gesture worklets via runOnJS-free path. */
480
- const showGrid = useCallback(() => {
481
- gridOpacity.value = withTiming(1, { duration: 120, easing: Easing.out(Easing.quad) });
482
- }, [gridOpacity]);
483
-
484
- const hideGrid = useCallback(() => {
485
- gridOpacity.value = withDelay(
486
- GRID_FADE_DELAY_MS,
487
- withTiming(0, { duration: GRID_FADE_DURATION_MS, easing: Easing.in(Easing.quad) }),
488
- );
489
- }, [gridOpacity]);
490
-
491
- const showZoomChip = useCallback(() => {
492
- zoomChipOpacity.value = withTiming(1, { duration: 100 });
493
- }, [zoomChipOpacity]);
494
-
495
- const hideZoomChip = useCallback(() => {
496
- zoomChipOpacity.value = withDelay(
497
- ZOOM_CHIP_FADE_DELAY_MS,
498
- withTiming(0, { duration: ZOOM_CHIP_FADE_DURATION_MS }),
499
- );
500
- }, [zoomChipOpacity]);
501
-
502
- /** Dev-only ping fired once per gesture start so we can confirm in logs. */
503
- const logGestureStart = useCallback((kind: 'pan' | 'pinch') => {
504
- logger.debug(`gesture start: ${kind}`, { component: LOG_COMPONENT });
505
- }, []);
506
-
507
- const panGesture = useMemo(
508
- () =>
509
- Gesture.Pan()
510
- .minDistance(2)
511
- .onStart(() => {
512
- 'worklet';
513
- savedTranslateX.value = translateX.value;
514
- savedTranslateY.value = translateY.value;
515
- runOnJS(showGrid)();
516
- runOnJS(logGestureStart)('pan');
517
- })
518
- .onUpdate((event) => {
519
- 'worklet';
520
- if (!baseFit) return;
521
- const next = clampTranslation(
522
- savedTranslateX.value + event.translationX,
523
- savedTranslateY.value + event.translationY,
524
- scale.value,
525
- baseFit.width,
526
- baseFit.height,
527
- );
528
- translateX.value = next.tx;
529
- translateY.value = next.ty;
530
- })
531
- .onEnd(() => {
532
- 'worklet';
533
- runOnJS(commitTransform)(scale.value, translateX.value, translateY.value);
534
- runOnJS(hideGrid)();
535
- }),
536
- [
537
- baseFit,
538
- commitTransform,
539
- hideGrid,
540
- logGestureStart,
541
- savedTranslateX,
542
- savedTranslateY,
543
- scale,
544
- showGrid,
545
- translateX,
546
- translateY,
547
- ],
345
+ const theme = useTheme();
346
+ const { t } = useI18n();
347
+ const { oxyServices } = useOxy();
348
+ // Reactive OS reduce-motion preference (useSyncExternalStore, no effect). Pins
349
+ // the entrance spring off. Declared before `entrance` so its initial rest value
350
+ // is correct under reduce-motion.
351
+ const reduceMotion = useReduceMotion();
352
+
353
+ // The "My Oxy files" source hands us a file ID instead of a ready URL. Its
354
+ // download URL is usually private, so it must be resolved through the SDK
355
+ // (which throws on failure). Resolving it HERE — not in the source list — is
356
+ // what keeps the list from lingering on screen for the round-trip: this screen
357
+ // shows its own spinner on the dark canvas while the URL resolves. A React
358
+ // Query read (not an effect) so it stays declarative and cached.
359
+ const shouldResolveFile = !imageUri && !!imageFileId;
360
+ const sourceQuery = useQuery({
361
+ queryKey: ['avatarCropSource', imageFileId],
362
+ enabled: shouldResolveFile,
363
+ staleTime: Number.POSITIVE_INFINITY,
364
+ retry: false,
365
+ queryFn: async () => {
366
+ if (!imageFileId) {
367
+ throw new Error('No file id to resolve for cropping');
368
+ }
369
+ const resolved = await oxyServices.assets.url(imageFileId);
370
+ if (!resolved) {
371
+ throw new Error('No download URL returned for the selected image');
372
+ }
373
+ return resolved;
374
+ },
375
+ });
376
+
377
+ // The URL the cropper actually loads: the passed `imageUri`, else the URL the
378
+ // file-ID query resolved. While it resolves, `effectiveUri` is undefined and
379
+ // the stage shows its own spinner (never the empty state).
380
+ const effectiveUri = imageUri ?? sourceQuery.data;
381
+ const isResolvingSource = shouldResolveFile && !sourceQuery.data && !sourceQuery.isError;
382
+ const sourceErrorMessage = sourceQuery.isError
383
+ ? t('changeAvatar.errors.loadImageFailed') || 'Could not load the selected image'
384
+ : null;
385
+
386
+ // Natural size of the source image. Known up front (device/camera sources pass
387
+ // the dimensions) OR measured lazily from `effectiveUri` via a React Query read
388
+ // (no effect) — the same Image.getSize→useQuery pattern the photo grid uses.
389
+ const hasPassedDimensions = !!(sourceWidth && sourceHeight);
390
+ const measureQuery = useQuery({
391
+ queryKey: ['avatarCropMeasure', effectiveUri],
392
+ enabled: !!effectiveUri && !hasPassedDimensions,
393
+ queryFn: () => {
394
+ if (!effectiveUri) {
395
+ throw new Error('No image to measure');
396
+ }
397
+ return measureImageSize(effectiveUri);
398
+ },
399
+ staleTime: Number.POSITIVE_INFINITY,
400
+ retry: false,
401
+ placeholderData: keepPreviousData,
402
+ });
403
+ const naturalSize = useMemo<ImageNaturalSize | null>(() => {
404
+ if (sourceWidth && sourceHeight) return { width: sourceWidth, height: sourceHeight };
405
+ return measureQuery.data ?? null;
406
+ }, [sourceWidth, sourceHeight, measureQuery.data]);
407
+ /** Measurement failed (bad dimensions / getSize error) — surfaced via the empty UI. */
408
+ const measureErrorMessage = measureQuery.isError
409
+ ? t('editProfile.toasts.cropMeasureFailed') || 'Could not measure the image'
410
+ : null;
411
+
412
+ const [isProcessing, setIsProcessing] = useState(false);
413
+ const [zoomLabel, setZoomLabel] = useState('1.0');
414
+ /** True when scale != MIN_SCALE OR translate != 0 — used to reveal the reset link. */
415
+ const [isModified, setIsModified] = useState(false);
416
+
417
+ // Shared values for the active gesture transform.
418
+ const scale = useSharedValue(MIN_SCALE);
419
+ const translateX = useSharedValue(0);
420
+ const translateY = useSharedValue(0);
421
+
422
+ // Entrance scale of the crop circle (pulse-in on mount).
423
+ const entrance = useSharedValue(reduceMotion ? 1 : 0.95);
424
+ /** 0..1 opacity for the rule-of-thirds grid and zoom chip. */
425
+ const gridOpacity = useSharedValue(0);
426
+ const zoomChipOpacity = useSharedValue(0);
427
+
428
+ // Refs that mirror the latest committed shared values so the JS-side
429
+ // confirm handler can read them without an extra `useSharedValue → react`
430
+ // bridge. Updated by `runOnJS(commit*)` from the gesture worklets.
431
+ const committedScale = useRef(MIN_SCALE);
432
+ const committedTranslateX = useRef(0);
433
+ const committedTranslateY = useRef(0);
434
+
435
+ // Saved start values for relative gesture math.
436
+ const savedScale = useSharedValue(MIN_SCALE);
437
+ const savedTranslateX = useSharedValue(0);
438
+ const savedTranslateY = useSharedValue(0);
439
+
440
+ // Track whether we've already announced a min/max bound during the current
441
+ // pinch so selection haptics don't fire on every frame.
442
+ const hitMinRef = useRef(false);
443
+ const hitMaxRef = useRef(false);
444
+
445
+ // The image is rendered at "cover" relative to the viewport. We compute
446
+ // `baseScale` so 1.0x means the image exactly covers the square; everything
447
+ // larger than that is user-zoom.
448
+ const baseFit = useMemo((): { width: number; height: number } | null => {
449
+ if (!naturalSize) return null;
450
+ const ratio = naturalSize.width / naturalSize.height;
451
+ // Cover: the smaller dimension matches the viewport.
452
+ if (ratio >= 1) {
453
+ // Landscape — height fills viewport
454
+ return { width: VIEWPORT_SIZE * ratio, height: VIEWPORT_SIZE };
455
+ }
456
+ return { width: VIEWPORT_SIZE, height: VIEWPORT_SIZE / ratio };
457
+ }, [naturalSize]);
458
+
459
+ // Entrance pulse, driven from the crop frame's ref callback (fires on mount)
460
+ // rather than an effect — the media-cleanup `startEntrance` pattern. Reduce
461
+ // motion pins the frame at rest; otherwise it springs in from 0.95→1. The
462
+ // callback depends on `reduceMotion`, so if that preference resolves/flips the
463
+ // callback re-runs with the node and re-pins (snap to 1) — matching the old
464
+ // effect that snapped the entrance to rest when reduce-motion turned on.
465
+ const startEntrance = useCallback(
466
+ (node: unknown): void => {
467
+ if (!node) return; // unmount
468
+ if (reduceMotion) {
469
+ entrance.value = 1;
470
+ return;
471
+ }
472
+ entrance.value = withSpring(1, { damping: 14, stiffness: 180, mass: 0.9 });
473
+ },
474
+ [entrance, reduceMotion],
475
+ );
476
+
477
+ const commitTransform = useCallback((s: number, tx: number, ty: number) => {
478
+ committedScale.current = s;
479
+ committedTranslateX.current = tx;
480
+ committedTranslateY.current = ty;
481
+ const modified = Math.abs(s - MIN_SCALE) > 0.001 || Math.abs(tx) > 0.5 || Math.abs(ty) > 0.5;
482
+ setIsModified(modified);
483
+ setZoomLabel(s.toFixed(1));
484
+ }, []);
485
+
486
+ /** Show the rule-of-thirds grid; called from gesture worklets via runOnJS-free path. */
487
+ const showGrid = useCallback(() => {
488
+ gridOpacity.value = withTiming(1, { duration: 120, easing: Easing.out(Easing.quad) });
489
+ }, [gridOpacity]);
490
+
491
+ const hideGrid = useCallback(() => {
492
+ gridOpacity.value = withDelay(
493
+ GRID_FADE_DELAY_MS,
494
+ withTiming(0, { duration: GRID_FADE_DURATION_MS, easing: Easing.in(Easing.quad) }),
548
495
  );
496
+ }, [gridOpacity]);
549
497
 
550
- /** Imperative helpers invoked from worklets via runOnJS. Stable refs. */
551
- const resetPinchBounds = useCallback((): void => {
552
- hitMinRef.current = false;
553
- hitMaxRef.current = false;
554
- }, []);
555
-
556
- const updateZoomLabel = useCallback((s: number): void => {
557
- setZoomLabel(s.toFixed(1));
558
- }, []);
559
-
560
- const notifyMinBoundHit = useCallback((): void => {
561
- if (hitMinRef.current) return;
562
- hitMinRef.current = true;
563
- void hapticSelection();
564
- }, []);
565
-
566
- const notifyMaxBoundHit = useCallback((): void => {
567
- if (hitMaxRef.current) return;
568
- hitMaxRef.current = true;
569
- void hapticSelection();
570
- }, []);
571
-
572
- const pinchGesture = useMemo(
573
- () =>
574
- Gesture.Pinch()
575
- .onStart(() => {
576
- 'worklet';
577
- savedScale.value = scale.value;
578
- runOnJS(showGrid)();
579
- runOnJS(showZoomChip)();
580
- runOnJS(resetPinchBounds)();
581
- runOnJS(logGestureStart)('pinch');
582
- })
583
- .onUpdate((event) => {
584
- 'worklet';
585
- if (!baseFit) return;
586
- const raw = savedScale.value * event.scale;
587
- const nextScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, raw));
588
- scale.value = nextScale;
589
- // Re-clamp translation since the bounds depend on scale.
590
- const clamped = clampTranslation(
591
- translateX.value,
592
- translateY.value,
593
- nextScale,
594
- baseFit.width,
595
- baseFit.height,
596
- );
597
- translateX.value = clamped.tx;
598
- translateY.value = clamped.ty;
599
- runOnJS(updateZoomLabel)(nextScale);
600
- // Selection haptic on first frame at min/max.
601
- if (nextScale <= MIN_SCALE + 0.001 && raw < MIN_SCALE) {
602
- runOnJS(notifyMinBoundHit)();
603
- } else if (nextScale >= MAX_SCALE - 0.001 && raw > MAX_SCALE) {
604
- runOnJS(notifyMaxBoundHit)();
605
- }
606
- })
607
- .onEnd(() => {
608
- 'worklet';
609
- runOnJS(commitTransform)(scale.value, translateX.value, translateY.value);
610
- runOnJS(hideGrid)();
611
- runOnJS(hideZoomChip)();
612
- }),
613
- [
614
- baseFit,
615
- commitTransform,
616
- hideGrid,
617
- hideZoomChip,
618
- logGestureStart,
619
- notifyMaxBoundHit,
620
- notifyMinBoundHit,
621
- resetPinchBounds,
622
- savedScale,
623
- scale,
624
- showGrid,
625
- showZoomChip,
626
- translateX,
627
- translateY,
628
- updateZoomLabel,
629
- ],
630
- );
498
+ const showZoomChip = useCallback(() => {
499
+ zoomChipOpacity.value = withTiming(1, { duration: 100 });
500
+ }, [zoomChipOpacity]);
631
501
 
632
- const composedGesture = useMemo(
633
- () => Gesture.Simultaneous(panGesture, pinchGesture),
634
- [panGesture, pinchGesture],
502
+ const hideZoomChip = useCallback(() => {
503
+ zoomChipOpacity.value = withDelay(
504
+ ZOOM_CHIP_FADE_DELAY_MS,
505
+ withTiming(0, { duration: ZOOM_CHIP_FADE_DURATION_MS }),
635
506
  );
636
-
637
- // Deps list every shared value the mapper reads so it re-runs on web (RN-Web
638
- // has no worklets plugin — a mapper omitting its driver SVs freezes at the
639
- // first frame, which would leave the button-driven zoom stuck). Native
640
- // auto-tracks and ignores the extra deps, so this is safe cross-platform.
641
- const imageAnimatedStyle = useAnimatedStyle(
642
- () => ({
643
- transform: [
644
- { translateX: translateX.value },
645
- { translateY: translateY.value },
646
- { scale: scale.value },
647
- ],
507
+ }, [zoomChipOpacity]);
508
+
509
+ /** Dev-only ping fired once per gesture start so we can confirm in logs. */
510
+ const logGestureStart = useCallback((kind: 'pan' | 'pinch') => {
511
+ logger.debug(`gesture start: ${kind}`, { component: LOG_COMPONENT });
512
+ }, []);
513
+
514
+ const panGesture = useMemo(
515
+ () =>
516
+ Gesture.Pan()
517
+ .minDistance(2)
518
+ .onStart(() => {
519
+ 'worklet';
520
+ savedTranslateX.value = translateX.value;
521
+ savedTranslateY.value = translateY.value;
522
+ runOnJS(showGrid)();
523
+ runOnJS(logGestureStart)('pan');
524
+ })
525
+ .onUpdate((event) => {
526
+ 'worklet';
527
+ if (!baseFit) return;
528
+ const next = clampTranslation(
529
+ savedTranslateX.value + event.translationX,
530
+ savedTranslateY.value + event.translationY,
531
+ scale.value,
532
+ baseFit.width,
533
+ baseFit.height,
534
+ );
535
+ translateX.value = next.tx;
536
+ translateY.value = next.ty;
537
+ })
538
+ .onEnd(() => {
539
+ 'worklet';
540
+ runOnJS(commitTransform)(scale.value, translateX.value, translateY.value);
541
+ runOnJS(hideGrid)();
648
542
  }),
649
- [translateX, translateY, scale],
650
- );
651
-
652
- // `[entrance]` in deps so the ref-callback-driven entrance spring ticks on
653
- // RN-Web (no worklets plugin); native auto-tracks and ignores the extra dep.
654
- const cropFrameAnimatedStyle = useAnimatedStyle(
655
- () => ({
656
- transform: [{ scale: entrance.value }],
543
+ [
544
+ baseFit,
545
+ commitTransform,
546
+ hideGrid,
547
+ logGestureStart,
548
+ savedTranslateX,
549
+ savedTranslateY,
550
+ scale,
551
+ showGrid,
552
+ translateX,
553
+ translateY,
554
+ ],
555
+ );
556
+
557
+ /** Imperative helpers invoked from worklets via runOnJS. Stable refs. */
558
+ const resetPinchBounds = useCallback((): void => {
559
+ hitMinRef.current = false;
560
+ hitMaxRef.current = false;
561
+ }, []);
562
+
563
+ const updateZoomLabel = useCallback((s: number): void => {
564
+ setZoomLabel(s.toFixed(1));
565
+ }, []);
566
+
567
+ const notifyMinBoundHit = useCallback((): void => {
568
+ if (hitMinRef.current) return;
569
+ hitMinRef.current = true;
570
+ void hapticSelection();
571
+ }, []);
572
+
573
+ const notifyMaxBoundHit = useCallback((): void => {
574
+ if (hitMaxRef.current) return;
575
+ hitMaxRef.current = true;
576
+ void hapticSelection();
577
+ }, []);
578
+
579
+ const pinchGesture = useMemo(
580
+ () =>
581
+ Gesture.Pinch()
582
+ .onStart(() => {
583
+ 'worklet';
584
+ savedScale.value = scale.value;
585
+ runOnJS(showGrid)();
586
+ runOnJS(showZoomChip)();
587
+ runOnJS(resetPinchBounds)();
588
+ runOnJS(logGestureStart)('pinch');
589
+ })
590
+ .onUpdate((event) => {
591
+ 'worklet';
592
+ if (!baseFit) return;
593
+ const raw = savedScale.value * event.scale;
594
+ const nextScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, raw));
595
+ scale.value = nextScale;
596
+ // Re-clamp translation since the bounds depend on scale.
597
+ const clamped = clampTranslation(
598
+ translateX.value,
599
+ translateY.value,
600
+ nextScale,
601
+ baseFit.width,
602
+ baseFit.height,
603
+ );
604
+ translateX.value = clamped.tx;
605
+ translateY.value = clamped.ty;
606
+ runOnJS(updateZoomLabel)(nextScale);
607
+ // Selection haptic on first frame at min/max.
608
+ if (nextScale <= MIN_SCALE + 0.001 && raw < MIN_SCALE) {
609
+ runOnJS(notifyMinBoundHit)();
610
+ } else if (nextScale >= MAX_SCALE - 0.001 && raw > MAX_SCALE) {
611
+ runOnJS(notifyMaxBoundHit)();
612
+ }
613
+ })
614
+ .onEnd(() => {
615
+ 'worklet';
616
+ runOnJS(commitTransform)(scale.value, translateX.value, translateY.value);
617
+ runOnJS(hideGrid)();
618
+ runOnJS(hideZoomChip)();
657
619
  }),
658
- [entrance],
620
+ [
621
+ baseFit,
622
+ commitTransform,
623
+ hideGrid,
624
+ hideZoomChip,
625
+ logGestureStart,
626
+ notifyMaxBoundHit,
627
+ notifyMinBoundHit,
628
+ resetPinchBounds,
629
+ savedScale,
630
+ scale,
631
+ showGrid,
632
+ showZoomChip,
633
+ translateX,
634
+ translateY,
635
+ updateZoomLabel,
636
+ ],
637
+ );
638
+
639
+ const composedGesture = useMemo(
640
+ () => Gesture.Simultaneous(panGesture, pinchGesture),
641
+ [panGesture, pinchGesture],
642
+ );
643
+
644
+ // Deps list every shared value the mapper reads so it re-runs on web (RN-Web
645
+ // has no worklets plugin — a mapper omitting its driver SVs freezes at the
646
+ // first frame, which would leave the button-driven zoom stuck). Native
647
+ // auto-tracks and ignores the extra deps, so this is safe cross-platform.
648
+ const imageAnimatedStyle = useAnimatedStyle(
649
+ () => ({
650
+ transform: [
651
+ { translateX: translateX.value },
652
+ { translateY: translateY.value },
653
+ { scale: scale.value },
654
+ ],
655
+ }),
656
+ [translateX, translateY, scale],
657
+ );
658
+
659
+ // `[entrance]` in deps so the ref-callback-driven entrance spring ticks on
660
+ // RN-Web (no worklets plugin); native auto-tracks and ignores the extra dep.
661
+ const cropFrameAnimatedStyle = useAnimatedStyle(
662
+ () => ({
663
+ transform: [{ scale: entrance.value }],
664
+ }),
665
+ [entrance],
666
+ );
667
+
668
+ const gridAnimatedStyle = useAnimatedStyle(() => ({
669
+ opacity: gridOpacity.value,
670
+ }));
671
+
672
+ const zoomChipAnimatedStyle = useAnimatedStyle(() => ({
673
+ opacity: zoomChipOpacity.value,
674
+ }));
675
+
676
+ const resetTransform = useCallback(() => {
677
+ const duration = reduceMotion ? 0 : 220;
678
+ scale.value = withTiming(MIN_SCALE, { duration });
679
+ translateX.value = withTiming(0, { duration });
680
+ translateY.value = withTiming(0, { duration });
681
+ commitTransform(MIN_SCALE, 0, 0);
682
+ void hapticImpact('light');
683
+ AccessibilityInfo.announceForAccessibility(
684
+ t('editProfile.crop.a11yResetAnnouncement') || 'Crop reset',
659
685
  );
686
+ }, [commitTransform, reduceMotion, scale, t, translateX, translateY]);
687
+
688
+ /**
689
+ * Web zoom controls. A desktop pointer has no pinch, so the +/- buttons drive
690
+ * the SAME `scale` shared value the pinch gesture does — clamped to the same
691
+ * bounds, with translation re-clamped through the shared {@link clampTranslation}
692
+ * helper so the image never leaves the viewport. Native keeps pinch (the
693
+ * buttons are hidden there). Reduced motion collapses the tween to an instant
694
+ * set. Reads the committed transform (refs) at press time, never during render.
695
+ */
696
+ const applyZoom = useCallback(
697
+ (direction: 1 | -1) => {
698
+ if (!baseFit || isProcessing) return;
699
+ const current = committedScale.current;
700
+ const target = Math.min(MAX_SCALE, Math.max(MIN_SCALE, current + direction * ZOOM_STEP));
701
+ if (Math.abs(target - current) < 0.001) {
702
+ // Already at the bound — a soft selection tick, no transform change.
703
+ void hapticSelection();
704
+ return;
705
+ }
706
+ const clamped = clampTranslation(
707
+ committedTranslateX.current,
708
+ committedTranslateY.current,
709
+ target,
710
+ baseFit.width,
711
+ baseFit.height,
712
+ );
713
+ const duration = reduceMotion ? 0 : ZOOM_BUTTON_DURATION_MS;
714
+ scale.value = withTiming(target, { duration });
715
+ translateX.value = withTiming(clamped.tx, { duration });
716
+ translateY.value = withTiming(clamped.ty, { duration });
717
+ commitTransform(target, clamped.tx, clamped.ty);
718
+ void hapticImpact('light');
719
+ // Mirror the pinch's transient chrome so the buttons feel identical.
720
+ showGrid();
721
+ hideGrid();
722
+ showZoomChip();
723
+ hideZoomChip();
724
+ },
725
+ [
726
+ baseFit,
727
+ commitTransform,
728
+ hideGrid,
729
+ hideZoomChip,
730
+ isProcessing,
731
+ reduceMotion,
732
+ scale,
733
+ showGrid,
734
+ showZoomChip,
735
+ translateX,
736
+ translateY,
737
+ ],
738
+ );
739
+
740
+ /**
741
+ * Convert the on-screen transform into pixel-space crop coordinates
742
+ * relative to the source image, then invoke expo-image-manipulator to
743
+ * crop + resize to 512x512 JPEG.
744
+ */
745
+ const handleConfirm = useCallback(async () => {
746
+ // Dev-only breadcrumb. Avoid logging `imageUri` so on-device file
747
+ // paths don't leak into breadcrumb sinks that surface debug output.
748
+ logger.debug(
749
+ 'handleConfirm start',
750
+ { component: LOG_COMPONENT },
751
+ {
752
+ hasBaseFit: !!baseFit,
753
+ hasNaturalSize: !!naturalSize,
754
+ committedScale: committedScale.current,
755
+ committedTranslateX: committedTranslateX.current,
756
+ committedTranslateY: committedTranslateY.current,
757
+ },
758
+ );
759
+ if (!effectiveUri || !baseFit || !naturalSize) {
760
+ toast.error(t('editProfile.toasts.cropNotReady') || 'Image not ready yet');
761
+ return;
762
+ }
660
763
 
661
- const gridAnimatedStyle = useAnimatedStyle(() => ({
662
- opacity: gridOpacity.value,
663
- }));
664
-
665
- const zoomChipAnimatedStyle = useAnimatedStyle(() => ({
666
- opacity: zoomChipOpacity.value,
667
- }));
668
-
669
- const resetTransform = useCallback(() => {
670
- const duration = reduceMotion ? 0 : 220;
671
- scale.value = withTiming(MIN_SCALE, { duration });
672
- translateX.value = withTiming(0, { duration });
673
- translateY.value = withTiming(0, { duration });
674
- commitTransform(MIN_SCALE, 0, 0);
675
- void hapticImpact('light');
676
- AccessibilityInfo.announceForAccessibility(
677
- t('editProfile.crop.a11yResetAnnouncement') || 'Crop reset',
678
- );
679
- }, [commitTransform, reduceMotion, scale, t, translateX, translateY]);
680
-
681
- /**
682
- * Web zoom controls. A desktop pointer has no pinch, so the +/- buttons drive
683
- * the SAME `scale` shared value the pinch gesture does — clamped to the same
684
- * bounds, with translation re-clamped through the shared {@link clampTranslation}
685
- * helper so the image never leaves the viewport. Native keeps pinch (the
686
- * buttons are hidden there). Reduced motion collapses the tween to an instant
687
- * set. Reads the committed transform (refs) at press time, never during render.
688
- */
689
- const applyZoom = useCallback(
690
- (direction: 1 | -1) => {
691
- if (!baseFit || isProcessing) return;
692
- const current = committedScale.current;
693
- const target = Math.min(
694
- MAX_SCALE,
695
- Math.max(MIN_SCALE, current + direction * ZOOM_STEP),
696
- );
697
- if (Math.abs(target - current) < 0.001) {
698
- // Already at the bound — a soft selection tick, no transform change.
699
- void hapticSelection();
700
- return;
701
- }
702
- const clamped = clampTranslation(
703
- committedTranslateX.current,
704
- committedTranslateY.current,
705
- target,
706
- baseFit.width,
707
- baseFit.height,
708
- );
709
- const duration = reduceMotion ? 0 : ZOOM_BUTTON_DURATION_MS;
710
- scale.value = withTiming(target, { duration });
711
- translateX.value = withTiming(clamped.tx, { duration });
712
- translateY.value = withTiming(clamped.ty, { duration });
713
- commitTransform(target, clamped.tx, clamped.ty);
714
- void hapticImpact('light');
715
- // Mirror the pinch's transient chrome so the buttons feel identical.
716
- showGrid();
717
- hideGrid();
718
- showZoomChip();
719
- hideZoomChip();
764
+ setIsProcessing(true);
765
+ let preparedImageCleanup: (() => void) | undefined;
766
+ try {
767
+ const { manipulateAsync, SaveFormat } = await loadImageManipulator();
768
+
769
+ const s = committedScale.current;
770
+ const tx = committedTranslateX.current;
771
+ const ty = committedTranslateY.current;
772
+
773
+ // Visible viewport in *displayed image* pixel space (with scale applied):
774
+ // The viewport is centered on the image origin, then offset by (-tx, -ty).
775
+ const scaledImageWidth = baseFit.width * s;
776
+ const scaledImageHeight = baseFit.height * s;
777
+
778
+ // Top-left of viewport in displayed-pixel space:
779
+ const viewportLeft = (scaledImageWidth - VIEWPORT_SIZE) / 2 - tx;
780
+ const viewportTop = (scaledImageHeight - VIEWPORT_SIZE) / 2 - ty;
781
+
782
+ // Convert to source-image pixel space:
783
+ const sourcePerDisplay = naturalSize.width / scaledImageWidth;
784
+ const cropX = Math.max(0, viewportLeft * sourcePerDisplay);
785
+ const cropY = Math.max(0, viewportTop * sourcePerDisplay);
786
+ const cropSize = Math.min(
787
+ naturalSize.width - cropX,
788
+ naturalSize.height - cropY,
789
+ VIEWPORT_SIZE * sourcePerDisplay,
790
+ );
791
+
792
+ if (!Number.isFinite(cropSize) || cropSize <= 0) {
793
+ throw new Error('Computed crop region is invalid');
794
+ }
795
+
796
+ // Dev-only crop coordinates. We log the derived crop region but
797
+ // never the input URI — paths are PII-adjacent on some platforms.
798
+ logger.debug(
799
+ 'manipulateAsync input',
800
+ { component: LOG_COMPONENT },
801
+ {
802
+ cropX,
803
+ cropY,
804
+ cropSize,
805
+ outputSize: OUTPUT_SIZE,
720
806
  },
807
+ );
808
+
809
+ const preparedImage = await prepareImageForManipulator(effectiveUri);
810
+ preparedImageCleanup = preparedImage.cleanup;
811
+ const result = await manipulateAsync(
812
+ preparedImage.uri,
721
813
  [
722
- baseFit,
723
- commitTransform,
724
- hideGrid,
725
- hideZoomChip,
726
- isProcessing,
727
- reduceMotion,
728
- scale,
729
- showGrid,
730
- showZoomChip,
731
- translateX,
732
- translateY,
814
+ {
815
+ crop: {
816
+ originX: cropX,
817
+ originY: cropY,
818
+ width: cropSize,
819
+ height: cropSize,
820
+ },
821
+ },
822
+ { resize: { width: OUTPUT_SIZE, height: OUTPUT_SIZE } },
733
823
  ],
734
- );
735
-
736
- /**
737
- * Convert the on-screen transform into pixel-space crop coordinates
738
- * relative to the source image, then invoke expo-image-manipulator to
739
- * crop + resize to 512x512 JPEG.
740
- */
741
- const handleConfirm = useCallback(async () => {
742
- // Dev-only breadcrumb. Avoid logging `imageUri` so on-device file
743
- // paths don't leak into breadcrumb sinks that surface debug output.
744
- logger.debug(
745
- 'handleConfirm start',
746
- { component: LOG_COMPONENT },
747
- {
748
- hasBaseFit: !!baseFit,
749
- hasNaturalSize: !!naturalSize,
750
- committedScale: committedScale.current,
751
- committedTranslateX: committedTranslateX.current,
752
- committedTranslateY: committedTranslateY.current,
824
+ { format: SaveFormat.JPEG, compress: 0.85 },
825
+ );
826
+
827
+ // Log only the result's dimensions, never the on-disk URI.
828
+ logger.debug(
829
+ 'manipulateAsync result',
830
+ { component: LOG_COMPONENT },
831
+ { width: result.width, height: result.height },
832
+ );
833
+
834
+ void hapticNotification('success');
835
+
836
+ // Resolve the surface's present() promise with the cropped JPEG and
837
+ // close it. The awaiting caller (`useAvatarPicker`) handles the
838
+ // upload + any success toast.
839
+ dismiss?.({
840
+ uri: result.uri,
841
+ width: result.width,
842
+ height: result.height,
843
+ mime: 'image/jpeg',
844
+ });
845
+ } catch (err) {
846
+ const normalizedError = normalizeCropError(err);
847
+ logger.error('handleConfirm failed', normalizedError, { component: LOG_COMPONENT });
848
+ void hapticNotification('error');
849
+ toast.error(
850
+ normalizedError.message || t('editProfile.toasts.cropFailed') || 'Failed to crop image',
851
+ );
852
+ } finally {
853
+ preparedImageCleanup?.();
854
+ setIsProcessing(false);
855
+ }
856
+ }, [baseFit, effectiveUri, naturalSize, dismiss, t]);
857
+
858
+ /**
859
+ * The surface's "Use photo" CTA, declared through the Dialog header's
860
+ * `primaryAction` (a real Bloom Button with a loading spinner + disabled
861
+ * state) — the standardized trailing action, no bespoke node. Memoized so the
862
+ * header does not thrash.
863
+ */
864
+ const donePrimaryAction = useMemo<SurfaceHeaderContent['primaryAction']>(
865
+ () => ({
866
+ label: t('editProfile.crop.confirm'),
867
+ onPress: () => void handleConfirm(),
868
+ disabled: isProcessing || !baseFit,
869
+ loading: isProcessing,
870
+ }),
871
+ [t, handleConfirm, isProcessing, baseFit],
872
+ );
873
+
874
+ // No bar of our own: the surface owns the chrome. `goBack` pops back to the
875
+ // ChangeAvatar source list (and dismisses the surface if this is somehow the
876
+ // root frame), which is exactly Cancel.
877
+ useSurfaceHeader({
878
+ title: t('editProfile.crop.title'),
879
+ largeTitle: false,
880
+ primaryAction: donePrimaryAction,
881
+ onBack: goBack,
882
+ });
883
+
884
+ const styles = useMemo(
885
+ () =>
886
+ StyleSheet.create({
887
+ container: {
888
+ backgroundColor: theme.colors.background,
889
+ },
890
+ stage: {
891
+ backgroundColor: CANVAS_BG,
892
+ borderRadius: STAGE_RADIUS,
893
+ overflow: 'hidden',
894
+ // Top padding clears the zoom chip, which floats 44dp above
895
+ // the crop frame and would otherwise be clipped by `overflow`.
896
+ paddingTop: 60,
897
+ paddingBottom: 28,
898
+ paddingHorizontal: 16,
899
+ alignItems: 'center',
900
+ justifyContent: 'center',
901
+ },
902
+ cropFrame: {
903
+ width: VIEWPORT_SIZE,
904
+ height: VIEWPORT_SIZE,
905
+ alignItems: 'center',
906
+ justifyContent: 'center',
907
+ },
908
+ viewport: {
909
+ width: VIEWPORT_SIZE,
910
+ height: VIEWPORT_SIZE,
911
+ overflow: 'hidden',
912
+ borderRadius: VIEWPORT_SIZE / 2,
913
+ backgroundColor: '#1a1a1a',
914
+ alignItems: 'center',
915
+ justifyContent: 'center',
916
+ },
917
+ image: {
918
+ width: baseFit?.width ?? VIEWPORT_SIZE,
919
+ height: baseFit?.height ?? VIEWPORT_SIZE,
920
+ },
921
+ // Outer mask: large square that overlays the canvas, with a
922
+ // round transparent cutout in the middle. We achieve this with
923
+ // four edge boxes around the circle (top/bottom/left/right) so
924
+ // there's no need for SVG. Each box is 50% black.
925
+ ringOverlay: {
926
+ position: 'absolute',
927
+ width: VIEWPORT_SIZE,
928
+ height: VIEWPORT_SIZE,
929
+ borderRadius: VIEWPORT_SIZE / 2,
930
+ borderWidth: RING_WIDTH,
931
+ borderColor: RING_COLOR,
932
+ // Subtle inner shadow approximated with a thin secondary border.
933
+ ...Platform.select({
934
+ web: {
935
+ boxShadow: 'inset 0 0 14px rgba(0,0,0,0.45)',
753
936
  },
754
- );
755
- if (!effectiveUri || !baseFit || !naturalSize) {
756
- toast.error(
757
- t('editProfile.toasts.cropNotReady') || 'Image not ready yet',
758
- );
759
- return;
760
- }
761
-
762
- setIsProcessing(true);
763
- let preparedImageCleanup: (() => void) | undefined;
764
- try {
765
- const { manipulateAsync, SaveFormat } = await loadImageManipulator();
766
-
767
- const s = committedScale.current;
768
- const tx = committedTranslateX.current;
769
- const ty = committedTranslateY.current;
770
-
771
- // Visible viewport in *displayed image* pixel space (with scale applied):
772
- // The viewport is centered on the image origin, then offset by (-tx, -ty).
773
- const scaledImageWidth = baseFit.width * s;
774
- const scaledImageHeight = baseFit.height * s;
775
-
776
- // Top-left of viewport in displayed-pixel space:
777
- const viewportLeft = (scaledImageWidth - VIEWPORT_SIZE) / 2 - tx;
778
- const viewportTop = (scaledImageHeight - VIEWPORT_SIZE) / 2 - ty;
779
-
780
- // Convert to source-image pixel space:
781
- const sourcePerDisplay = naturalSize.width / scaledImageWidth;
782
- const cropX = Math.max(0, viewportLeft * sourcePerDisplay);
783
- const cropY = Math.max(0, viewportTop * sourcePerDisplay);
784
- const cropSize = Math.min(
785
- naturalSize.width - cropX,
786
- naturalSize.height - cropY,
787
- VIEWPORT_SIZE * sourcePerDisplay,
788
- );
789
-
790
- if (!Number.isFinite(cropSize) || cropSize <= 0) {
791
- throw new Error('Computed crop region is invalid');
792
- }
793
-
794
- // Dev-only crop coordinates. We log the derived crop region but
795
- // never the input URI — paths are PII-adjacent on some platforms.
796
- logger.debug(
797
- 'manipulateAsync input',
798
- { component: LOG_COMPONENT },
799
- {
800
- cropX,
801
- cropY,
802
- cropSize,
803
- outputSize: OUTPUT_SIZE,
804
- },
805
- );
806
-
807
- const preparedImage = await prepareImageForManipulator(effectiveUri);
808
- preparedImageCleanup = preparedImage.cleanup;
809
- const result = await manipulateAsync(
810
- preparedImage.uri,
811
- [
812
- {
813
- crop: {
814
- originX: cropX,
815
- originY: cropY,
816
- width: cropSize,
817
- height: cropSize,
818
- },
819
- },
820
- { resize: { width: OUTPUT_SIZE, height: OUTPUT_SIZE } },
821
- ],
822
- { format: SaveFormat.JPEG, compress: 0.85 },
823
- );
824
-
825
- // Log only the result's dimensions, never the on-disk URI.
826
- logger.debug(
827
- 'manipulateAsync result',
828
- { component: LOG_COMPONENT },
829
- { width: result.width, height: result.height },
830
- );
831
-
832
- void hapticNotification('success');
833
-
834
- // Resolve the surface's present() promise with the cropped JPEG and
835
- // close it. The awaiting caller (`useAvatarPicker`) handles the
836
- // upload + any success toast.
837
- dismiss?.({
838
- uri: result.uri,
839
- width: result.width,
840
- height: result.height,
841
- mime: 'image/jpeg',
842
- });
843
- } catch (err) {
844
- const normalizedError = normalizeCropError(err);
845
- logger.error('handleConfirm failed', normalizedError, { component: LOG_COMPONENT });
846
- void hapticNotification('error');
847
- toast.error(
848
- normalizedError.message || t('editProfile.toasts.cropFailed') || 'Failed to crop image',
849
- );
850
- } finally {
851
- preparedImageCleanup?.();
852
- setIsProcessing(false);
853
- }
854
- }, [baseFit, effectiveUri, naturalSize, dismiss, t]);
855
-
856
- /**
857
- * The surface's "Use photo" CTA, declared through the Dialog header's
858
- * `primaryAction` (a real Bloom Button with a loading spinner + disabled
859
- * state) — the standardized trailing action, no bespoke node. Memoized so the
860
- * header does not thrash.
861
- */
862
- const donePrimaryAction = useMemo<SurfaceHeaderContent['primaryAction']>(
863
- () => ({
864
- label: t('editProfile.crop.confirm'),
865
- onPress: () => void handleConfirm(),
866
- disabled: isProcessing || !baseFit,
867
- loading: isProcessing,
868
- }),
869
- [t, handleConfirm, isProcessing, baseFit],
870
- );
871
-
872
- // No bar of our own: the surface owns the chrome. `goBack` pops back to the
873
- // ChangeAvatar source list (and dismisses the surface if this is somehow the
874
- // root frame), which is exactly Cancel.
875
- useSurfaceHeader({
876
- title: t('editProfile.crop.title'),
877
- largeTitle: false,
878
- primaryAction: donePrimaryAction,
879
- onBack: goBack,
880
- });
881
-
882
- const styles = useMemo(
883
- () =>
884
- StyleSheet.create({
885
- container: {
886
- backgroundColor: theme.colors.background,
887
- },
888
- stage: {
889
- backgroundColor: CANVAS_BG,
890
- borderRadius: STAGE_RADIUS,
891
- overflow: 'hidden',
892
- // Top padding clears the zoom chip, which floats 44dp above
893
- // the crop frame and would otherwise be clipped by `overflow`.
894
- paddingTop: 60,
895
- paddingBottom: 28,
896
- paddingHorizontal: 16,
897
- alignItems: 'center',
898
- justifyContent: 'center',
899
- },
900
- cropFrame: {
901
- width: VIEWPORT_SIZE,
902
- height: VIEWPORT_SIZE,
903
- alignItems: 'center',
904
- justifyContent: 'center',
905
- },
906
- viewport: {
907
- width: VIEWPORT_SIZE,
908
- height: VIEWPORT_SIZE,
909
- overflow: 'hidden',
910
- borderRadius: VIEWPORT_SIZE / 2,
911
- backgroundColor: '#1a1a1a',
912
- alignItems: 'center',
913
- justifyContent: 'center',
914
- },
915
- image: {
916
- width: baseFit?.width ?? VIEWPORT_SIZE,
917
- height: baseFit?.height ?? VIEWPORT_SIZE,
918
- },
919
- // Outer mask: large square that overlays the canvas, with a
920
- // round transparent cutout in the middle. We achieve this with
921
- // four edge boxes around the circle (top/bottom/left/right) so
922
- // there's no need for SVG. Each box is 50% black.
923
- ringOverlay: {
924
- position: 'absolute',
925
- width: VIEWPORT_SIZE,
926
- height: VIEWPORT_SIZE,
927
- borderRadius: VIEWPORT_SIZE / 2,
928
- borderWidth: RING_WIDTH,
929
- borderColor: RING_COLOR,
930
- // Subtle inner shadow approximated with a thin secondary border.
931
- ...Platform.select({
932
- web: {
933
- boxShadow: 'inset 0 0 14px rgba(0,0,0,0.45)',
934
- },
935
- default: {},
936
- }),
937
- },
938
- gridOverlay: {
939
- position: 'absolute',
940
- width: VIEWPORT_SIZE,
941
- height: VIEWPORT_SIZE,
942
- borderRadius: VIEWPORT_SIZE / 2,
943
- overflow: 'hidden',
944
- pointerEvents: 'none',
945
- },
946
- gridLineH: {
947
- position: 'absolute',
948
- left: 0,
949
- right: 0,
950
- height: StyleSheet.hairlineWidth,
951
- backgroundColor: 'rgba(255,255,255,0.45)',
952
- },
953
- gridLineV: {
954
- position: 'absolute',
955
- top: 0,
956
- bottom: 0,
957
- width: StyleSheet.hairlineWidth,
958
- backgroundColor: 'rgba(255,255,255,0.45)',
959
- },
960
- zoomChip: {
961
- position: 'absolute',
962
- top: -44,
963
- alignSelf: 'center',
964
- paddingHorizontal: 12,
965
- paddingVertical: 6,
966
- borderRadius: 999,
967
- backgroundColor: 'rgba(0,0,0,0.7)',
968
- minWidth: 56,
969
- alignItems: 'center',
970
- justifyContent: 'center',
971
- },
972
- zoomChipText: {
973
- color: '#ffffff',
974
- fontFamily: Platform.select({
975
- ios: 'Menlo',
976
- android: 'monospace',
977
- default: 'ui-monospace, SFMono-Regular, Menlo, monospace',
978
- }),
979
- fontSize: 12,
980
- letterSpacing: 0.2,
981
- },
982
- helperBlock: {
983
- paddingTop: 16,
984
- alignItems: 'center',
985
- gap: 8,
986
- },
987
- helper: {
988
- fontSize: 13,
989
- lineHeight: 18,
990
- color: theme.colors.textSecondary,
991
- textAlign: 'center',
992
- maxWidth: 320,
993
- },
994
- resetLink: {
995
- paddingHorizontal: 12,
996
- paddingVertical: 6,
997
- },
998
- resetLinkText: {
999
- fontSize: 13,
1000
- color: theme.colors.primary,
1001
- textDecorationLine: 'underline',
1002
- },
1003
- emptyState: {
1004
- alignItems: 'center',
1005
- justifyContent: 'center',
1006
- paddingVertical: 48,
1007
- },
1008
- emptyLabel: {
1009
- fontSize: 14,
1010
- color: theme.colors.textSecondary,
1011
- textAlign: 'center',
1012
- },
1013
- }),
1014
- [baseFit, theme],
937
+ default: {},
938
+ }),
939
+ },
940
+ gridOverlay: {
941
+ position: 'absolute',
942
+ width: VIEWPORT_SIZE,
943
+ height: VIEWPORT_SIZE,
944
+ borderRadius: VIEWPORT_SIZE / 2,
945
+ overflow: 'hidden',
946
+ pointerEvents: 'none',
947
+ },
948
+ gridLineH: {
949
+ position: 'absolute',
950
+ left: 0,
951
+ right: 0,
952
+ height: StyleSheet.hairlineWidth,
953
+ backgroundColor: 'rgba(255,255,255,0.45)',
954
+ },
955
+ gridLineV: {
956
+ position: 'absolute',
957
+ top: 0,
958
+ bottom: 0,
959
+ width: StyleSheet.hairlineWidth,
960
+ backgroundColor: 'rgba(255,255,255,0.45)',
961
+ },
962
+ zoomChip: {
963
+ position: 'absolute',
964
+ top: -44,
965
+ alignSelf: 'center',
966
+ paddingHorizontal: 12,
967
+ paddingVertical: 6,
968
+ borderRadius: 999,
969
+ backgroundColor: 'rgba(0,0,0,0.7)',
970
+ minWidth: 56,
971
+ alignItems: 'center',
972
+ justifyContent: 'center',
973
+ },
974
+ zoomChipText: {
975
+ color: '#ffffff',
976
+ fontFamily: Platform.select({
977
+ ios: 'Menlo',
978
+ android: 'monospace',
979
+ default: 'ui-monospace, SFMono-Regular, Menlo, monospace',
980
+ }),
981
+ fontSize: 12,
982
+ letterSpacing: 0.2,
983
+ },
984
+ helperBlock: {
985
+ paddingTop: 16,
986
+ alignItems: 'center',
987
+ gap: 8,
988
+ },
989
+ helper: {
990
+ fontSize: 13,
991
+ lineHeight: 18,
992
+ color: theme.colors.textSecondary,
993
+ textAlign: 'center',
994
+ maxWidth: 320,
995
+ },
996
+ resetLink: {
997
+ paddingHorizontal: 12,
998
+ paddingVertical: 6,
999
+ },
1000
+ resetLinkText: {
1001
+ fontSize: 13,
1002
+ color: theme.colors.primary,
1003
+ textDecorationLine: 'underline',
1004
+ },
1005
+ emptyState: {
1006
+ alignItems: 'center',
1007
+ justifyContent: 'center',
1008
+ paddingVertical: 48,
1009
+ },
1010
+ emptyLabel: {
1011
+ fontSize: 14,
1012
+ color: theme.colors.textSecondary,
1013
+ textAlign: 'center',
1014
+ },
1015
+ }),
1016
+ [baseFit, theme],
1017
+ );
1018
+
1019
+ // Reset link reveal — only show when the image is not at the default
1020
+ // transform. Use derived display state to avoid mounting/unmounting jank.
1021
+ const resetLinkOpacity = isModified ? 1 : 0;
1022
+
1023
+ // Zoom-button state is derived from `zoomLabel` (reactive state, set on every
1024
+ // commit) rather than the committed-scale ref, so it stays correct across
1025
+ // renders without reading mutable state during render. The +/- pair is web-only
1026
+ // (native has pinch).
1027
+ const currentZoom = Number.parseFloat(zoomLabel) || MIN_SCALE;
1028
+ const canZoomOut = currentZoom > MIN_SCALE + 0.001;
1029
+ const canZoomIn = currentZoom < MAX_SCALE - 0.001;
1030
+ const showZoomControls = Platform.OS === 'web';
1031
+ const zoomOutDisabled = !canZoomOut || !baseFit || isProcessing;
1032
+ const zoomInDisabled = !canZoomIn || !baseFit || isProcessing;
1033
+
1034
+ // No source at all (and none resolving), a resolution failure, or a
1035
+ // measurement failure — render a minimal empty state. While a file ID is still
1036
+ // resolving, fall THROUGH to the stage so its own spinner shows, not this.
1037
+ // The surface's nav bar (back = cancel) is the only chrome needed here.
1038
+ if ((!effectiveUri && !isResolvingSource) || sourceErrorMessage || measureErrorMessage) {
1039
+ const emptyMessage = sourceErrorMessage
1040
+ ? sourceErrorMessage
1041
+ : measureErrorMessage
1042
+ ? measureErrorMessage
1043
+ : t('editProfile.crop.noImage');
1044
+ return (
1045
+ <View style={styles.container} className="px-screen-margin pb-space-24">
1046
+ <View style={styles.emptyState}>
1047
+ <Text style={styles.emptyLabel}>{emptyMessage}</Text>
1048
+ </View>
1049
+ </View>
1015
1050
  );
1016
-
1017
- // Reset link reveal — only show when the image is not at the default
1018
- // transform. Use derived display state to avoid mounting/unmounting jank.
1019
- const resetLinkOpacity = isModified ? 1 : 0;
1020
-
1021
- // Zoom-button state is derived from `zoomLabel` (reactive state, set on every
1022
- // commit) rather than the committed-scale ref, so it stays correct across
1023
- // renders without reading mutable state during render. The +/- pair is web-only
1024
- // (native has pinch).
1025
- const currentZoom = Number.parseFloat(zoomLabel) || MIN_SCALE;
1026
- const canZoomOut = currentZoom > MIN_SCALE + 0.001;
1027
- const canZoomIn = currentZoom < MAX_SCALE - 0.001;
1028
- const showZoomControls = Platform.OS === 'web';
1029
- const zoomOutDisabled = !canZoomOut || !baseFit || isProcessing;
1030
- const zoomInDisabled = !canZoomIn || !baseFit || isProcessing;
1031
-
1032
- // No source at all (and none resolving), a resolution failure, or a
1033
- // measurement failure — render a minimal empty state. While a file ID is still
1034
- // resolving, fall THROUGH to the stage so its own spinner shows, not this.
1035
- // The surface's nav bar (back = cancel) is the only chrome needed here.
1036
- if ((!effectiveUri && !isResolvingSource) || sourceErrorMessage || measureErrorMessage) {
1037
- const emptyMessage = sourceErrorMessage
1038
- ? sourceErrorMessage
1039
- : measureErrorMessage
1040
- ? measureErrorMessage
1041
- : t('editProfile.crop.noImage');
1042
- return (
1043
- <View style={styles.container} className="px-screen-margin pb-space-24">
1044
- <View style={styles.emptyState}>
1045
- <Text style={styles.emptyLabel}>{emptyMessage}</Text>
1046
- </View>
1051
+ }
1052
+
1053
+ return (
1054
+ <View style={styles.container} className="px-screen-margin pb-space-24">
1055
+ <View style={styles.stage}>
1056
+ <Animated.View
1057
+ ref={startEntrance}
1058
+ style={[styles.cropFrame, cropFrameAnimatedStyle]}
1059
+ accessible
1060
+ accessibilityRole="image"
1061
+ accessibilityLabel={t('editProfile.crop.a11yImage')}
1062
+ >
1063
+ <GestureDetector gesture={composedGesture}>
1064
+ <View style={styles.viewport}>
1065
+ {baseFit && effectiveUri ? (
1066
+ <Animated.Image
1067
+ source={{ uri: effectiveUri }}
1068
+ style={[styles.image, imageAnimatedStyle]}
1069
+ resizeMode="cover"
1070
+ />
1071
+ ) : (
1072
+ <ActivityIndicator color="#ffffff" />
1073
+ )}
1047
1074
  </View>
1048
- );
1049
- }
1050
-
1051
- return (
1052
- <View style={styles.container} className="px-screen-margin pb-space-24">
1053
- <View style={styles.stage}>
1054
- <Animated.View
1055
- ref={startEntrance}
1056
- style={[styles.cropFrame, cropFrameAnimatedStyle]}
1057
- accessible
1058
- accessibilityRole="image"
1059
- accessibilityLabel={t('editProfile.crop.a11yImage')}
1060
- >
1061
- <GestureDetector gesture={composedGesture}>
1062
- <View style={styles.viewport}>
1063
- {baseFit && effectiveUri ? (
1064
- <Animated.Image
1065
- source={{ uri: effectiveUri }}
1066
- style={[styles.image, imageAnimatedStyle]}
1067
- resizeMode="cover"
1068
- />
1069
- ) : (
1070
- <ActivityIndicator color="#ffffff" />
1071
- )}
1072
- </View>
1073
- </GestureDetector>
1074
-
1075
- {/* White ring + inner shadow around the circle. */}
1076
- <View pointerEvents="none" style={styles.ringOverlay} />
1077
-
1078
- {/* Rule-of-thirds grid overlay (fades in during gesture). */}
1079
- <Animated.View
1080
- pointerEvents="none"
1081
- style={[styles.gridOverlay, gridAnimatedStyle]}
1082
- >
1083
- <View style={[styles.gridLineH, { top: VIEWPORT_SIZE / 3 }]} />
1084
- <View style={[styles.gridLineH, { top: (VIEWPORT_SIZE * 2) / 3 }]} />
1085
- <View style={[styles.gridLineV, { left: VIEWPORT_SIZE / 3 }]} />
1086
- <View style={[styles.gridLineV, { left: (VIEWPORT_SIZE * 2) / 3 }]} />
1087
- </Animated.View>
1088
-
1089
- {/* Floating zoom chip during pinch. */}
1090
- <Animated.View
1091
- pointerEvents="none"
1092
- style={[styles.zoomChip, zoomChipAnimatedStyle]}
1093
- >
1094
- <Text style={styles.zoomChipText}>
1095
- {t('editProfile.crop.zoom', { value: zoomLabel })}
1096
- </Text>
1097
- </Animated.View>
1098
- </Animated.View>
1099
-
1100
- {/* Web-only +/- zoom controls (a desktop pointer has no pinch).
1075
+ </GestureDetector>
1076
+
1077
+ {/* White ring + inner shadow around the circle. */}
1078
+ <View pointerEvents="none" style={styles.ringOverlay} />
1079
+
1080
+ {/* Rule-of-thirds grid overlay (fades in during gesture). */}
1081
+ <Animated.View pointerEvents="none" style={[styles.gridOverlay, gridAnimatedStyle]}>
1082
+ <View style={[styles.gridLineH, { top: VIEWPORT_SIZE / 3 }]} />
1083
+ <View style={[styles.gridLineH, { top: (VIEWPORT_SIZE * 2) / 3 }]} />
1084
+ <View style={[styles.gridLineV, { left: VIEWPORT_SIZE / 3 }]} />
1085
+ <View style={[styles.gridLineV, { left: (VIEWPORT_SIZE * 2) / 3 }]} />
1086
+ </Animated.View>
1087
+
1088
+ {/* Floating zoom chip during pinch. */}
1089
+ <Animated.View pointerEvents="none" style={[styles.zoomChip, zoomChipAnimatedStyle]}>
1090
+ <Text style={styles.zoomChipText}>
1091
+ {t('editProfile.crop.zoom', { value: zoomLabel })}
1092
+ </Text>
1093
+ </Animated.View>
1094
+ </Animated.View>
1095
+
1096
+ {/* Web-only +/- zoom controls (a desktop pointer has no pinch).
1101
1097
  Styled with NativeWind to match the translucent zoom-chip
1102
1098
  aesthetic; the full-width row is `box-none` so only the pill
1103
1099
  takes pointer events and the rest of the circle stays pannable. */}
1104
- {showZoomControls ? (
1105
- <View
1106
- className="absolute bottom-4 left-0 right-0 items-center"
1107
- pointerEvents="box-none"
1108
- >
1109
- <View className="flex-row overflow-hidden rounded-full bg-black/70">
1110
- <Pressable
1111
- accessibilityRole="button"
1112
- accessibilityLabel={t('editProfile.crop.zoomOut')}
1113
- accessibilityState={{ disabled: zoomOutDisabled }}
1114
- disabled={zoomOutDisabled}
1115
- onPress={() => applyZoom(-1)}
1116
- className={`h-10 w-[52px] items-center justify-center active:bg-white/10${
1117
- zoomOutDisabled ? ' opacity-40' : ''
1118
- }`}
1119
- >
1120
- <Text className="text-white text-[22px] font-medium leading-[24px]">
1121
- {'−'}
1122
- </Text>
1123
- </Pressable>
1124
- <View className="w-px self-stretch bg-white/25" />
1125
- <Pressable
1126
- accessibilityRole="button"
1127
- accessibilityLabel={t('editProfile.crop.zoomIn')}
1128
- accessibilityState={{ disabled: zoomInDisabled }}
1129
- disabled={zoomInDisabled}
1130
- onPress={() => applyZoom(1)}
1131
- className={`h-10 w-[52px] items-center justify-center active:bg-white/10${
1132
- zoomInDisabled ? ' opacity-40' : ''
1133
- }`}
1134
- >
1135
- <Text className="text-white text-[22px] font-medium leading-[24px]">
1136
- {'+'}
1137
- </Text>
1138
- </Pressable>
1139
- </View>
1140
- </View>
1141
- ) : null}
1142
- </View>
1143
-
1144
- <View style={styles.helperBlock}>
1145
- <Text style={styles.helper}>{t('editProfile.crop.helper')}</Text>
1146
- <Pressable
1147
- accessibilityLabel={t('editProfile.crop.a11yReset')}
1148
- accessibilityRole="button"
1149
- accessibilityState={{ disabled: !isModified || isProcessing }}
1150
- onPress={resetTransform}
1151
- disabled={!isModified || isProcessing}
1152
- style={[styles.resetLink, { opacity: isProcessing ? 0.3 : resetLinkOpacity }]}
1153
- >
1154
- <Text style={styles.resetLinkText}>{t('editProfile.crop.resetToCenter')}</Text>
1155
- </Pressable>
1100
+ {showZoomControls ? (
1101
+ <View className="absolute bottom-4 left-0 right-0 items-center" pointerEvents="box-none">
1102
+ <View className="flex-row overflow-hidden rounded-full bg-black/70">
1103
+ <Pressable
1104
+ accessibilityRole="button"
1105
+ accessibilityLabel={t('editProfile.crop.zoomOut')}
1106
+ accessibilityState={{ disabled: zoomOutDisabled }}
1107
+ disabled={zoomOutDisabled}
1108
+ onPress={() => applyZoom(-1)}
1109
+ className={`h-10 w-[52px] items-center justify-center active:bg-white/10${
1110
+ zoomOutDisabled ? ' opacity-40' : ''
1111
+ }`}
1112
+ >
1113
+ <Text className="text-white text-[22px] font-medium leading-[24px]">{'−'}</Text>
1114
+ </Pressable>
1115
+ <View className="w-px self-stretch bg-white/25" />
1116
+ <Pressable
1117
+ accessibilityRole="button"
1118
+ accessibilityLabel={t('editProfile.crop.zoomIn')}
1119
+ accessibilityState={{ disabled: zoomInDisabled }}
1120
+ disabled={zoomInDisabled}
1121
+ onPress={() => applyZoom(1)}
1122
+ className={`h-10 w-[52px] items-center justify-center active:bg-white/10${
1123
+ zoomInDisabled ? ' opacity-40' : ''
1124
+ }`}
1125
+ >
1126
+ <Text className="text-white text-[22px] font-medium leading-[24px]">{'+'}</Text>
1127
+ </Pressable>
1156
1128
  </View>
1157
- </View>
1158
- );
1129
+ </View>
1130
+ ) : null}
1131
+ </View>
1132
+
1133
+ <View style={styles.helperBlock}>
1134
+ <Text style={styles.helper}>{t('editProfile.crop.helper')}</Text>
1135
+ <Pressable
1136
+ accessibilityLabel={t('editProfile.crop.a11yReset')}
1137
+ accessibilityRole="button"
1138
+ accessibilityState={{ disabled: !isModified || isProcessing }}
1139
+ onPress={resetTransform}
1140
+ disabled={!isModified || isProcessing}
1141
+ style={[styles.resetLink, { opacity: isProcessing ? 0.3 : resetLinkOpacity }]}
1142
+ >
1143
+ <Text style={styles.resetLinkText}>{t('editProfile.crop.resetToCenter')}</Text>
1144
+ </Pressable>
1145
+ </View>
1146
+ </View>
1147
+ );
1159
1148
  };
1160
1149
 
1161
1150
  export default AvatarCropScreen;