@oxy.so/services 11.2.0-oxy-one.2 → 11.2.0-oxy-one.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/assets/assets/icons/OxyServices.tsx +42 -40
- package/lib/commonjs/assets/assets/illustrations/HighFive.tsx +58 -31
- package/lib/commonjs/assets/icons/OxyServices.js.map +1 -1
- package/lib/commonjs/assets/illustrations/HighFive.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/notifications/deviceNotifications.js +8 -4
- package/lib/commonjs/notifications/deviceNotifications.js.map +1 -1
- package/lib/commonjs/ui/analytics/productAnalytics.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/BenefitList.js.map +1 -1
- package/lib/commonjs/ui/components/BloomLocaleBridge.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/FollowTargetButton.js +1 -1
- package/lib/commonjs/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/commonjs/ui/components/LanguageBridge.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthPrompt.js.map +1 -1
- package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInRequestSurface.js.map +1 -1
- package/lib/commonjs/ui/components/PeableButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileSummaryCard.js +1 -1
- package/lib/commonjs/ui/components/ProfileSummaryCard.js.map +1 -1
- package/lib/commonjs/ui/components/RequireOxyAuth.js.map +1 -1
- package/lib/commonjs/ui/components/SettingsIcon.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceHeaderAction.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/components/feedback/constants.js.map +1 -1
- package/lib/commonjs/ui/components/feedback/useFeedbackForm.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js +1 -1
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/components/followButtonAccessibility.js.map +1 -1
- package/lib/commonjs/ui/components/followRules.js.map +1 -1
- package/lib/commonjs/ui/components/icon/FAIRWalletIcon.js.map +1 -1
- package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -1
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/commonjs/ui/components/oauthNavigation.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentMethodStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/constants.js.map +1 -1
- package/lib/commonjs/ui/components/payment/paymentStyles.js.map +1 -1
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/InlineCommonsQr.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/artwork.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/signInFlowStore.js.map +1 -1
- package/lib/commonjs/ui/components/styles/overlay.js.map +1 -1
- package/lib/commonjs/ui/components/styles/shadow.js.map +1 -1
- package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
- package/lib/commonjs/ui/components/utils/hasTouchHandler.js.map +1 -1
- package/lib/commonjs/ui/components/utils/roundLayoutSize.js.map +1 -1
- package/lib/commonjs/ui/components/utils/splitStyles.js.map +1 -1
- package/lib/commonjs/ui/constants/spacing.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/isolatedOAuthSession.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountQueryPersistence.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/mutationFactory.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/persistedQueryCache.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useFileQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/usePaymentQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/usePersonalPlans.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useSecurityQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/userCache.js +38 -38
- package/lib/commonjs/ui/hooks/queries/userCache.js.map +1 -1
- package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFileFiltering.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
- package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/hooks/useReduceMotion.js.map +1 -1
- package/lib/commonjs/ui/hooks/useResolvedFileUrls.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/commonjs/ui/hooks/useStorage.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSurfaceFrameWidth.js.map +1 -1
- package/lib/commonjs/ui/icons/Ionicons.js.map +1 -1
- package/lib/commonjs/ui/icons/MaterialCommunityIcons.js.map +1 -1
- package/lib/commonjs/ui/icons/decorativeIconSet.js.map +1 -1
- package/lib/commonjs/ui/isFrontend.js.map +1 -1
- package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
- package/lib/commonjs/ui/oauth/applicationSessionLane.js.map +1 -1
- package/lib/commonjs/ui/oauth/browserAuthTransport.js.map +1 -1
- package/lib/commonjs/ui/oauth/browserBridge.js.map +1 -1
- package/lib/commonjs/ui/oauth/completeOAuthCode.js.map +1 -1
- package/lib/commonjs/ui/oauth/nativeAuthTransport.js.map +1 -1
- package/lib/commonjs/ui/oauth/oauthHandshake.js.map +1 -1
- package/lib/commonjs/ui/oauth/oauthPopup.js.map +1 -1
- package/lib/commonjs/ui/oauth/oauthPopupMessages.js.map +1 -1
- package/lib/commonjs/ui/runtime/createOxyRuntime.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSecurityPanelScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/DomainsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +2 -5
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/NotificationsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +3 -3
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/UserListScreen.js +1 -1
- package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/FileLibraryError.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/FileListSection.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +7 -7
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/UploadBar.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/photoGridLayout.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/shared.js.map +1 -1
- package/lib/commonjs/ui/screens/linkFormat.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustFAQScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/commonjs/ui/session/authStore.js.map +1 -1
- package/lib/commonjs/ui/session/backgroundSession.js.map +1 -1
- package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
- package/lib/commonjs/ui/session/deviceCredentialTracker.js.map +1 -1
- package/lib/commonjs/ui/session/identityBinding.js.map +1 -1
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js.map +1 -1
- package/lib/commonjs/ui/session/tokenLossRecovery.js.map +1 -1
- package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
- package/lib/commonjs/ui/stores/assetStore.js.map +1 -1
- package/lib/commonjs/ui/stores/followStore.js.map +1 -1
- package/lib/commonjs/ui/utils/accountDeletionHandoff.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/commonjs/ui/utils/clipboard.native.js +23 -6
- package/lib/commonjs/ui/utils/clipboard.native.js.map +1 -1
- package/lib/commonjs/ui/utils/commonsStoreLinks.js.map +1 -1
- package/lib/commonjs/ui/utils/errorHandlers.js.map +1 -1
- package/lib/commonjs/ui/utils/fileManagement.js +24 -24
- package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
- package/lib/commonjs/ui/utils/iconNames.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/ui/utils/userUtils.js.map +1 -1
- package/lib/commonjs/utils/hookUtils.js +2 -4
- package/lib/commonjs/utils/hookUtils.js.map +1 -1
- package/lib/module/assets/assets/icons/OxyServices.tsx +42 -40
- package/lib/module/assets/assets/illustrations/HighFive.tsx +58 -31
- package/lib/module/assets/icons/OxyServices.js.map +1 -1
- package/lib/module/assets/illustrations/HighFive.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/notifications/deviceNotifications.js +8 -4
- package/lib/module/notifications/deviceNotifications.js.map +1 -1
- package/lib/module/ui/analytics/productAnalytics.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/BenefitList.js.map +1 -1
- package/lib/module/ui/components/BloomLocaleBridge.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/FollowTargetButton.js +1 -1
- package/lib/module/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/module/ui/components/LanguageBridge.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyAuthPrompt.js.map +1 -1
- package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +1 -1
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/OxySignInRequestSurface.js.map +1 -1
- package/lib/module/ui/components/PeableButton.js.map +1 -1
- package/lib/module/ui/components/ProfileButton.js.map +1 -1
- package/lib/module/ui/components/ProfileSummaryCard.js +1 -1
- package/lib/module/ui/components/ProfileSummaryCard.js.map +1 -1
- package/lib/module/ui/components/RequireOxyAuth.js.map +1 -1
- package/lib/module/ui/components/SettingsIcon.js.map +1 -1
- package/lib/module/ui/components/SurfaceHeaderAction.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
- package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/components/feedback/constants.js.map +1 -1
- package/lib/module/ui/components/feedback/useFeedbackForm.js.map +1 -1
- package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
- package/lib/module/ui/components/fileManagement/FileViewer.js +1 -1
- package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/components/followButtonAccessibility.js.map +1 -1
- package/lib/module/ui/components/followRules.js.map +1 -1
- package/lib/module/ui/components/icon/FAIRWalletIcon.js.map +1 -1
- package/lib/module/ui/components/logo/LogoText.js.map +1 -1
- package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/module/ui/components/oauthNavigation.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentMethodStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentSummaryStep.js +1 -1
- package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
- package/lib/module/ui/components/payment/constants.js.map +1 -1
- package/lib/module/ui/components/payment/paymentStyles.js.map +1 -1
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/module/ui/components/signIn/InlineCommonsQr.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAccountPicker.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignUpPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/module/ui/components/signIn/artwork.js.map +1 -1
- package/lib/module/ui/components/signIn/signInFlowStore.js.map +1 -1
- package/lib/module/ui/components/styles/overlay.js.map +1 -1
- package/lib/module/ui/components/styles/shadow.js.map +1 -1
- package/lib/module/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
- package/lib/module/ui/components/utils/hasTouchHandler.js.map +1 -1
- package/lib/module/ui/components/utils/roundLayoutSize.js.map +1 -1
- package/lib/module/ui/components/utils/splitStyles.js.map +1 -1
- package/lib/module/ui/constants/spacing.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/isolatedOAuthSession.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/module/ui/hooks/accountQueryPersistence.js.map +1 -1
- package/lib/module/ui/hooks/mutations/mutationFactory.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/persistedQueryCache.js.map +1 -1
- package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useFileQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/usePaymentQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/usePersonalPlans.js.map +1 -1
- package/lib/module/ui/hooks/queries/useSecurityQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/userCache.js +38 -38
- package/lib/module/ui/hooks/queries/userCache.js.map +1 -1
- package/lib/module/ui/hooks/queryClient.js.map +1 -1
- package/lib/module/ui/hooks/useAssets.js.map +1 -1
- package/lib/module/ui/hooks/useAsyncAction.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceSwitcher.js.map +1 -1
- package/lib/module/ui/hooks/useFileFiltering.js.map +1 -1
- package/lib/module/ui/hooks/useFollow.js.map +1 -1
- package/lib/module/ui/hooks/useI18n.js.map +1 -1
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/hooks/useReduceMotion.js.map +1 -1
- package/lib/module/ui/hooks/useResolvedFileUrls.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/module/ui/hooks/useStorage.js.map +1 -1
- package/lib/module/ui/hooks/useSurfaceFrameWidth.js.map +1 -1
- package/lib/module/ui/icons/Ionicons.js.map +1 -1
- package/lib/module/ui/icons/MaterialCommunityIcons.js.map +1 -1
- package/lib/module/ui/icons/decorativeIconSet.js.map +1 -1
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/isFrontend.js.map +1 -1
- package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/navigation/surfaces.js.map +1 -1
- package/lib/module/ui/oauth/applicationSessionLane.js.map +1 -1
- package/lib/module/ui/oauth/browserAuthTransport.js.map +1 -1
- package/lib/module/ui/oauth/browserBridge.js.map +1 -1
- package/lib/module/ui/oauth/completeOAuthCode.js.map +1 -1
- package/lib/module/ui/oauth/nativeAuthTransport.js.map +1 -1
- package/lib/module/ui/oauth/oauthHandshake.js.map +1 -1
- package/lib/module/ui/oauth/oauthPopup.js.map +1 -1
- package/lib/module/ui/oauth/oauthPopupMessages.js.map +1 -1
- package/lib/module/ui/runtime/createOxyRuntime.js.map +1 -1
- package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSecurityPanelScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -1
- package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/DomainsScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +2 -5
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
- package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/module/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
- package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/NotificationsScreen.js.map +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +3 -3
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
- package/lib/module/ui/screens/UserListScreen.js +1 -1
- package/lib/module/ui/screens/UserListScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/FileLibraryError.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/FileListSection.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +7 -7
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/UploadBar.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/photoGridLayout.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/shared.js.map +1 -1
- package/lib/module/ui/screens/linkFormat.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustFAQScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/module/ui/server.js.map +1 -1
- package/lib/module/ui/session/authStore.js.map +1 -1
- package/lib/module/ui/session/backgroundSession.js.map +1 -1
- package/lib/module/ui/session/createSessionClient.js.map +1 -1
- package/lib/module/ui/session/deviceCredentialTracker.js.map +1 -1
- package/lib/module/ui/session/identityBinding.js.map +1 -1
- package/lib/module/ui/session/sharedDeviceCredentialStore.js.map +1 -1
- package/lib/module/ui/session/tokenLossRecovery.js.map +1 -1
- package/lib/module/ui/session/tokenTransport.js.map +1 -1
- package/lib/module/ui/stores/assetStore.js.map +1 -1
- package/lib/module/ui/stores/followStore.js.map +1 -1
- package/lib/module/ui/utils/accountDeletionHandoff.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/ui/utils/clipboard.native.js +23 -6
- package/lib/module/ui/utils/clipboard.native.js.map +1 -1
- package/lib/module/ui/utils/commonsStoreLinks.js.map +1 -1
- package/lib/module/ui/utils/errorHandlers.js.map +1 -1
- package/lib/module/ui/utils/fileManagement.js +25 -25
- package/lib/module/ui/utils/fileManagement.js.map +1 -1
- package/lib/module/ui/utils/iconNames.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/ui/utils/userUtils.js.map +1 -1
- package/lib/module/utils/hookUtils.js +2 -4
- package/lib/module/utils/hookUtils.js.map +1 -1
- package/lib/typescript/commonjs/assets/icons/OxyServices.d.ts.map +1 -1
- package/lib/typescript/commonjs/assets/illustrations/HighFive.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -4
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts +8 -4
- package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/analytics/productAnalytics.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/BenefitList.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/LanguageBridge.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/LanguageBridge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthPrompt.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/PeableButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileSummaryCard.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SettingsIcon.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceHeaderAction.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/feedback/constants.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/feedback/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/feedback/useFeedbackForm.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/followButtonAccessibility.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/followRules.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/logo/LogoText.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/oauthNavigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/PaymentMethodStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/PaymentReviewStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/constants.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/paymentStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/types.d.ts +6 -6
- package/lib/typescript/commonjs/ui/components/payment/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/artwork.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/signInFlowStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/styles/overlay.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/utils/hasTouchHandler.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/utils/roundLayoutSize.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/utils/splitStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/isolatedOAuthSession.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/mutationFactory.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/paymentTypes.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useFileQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts +5 -5
- package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAsyncAction.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useResolvedFileUrls.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSettingToggle.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useStorage.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useStorage.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSurfaceFrameWidth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/decorativeIconSet.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/isFrontend.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/applicationSessionLane.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/browserBridge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/oauthHandshake.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/oauthPopupMessages.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSecurityPanelScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/DomainsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/HelpSupportScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/NotificationsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/PreferencesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/UserLinksScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/UploadBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/photoGridLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/shared.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/linkFormat.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustAboutScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustRewardsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/deviceCredentialTracker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/identityBinding.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/tokenLossRecovery.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/assetStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/followStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/fileManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/surfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/accountDeletionHandoff.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts +16 -5
- package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/commonsStoreLinks.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/errorHandlers.d.ts +1 -1
- package/lib/typescript/commonjs/ui/utils/errorHandlers.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +4 -4
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +1 -1
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/userUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/hookUtils.d.ts.map +1 -1
- package/lib/typescript/module/assets/icons/OxyServices.d.ts.map +1 -1
- package/lib/typescript/module/assets/illustrations/HighFive.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -4
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/notifications/deviceNotifications.d.ts +8 -4
- package/lib/typescript/module/notifications/deviceNotifications.d.ts.map +1 -1
- package/lib/typescript/module/ui/analytics/productAnalytics.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/BenefitList.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowTargetButton.d.ts +1 -1
- package/lib/typescript/module/ui/components/FollowTargetButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/LanguageBridge.d.ts +1 -1
- package/lib/typescript/module/ui/components/LanguageBridge.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthPrompt.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/PeableButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileSummaryCard.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SettingsIcon.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceHeaderAction.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/feedback/constants.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/feedback/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/feedback/useFeedbackForm.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/followButtonAccessibility.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/followRules.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/logo/LogoText.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/oauthNavigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/PaymentMethodStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/PaymentReviewStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/constants.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/paymentStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/types.d.ts +6 -6
- package/lib/typescript/module/ui/components/payment/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/artwork.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/signInFlowStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/styles/overlay.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/utils/hasTouchHandler.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/utils/roundLayoutSize.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/utils/splitStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/isolatedOAuthSession.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/mutationFactory.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/paymentTypes.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useFileQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/userCache.d.ts +5 -5
- package/lib/typescript/module/ui/hooks/queries/userCache.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAssets.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAsyncAction.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useResolvedFileUrls.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSettingToggle.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useStorage.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useStorage.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSurfaceFrameWidth.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/Ionicons.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/decorativeIconSet.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/isFrontend.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/applicationSessionLane.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/browserBridge.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/oauthHandshake.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/oauthPopup.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/oauthPopupMessages.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSecurityPanelScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/DomainsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/HelpSupportScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/LearnMoreUsernamesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/NotificationsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/PreferencesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/UserLinksScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/UserListScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/UploadBar.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/photoGridLayout.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/shared.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/linkFormat.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustAboutScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustRewardsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/backgroundSession.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/deviceCredentialTracker.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/identityBinding.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/tokenLossRecovery.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/assetStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/followStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/fileManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/surfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/accountDeletionHandoff.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/clipboard.native.d.ts +16 -5
- package/lib/typescript/module/ui/utils/clipboard.native.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/commonsStoreLinks.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/errorHandlers.d.ts +1 -1
- package/lib/typescript/module/ui/utils/errorHandlers.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/fileManagement.d.ts +4 -4
- package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +1 -1
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/userUtils.d.ts.map +1 -1
- package/lib/typescript/module/utils/hookUtils.d.ts.map +1 -1
- package/lib/typescript/types/expo-crypto.d.ts +2 -4
- package/lib/typescript/types/expo-document-picker.d.ts +27 -28
- package/lib/typescript/types/expo-image-picker.d.ts +27 -27
- package/lib/typescript/types/expo-secure-store.d.ts +11 -4
- package/lib/typescript/types/express.d.ts +22 -7
- package/package.json +5 -6
- package/src/assets/icons/OxyServices.tsx +42 -40
- package/src/assets/illustrations/HighFive.tsx +58 -31
- package/src/index.ts +99 -86
- package/src/notifications/deviceNotifications.ts +9 -7
- package/src/types/expo-crypto.d.ts +2 -4
- package/src/types/expo-document-picker.d.ts +27 -28
- package/src/types/expo-image-picker.d.ts +27 -27
- package/src/types/expo-secure-store.d.ts +11 -4
- package/src/types/express.d.ts +22 -7
- package/src/ui/analytics/productAnalytics.tsx +5 -5
- package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +91 -29
- package/src/ui/boot/runProviderColdBoot.ts +12 -3
- package/src/ui/client.ts +31 -31
- package/src/ui/components/BenefitList.tsx +78 -77
- package/src/ui/components/BloomLocaleBridge.tsx +3 -3
- package/src/ui/components/FollowButton.tsx +88 -84
- package/src/ui/components/FollowTargetButton.tsx +15 -6
- package/src/ui/components/LanguageBridge.tsx +24 -19
- package/src/ui/components/OxyAuthChooser.tsx +17 -3
- package/src/ui/components/OxyAuthPrompt.tsx +79 -98
- package/src/ui/components/OxyConsentScreen.tsx +51 -12
- package/src/ui/components/OxyProvider.tsx +189 -182
- package/src/ui/components/OxySignInButton.tsx +415 -405
- package/src/ui/components/OxySignInRequestSurface.tsx +7 -1
- package/src/ui/components/PeableButton.tsx +57 -49
- package/src/ui/components/ProfileButton.tsx +158 -109
- package/src/ui/components/ProfileSummaryCard.tsx +64 -64
- package/src/ui/components/RequireOxyAuth.tsx +15 -3
- package/src/ui/components/SettingsIcon.tsx +24 -19
- package/src/ui/components/SurfaceHeaderAction.tsx +6 -1
- package/src/ui/components/SurfaceScreen.tsx +21 -16
- package/src/ui/components/__tests__/BloomLocaleBridge.test.tsx +1 -1
- package/src/ui/components/__tests__/LanguageBridge.test.tsx +15 -5
- package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +21 -11
- package/src/ui/components/__tests__/followButtonAccessibility.test.tsx +42 -15
- package/src/ui/components/__tests__/followTargetButton.test.ts +23 -20
- package/src/ui/components/authChooser/AccountsMenuView.tsx +70 -56
- package/src/ui/components/authChooser/TroubleDisclosure.tsx +6 -1
- package/src/ui/components/authChooser/requestSurfaces.tsx +3 -1
- package/src/ui/components/authChooser/types.ts +6 -1
- package/src/ui/components/feedback/constants.ts +36 -12
- package/src/ui/components/feedback/types.ts +30 -30
- package/src/ui/components/feedback/useFeedbackForm.ts +38 -35
- package/src/ui/components/fileManagement/FileDetailsModal.tsx +86 -92
- package/src/ui/components/fileManagement/FileViewer.tsx +362 -338
- package/src/ui/components/fileManagement/UploadPreview.tsx +134 -127
- package/src/ui/components/followButtonAccessibility.ts +6 -2
- package/src/ui/components/followRules.ts +6 -2
- package/src/ui/components/icon/FAIRWalletIcon.tsx +47 -35
- package/src/ui/components/logo/LogoText.tsx +6 -1
- package/src/ui/components/modals/DeleteAccountModal.tsx +89 -83
- package/src/ui/components/oauthNavigation.ts +54 -56
- package/src/ui/components/payment/PaymentDetailsStep.tsx +269 -201
- package/src/ui/components/payment/PaymentMethodStep.tsx +88 -68
- package/src/ui/components/payment/PaymentReviewStep.tsx +119 -102
- package/src/ui/components/payment/PaymentSuccessStep.tsx +48 -48
- package/src/ui/components/payment/PaymentSummaryStep.tsx +167 -124
- package/src/ui/components/payment/constants.ts +38 -23
- package/src/ui/components/payment/paymentStyles.ts +252 -251
- package/src/ui/components/payment/types.ts +24 -24
- package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +207 -183
- package/src/ui/components/photogrid/__tests__/chunkPhotos.test.ts +32 -33
- package/src/ui/components/signIn/InlineCommonsQr.tsx +14 -6
- package/src/ui/components/signIn/OxyAccountPicker.tsx +19 -4
- package/src/ui/components/signIn/OxyAuthScreen.tsx +21 -11
- package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +47 -13
- package/src/ui/components/signIn/OxyDeleteAccountPanel.tsx +14 -4
- package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +69 -15
- package/src/ui/components/signIn/OxyPasswordPanel.tsx +19 -4
- package/src/ui/components/signIn/OxySignInPanel.tsx +74 -21
- package/src/ui/components/signIn/OxySignUpPanel.tsx +60 -12
- package/src/ui/components/signIn/ReauthStep.tsx +26 -5
- package/src/ui/components/signIn/accountFlowParts.tsx +45 -16
- package/src/ui/components/signIn/artwork.ts +3 -1
- package/src/ui/components/signIn/signInFlowStore.ts +5 -1
- package/src/ui/components/styles/overlay.tsx +10 -11
- package/src/ui/components/styles/shadow.tsx +59 -59
- package/src/ui/components/surfaces/ActionSheetSurface.tsx +51 -61
- package/src/ui/components/utils/hasTouchHandler.tsx +3 -13
- package/src/ui/components/utils/roundLayoutSize.ts +1 -2
- package/src/ui/components/utils/splitStyles.ts +2 -5
- package/src/ui/constants/spacing.ts +8 -9
- package/src/ui/context/OxyContext.tsx +324 -140
- package/src/ui/context/__tests__/commitSessionFlow.test.ts +8 -3
- package/src/ui/context/__tests__/isolatedOAuthSession.test.ts +11 -3
- package/src/ui/context/commitSessionFlow.ts +15 -3
- package/src/ui/context/hooks/useAuthOperations.ts +54 -16
- package/src/ui/context/isolatedOAuthSession.ts +4 -1
- package/src/ui/context/oxyContextTypes.ts +26 -9
- package/src/ui/context/useOxyAccountGraph.ts +26 -4
- package/src/ui/hooks/accountDialogSnapshot.ts +3 -1
- package/src/ui/hooks/accountQueryPersistence.ts +16 -5
- package/src/ui/hooks/mutations/mutationFactory.ts +11 -6
- package/src/ui/hooks/mutations/useAccountMutations.ts +77 -54
- package/src/ui/hooks/mutations/useServicesMutations.ts +10 -11
- package/src/ui/hooks/persistedQueryCache.ts +10 -2
- package/src/ui/hooks/queries/__tests__/userCache.test.ts +72 -17
- package/src/ui/hooks/queries/__tests__/userCacheClear.test.ts +18 -23
- package/src/ui/hooks/queries/paymentTypes.ts +2 -2
- package/src/ui/hooks/queries/queryKeys.ts +5 -5
- package/src/ui/hooks/queries/useAccountQueries.ts +12 -17
- package/src/ui/hooks/queries/useFileQueries.ts +88 -92
- package/src/ui/hooks/queries/usePaymentQueries.ts +13 -26
- package/src/ui/hooks/queries/usePersonalPlans.ts +14 -6
- package/src/ui/hooks/queries/useSecurityQueries.ts +16 -29
- package/src/ui/hooks/queries/useServicesQueries.ts +17 -23
- package/src/ui/hooks/queries/userCache.ts +249 -270
- package/src/ui/hooks/queryClient.ts +9 -5
- package/src/ui/hooks/useAssets.ts +166 -160
- package/src/ui/hooks/useAsyncAction.ts +92 -91
- package/src/ui/hooks/useAuth.ts +45 -42
- package/src/ui/hooks/useAvatarPicker.ts +2 -5
- package/src/ui/hooks/useDeviceManagement.ts +0 -2
- package/src/ui/hooks/useDeviceSwitcher.ts +73 -68
- package/src/ui/hooks/useFileFiltering.ts +2 -2
- package/src/ui/hooks/useFollow.ts +139 -67
- package/src/ui/hooks/useFollowTarget.ts +11 -11
- package/src/ui/hooks/useI18n.ts +7 -2
- package/src/ui/hooks/useProfileEditing.ts +167 -155
- package/src/ui/hooks/useQueryClient.ts +2 -3
- package/src/ui/hooks/useReduceMotion.ts +21 -21
- package/src/ui/hooks/useResolvedFileUrls.ts +5 -1
- package/src/ui/hooks/useSessionManagement.ts +3 -1
- package/src/ui/hooks/useSettingToggle.ts +122 -114
- package/src/ui/hooks/useStorage.ts +1 -4
- package/src/ui/hooks/useSurfaceFrameWidth.ts +3 -1
- package/src/ui/icons/Ionicons.ts +1 -5
- package/src/ui/icons/MaterialCommunityIcons.ts +1 -5
- package/src/ui/icons/__tests__/decorativeIconSet.test.tsx +3 -1
- package/src/ui/icons/decorativeIconSet.ts +13 -2
- package/src/ui/index.ts +4 -4
- package/src/ui/isFrontend.ts +3 -2
- package/src/ui/navigation/__tests__/getSurfaceConfig.test.ts +83 -83
- package/src/ui/navigation/__tests__/surfaceNavStack.test.ts +8 -2
- package/src/ui/navigation/bottomSheetManager.ts +1 -1
- package/src/ui/navigation/routes.ts +90 -88
- package/src/ui/navigation/surfaces.ts +7 -6
- package/src/ui/oauth/__tests__/browserAuthTransport.test.ts +18 -11
- package/src/ui/oauth/__tests__/browserBridge.test.ts +108 -21
- package/src/ui/oauth/__tests__/completeOAuthCode.test.ts +9 -6
- package/src/ui/oauth/__tests__/explicitOAuthConsent.test.ts +222 -241
- package/src/ui/oauth/__tests__/nativeAuthTransport.test.ts +118 -35
- package/src/ui/oauth/__tests__/oauthBearerCommit.http.test.ts +60 -18
- package/src/ui/oauth/__tests__/oauthPopup.test.ts +2 -7
- package/src/ui/oauth/__tests__/oauthPopupMessages.test.ts +9 -7
- package/src/ui/oauth/__tests__/sharedCompletionPath.test.ts +4 -5
- package/src/ui/oauth/applicationSessionLane.ts +12 -3
- package/src/ui/oauth/browserAuthTransport.ts +15 -7
- package/src/ui/oauth/browserBridge.ts +22 -5
- package/src/ui/oauth/completeOAuthCode.ts +9 -6
- package/src/ui/oauth/nativeAuthTransport.ts +73 -20
- package/src/ui/oauth/oauthHandshake.ts +6 -1
- package/src/ui/oauth/oauthPopup.ts +6 -4
- package/src/ui/oauth/oauthPopupMessages.ts +3 -4
- package/src/ui/oauth/types.ts +1 -3
- package/src/ui/runtime/createOxyRuntime.ts +21 -4
- package/src/ui/screens/AccountMembersScreen.tsx +274 -204
- package/src/ui/screens/AccountSecurityPanelScreen.tsx +3 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +133 -113
- package/src/ui/screens/AccountVerificationScreen.tsx +176 -161
- package/src/ui/screens/AppInfoScreen.tsx +360 -304
- package/src/ui/screens/AvatarCropScreen.tsx +977 -988
- package/src/ui/screens/ChangeAvatarScreen.tsx +257 -253
- package/src/ui/screens/ConnectedAppsScreen.tsx +117 -130
- package/src/ui/screens/CreateAccountScreen.tsx +173 -129
- package/src/ui/screens/DomainsScreen.tsx +157 -140
- package/src/ui/screens/EditProfileFieldScreen.tsx +693 -663
- package/src/ui/screens/EditProfileScreen.tsx +106 -100
- package/src/ui/screens/FileManagementScreen.tsx +1505 -1162
- package/src/ui/screens/HelpSupportScreen.tsx +88 -75
- package/src/ui/screens/HistoryViewScreen.tsx +143 -102
- package/src/ui/screens/LanguageSelectorScreen.tsx +206 -199
- package/src/ui/screens/LearnMoreUsernamesScreen.tsx +92 -110
- package/src/ui/screens/LegalDocumentsScreen.tsx +134 -193
- package/src/ui/screens/ManageAccountScreen.tsx +649 -871
- package/src/ui/screens/NotificationsScreen.tsx +111 -143
- package/src/ui/screens/PaymentGatewayScreen.tsx +229 -244
- package/src/ui/screens/PreferencesScreen.tsx +153 -196
- package/src/ui/screens/PrivacySettingsScreen.tsx +509 -351
- package/src/ui/screens/ProfileScreen.tsx +416 -319
- package/src/ui/screens/SearchSettingsScreen.tsx +100 -98
- package/src/ui/screens/UserLinksScreen.tsx +48 -43
- package/src/ui/screens/UserListScreen.tsx +53 -30
- package/src/ui/screens/WelcomeNewUserScreen.tsx +430 -334
- package/src/ui/screens/__tests__/linkFormat.test.ts +58 -62
- package/src/ui/screens/__tests__/photoGridLayout.test.ts +43 -47
- package/src/ui/screens/fileManagement/FileLibraryError.tsx +33 -27
- package/src/ui/screens/fileManagement/FileListSection.tsx +96 -87
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +508 -475
- package/src/ui/screens/fileManagement/UploadBar.tsx +26 -16
- package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +514 -490
- package/src/ui/screens/fileManagement/photoGridLayout.ts +13 -13
- package/src/ui/screens/fileManagement/shared.ts +40 -38
- package/src/ui/screens/linkFormat.ts +14 -14
- package/src/ui/screens/trust/TrustAboutScreen.tsx +53 -51
- package/src/ui/screens/trust/TrustCenterScreen.tsx +156 -214
- package/src/ui/screens/trust/TrustFAQScreen.tsx +60 -67
- package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +183 -183
- package/src/ui/screens/trust/TrustRewardsScreen.tsx +495 -494
- package/src/ui/screens/trust/TrustRulesScreen.tsx +84 -85
- package/src/ui/server.ts +12 -15
- package/src/ui/session/__tests__/authStore.test.ts +6 -1
- package/src/ui/session/__tests__/createSessionClient.test.ts +22 -8
- package/src/ui/session/__tests__/sharedDeviceCredentialStore.test.ts +24 -6
- package/src/ui/session/__tests__/tokenLossRecovery.test.ts +3 -1
- package/src/ui/session/__tests__/tokenTransport.test.ts +9 -2
- package/src/ui/session/authStore.ts +7 -1
- package/src/ui/session/backgroundSession.ts +4 -3
- package/src/ui/session/createSessionClient.ts +17 -5
- package/src/ui/session/deviceCredentialTracker.ts +12 -6
- package/src/ui/session/identityBinding.ts +6 -1
- package/src/ui/session/sharedDeviceCredentialStore.ts +15 -4
- package/src/ui/session/tokenLossRecovery.ts +24 -10
- package/src/ui/session/tokenTransport.ts +5 -1
- package/src/ui/stores/__tests__/followStore.test.ts +20 -7
- package/src/ui/stores/assetStore.ts +209 -207
- package/src/ui/stores/followStore.ts +80 -39
- package/src/ui/stores/followTargetStore.ts +1 -1
- package/src/ui/types/fileManagement.ts +48 -49
- package/src/ui/types/navigation.ts +168 -168
- package/src/ui/types/surfaceScreen.ts +4 -1
- package/src/ui/utils/__tests__/deviceSessionTitle.test.ts +4 -2
- package/src/ui/utils/accountDeletionHandoff.ts +14 -3
- package/src/ui/utils/avatarUtils.ts +7 -6
- package/src/ui/utils/clipboard.native.ts +23 -6
- package/src/ui/utils/commonsStoreLinks.ts +2 -1
- package/src/ui/utils/errorHandlers.ts +7 -12
- package/src/ui/utils/fileManagement.ts +92 -103
- package/src/ui/utils/iconNames.ts +60 -60
- package/src/ui/utils/sessionHelpers.ts +1 -6
- package/src/ui/utils/storageHelpers.ts +0 -1
- package/src/ui/utils/userUtils.ts +2 -6
- package/src/utils/hookUtils.ts +77 -65
|
@@ -28,17 +28,25 @@
|
|
|
28
28
|
|
|
29
29
|
import type React from 'react';
|
|
30
30
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
31
|
-
import {
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
tx: number,
|
|
122
|
+
ty: number,
|
|
123
|
+
s: number,
|
|
124
|
+
baseW: number,
|
|
125
|
+
baseH: number,
|
|
118
126
|
): { tx: number; ty: number } {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
191
|
-
|
|
198
|
+
uri: string;
|
|
199
|
+
cleanup?: () => void;
|
|
192
200
|
}
|
|
193
201
|
|
|
194
202
|
function isCanvasLoadFailure(err: unknown): boolean {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
return message.replace(SENSITIVE_URI_PATTERN, '[redacted-uri]');
|
|
207
215
|
}
|
|
208
216
|
|
|
209
217
|
function normalizeCropError(err: unknown): Error {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
338
|
+
dismiss,
|
|
339
|
+
goBack,
|
|
340
|
+
imageUri,
|
|
341
|
+
imageFileId,
|
|
342
|
+
sourceWidth,
|
|
343
|
+
sourceHeight,
|
|
336
344
|
}) => {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
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
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
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
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
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
|
-
|
|
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;
|