@oxyhq/services 6.9.46 → 6.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/index.js +36 -13
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/client.js +0 -20
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/ActingAsBanner.js +0 -4
- package/lib/commonjs/ui/components/ActingAsBanner.js.map +1 -1
- package/lib/commonjs/ui/components/ActivityIndicator.js +1 -1
- package/lib/commonjs/ui/components/ActivityIndicator.js.map +1 -1
- package/lib/commonjs/ui/components/Avatar.js +0 -2
- package/lib/commonjs/ui/components/Avatar.js.map +1 -1
- package/lib/commonjs/ui/components/BottomSheet.js +33 -398
- package/lib/commonjs/ui/components/BottomSheet.js.map +1 -1
- package/lib/commonjs/ui/components/BottomSheetRouter.js +8 -0
- package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +3 -5
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/Header.js +0 -5
- package/lib/commonjs/ui/components/Header.js.map +1 -1
- package/lib/commonjs/ui/components/Icon.js +6 -7
- package/lib/commonjs/ui/components/Icon.js.map +1 -1
- package/lib/commonjs/ui/components/IconButton/IconButton.js +1 -5
- package/lib/commonjs/ui/components/IconButton/IconButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyPayButton.js +0 -2
- package/lib/commonjs/ui/components/OxyPayButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +100 -37
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +23 -9
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileCard.js +11 -8
- package/lib/commonjs/ui/components/ProfileCard.js.map +1 -1
- package/lib/commonjs/ui/components/QuickActions.js +2 -2
- package/lib/commonjs/ui/components/QuickActions.js.map +1 -1
- package/lib/commonjs/ui/components/SignInModal.js +139 -284
- package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
- package/lib/commonjs/ui/components/StepBasedScreen.js +0 -2
- package/lib/commonjs/ui/components/StepBasedScreen.js.map +1 -1
- package/lib/commonjs/ui/components/TextField.js +0 -1
- package/lib/commonjs/ui/components/TextField.js.map +1 -1
- package/lib/commonjs/ui/components/TouchableRipple/TouchableRipple.native.js +1 -1
- package/lib/commonjs/ui/components/TouchableRipple/TouchableRipple.native.js.map +1 -1
- package/lib/commonjs/ui/components/feedback/feedbackStyles.js +0 -2
- package/lib/commonjs/ui/components/feedback/feedbackStyles.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js +0 -2
- package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js +80 -76
- package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js +6 -8
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +18 -23
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/styles.js +213 -25
- package/lib/commonjs/ui/components/fileManagement/styles.js.map +1 -1
- package/lib/commonjs/ui/components/logo/LogoIcon.js +63 -0
- package/lib/commonjs/ui/components/logo/LogoIcon.js.map +1 -0
- package/lib/commonjs/ui/components/logo/LogoText.js +50 -0
- package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -0
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +54 -88
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentReviewStep.js +1 -1
- package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/paymentStyles.js +2 -10
- package/lib/commonjs/ui/components/payment/paymentStyles.js.map +1 -1
- package/lib/commonjs/ui/components/styles/overlay.js +7 -9
- package/lib/commonjs/ui/components/styles/overlay.js.map +1 -1
- package/lib/commonjs/ui/components/theming.js +0 -1
- package/lib/commonjs/ui/components/theming.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +34 -21
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +1 -3
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/index.js +32 -12
- package/lib/commonjs/ui/hooks/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/mutationFactory.js +10 -5
- package/lib/commonjs/ui/hooks/mutations/mutationFactory.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/mutationKeys.js +34 -0
- package/lib/commonjs/ui/hooks/mutations/mutationKeys.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +235 -57
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js +17 -13
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/index.js +6 -0
- package/lib/commonjs/ui/hooks/queries/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js +2 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +80 -3
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useSecurityQueries.js +36 -1
- package/lib/commonjs/ui/hooks/queries/useSecurityQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queryClient.js +168 -70
- package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAsyncAction.js +7 -7
- package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +0 -8
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAvatarPicker.js +71 -20
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFileDownloadUrl.js +12 -41
- package/lib/commonjs/ui/hooks/useFileDownloadUrl.js.map +1 -1
- package/lib/commonjs/ui/hooks/useMutationStatus.js +86 -0
- package/lib/commonjs/ui/hooks/useMutationStatus.js.map +1 -0
- package/lib/commonjs/ui/hooks/useOnlineStatus.js +33 -0
- package/lib/commonjs/ui/hooks/useOnlineStatus.js.map +1 -0
- package/lib/commonjs/ui/hooks/useSessionSocket.js +101 -130
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSettingToggle.js +4 -4
- package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/commonjs/ui/index.js +1 -11
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +55 -3
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +16 -16
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +108 -123
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +20 -19
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +204 -161
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js +18 -18
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js +22 -23
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AvatarCropScreen.js +939 -0
- package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/CreateManagedAccountScreen.js +13 -20
- package/lib/commonjs/ui/screens/CreateManagedAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +10 -13
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FAQScreen.js +4 -4
- package/lib/commonjs/ui/screens/FAQScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FeedbackScreen.js +17 -15
- package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +797 -159
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HelpSupportScreen.js +8 -9
- package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HistoryViewScreen.js +29 -21
- package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +3 -5
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +5 -5
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +2 -2
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +75 -73
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +32 -21
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +4 -3
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +3 -3
- package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +57 -56
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/UserLinksScreen.js +2 -3
- package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/UserListScreen.js +39 -22
- package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +18 -21
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js +0 -3
- package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +6 -10
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +6 -6
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +13 -19
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +5 -5
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
- package/lib/commonjs/ui/server.js +0 -5
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +0 -5
- package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
- package/lib/commonjs/ui/styles/index.js +0 -11
- package/lib/commonjs/ui/styles/index.js.map +1 -1
- package/lib/commonjs/ui/styles/spacing.js +1 -42
- package/lib/commonjs/ui/styles/spacing.js.map +1 -1
- package/lib/commonjs/ui/styles/theme.js +0 -4
- package/lib/commonjs/ui/styles/theme.js.map +1 -1
- package/lib/commonjs/ui/utils/fileManagement.js +58 -39
- package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js +3 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/ui/utils/userUtils.js +33 -16
- package/lib/commonjs/ui/utils/userUtils.js.map +1 -1
- package/lib/module/index.js +9 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/client.js +0 -6
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/ActingAsBanner.js +0 -4
- package/lib/module/ui/components/ActingAsBanner.js.map +1 -1
- package/lib/module/ui/components/ActivityIndicator.js +1 -1
- package/lib/module/ui/components/ActivityIndicator.js.map +1 -1
- package/lib/module/ui/components/Avatar.js +0 -2
- package/lib/module/ui/components/Avatar.js.map +1 -1
- package/lib/module/ui/components/BottomSheet.js +35 -400
- package/lib/module/ui/components/BottomSheet.js.map +1 -1
- package/lib/module/ui/components/BottomSheetRouter.js +9 -1
- package/lib/module/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +2 -4
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/Header.js +0 -5
- package/lib/module/ui/components/Header.js.map +1 -1
- package/lib/module/ui/components/Icon.js +6 -7
- package/lib/module/ui/components/Icon.js.map +1 -1
- package/lib/module/ui/components/IconButton/IconButton.js +1 -5
- package/lib/module/ui/components/IconButton/IconButton.js.map +1 -1
- package/lib/module/ui/components/OxyPayButton.js +0 -2
- package/lib/module/ui/components/OxyPayButton.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +103 -40
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +23 -9
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/ProfileCard.js +11 -8
- package/lib/module/ui/components/ProfileCard.js.map +1 -1
- package/lib/module/ui/components/QuickActions.js +2 -2
- package/lib/module/ui/components/QuickActions.js.map +1 -1
- package/lib/module/ui/components/SignInModal.js +137 -284
- package/lib/module/ui/components/SignInModal.js.map +1 -1
- package/lib/module/ui/components/StepBasedScreen.js +0 -2
- package/lib/module/ui/components/StepBasedScreen.js.map +1 -1
- package/lib/module/ui/components/TextField.js +0 -1
- package/lib/module/ui/components/TextField.js.map +1 -1
- package/lib/module/ui/components/TouchableRipple/TouchableRipple.native.js +1 -1
- package/lib/module/ui/components/TouchableRipple/TouchableRipple.native.js.map +1 -1
- package/lib/module/ui/components/feedback/feedbackStyles.js +0 -2
- package/lib/module/ui/components/feedback/feedbackStyles.js.map +1 -1
- package/lib/module/ui/components/fileManagement/AnimatedButton.js +0 -1
- package/lib/module/ui/components/fileManagement/AnimatedButton.js.map +1 -1
- package/lib/module/ui/components/fileManagement/FileDetailsModal.js +80 -75
- package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
- package/lib/module/ui/components/fileManagement/FileViewer.js +6 -7
- package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js +18 -22
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/components/fileManagement/styles.js +212 -24
- package/lib/module/ui/components/fileManagement/styles.js.map +1 -1
- package/lib/module/ui/components/logo/LogoIcon.js +56 -0
- package/lib/module/ui/components/logo/LogoIcon.js.map +1 -0
- package/lib/module/ui/components/logo/LogoText.js +43 -0
- package/lib/module/ui/components/logo/LogoText.js.map +1 -0
- package/lib/module/ui/components/modals/DeleteAccountModal.js +55 -88
- package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentReviewStep.js +1 -1
- package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentSuccessStep.js +1 -1
- package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -1
- package/lib/module/ui/components/payment/paymentStyles.js +2 -10
- package/lib/module/ui/components/payment/paymentStyles.js.map +1 -1
- package/lib/module/ui/components/styles/overlay.js +8 -8
- package/lib/module/ui/components/styles/overlay.js.map +1 -1
- package/lib/module/ui/components/theming.js +0 -1
- package/lib/module/ui/components/theming.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +32 -19
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +1 -3
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/index.js +5 -3
- package/lib/module/ui/hooks/index.js.map +1 -1
- package/lib/module/ui/hooks/mutations/mutationFactory.js +6 -1
- package/lib/module/ui/hooks/mutations/mutationFactory.js.map +1 -1
- package/lib/module/ui/hooks/mutations/mutationKeys.js +30 -0
- package/lib/module/ui/hooks/mutations/mutationKeys.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +231 -52
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useServicesMutations.js +9 -5
- package/lib/module/ui/hooks/mutations/useServicesMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/index.js +1 -1
- package/lib/module/ui/hooks/queries/index.js.map +1 -1
- package/lib/module/ui/hooks/queries/queryKeys.js +2 -1
- package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js +80 -3
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useSecurityQueries.js +35 -1
- package/lib/module/ui/hooks/queries/useSecurityQueries.js.map +1 -1
- package/lib/module/ui/hooks/queryClient.js +166 -68
- package/lib/module/ui/hooks/queryClient.js.map +1 -1
- package/lib/module/ui/hooks/useAsyncAction.js +3 -3
- package/lib/module/ui/hooks/useAsyncAction.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +0 -3
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useAvatarPicker.js +73 -22
- package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/module/ui/hooks/useFileDownloadUrl.js +11 -39
- package/lib/module/ui/hooks/useFileDownloadUrl.js.map +1 -1
- package/lib/module/ui/hooks/useMutationStatus.js +82 -0
- package/lib/module/ui/hooks/useMutationStatus.js.map +1 -0
- package/lib/module/ui/hooks/useOnlineStatus.js +29 -0
- package/lib/module/ui/hooks/useOnlineStatus.js.map +1 -0
- package/lib/module/ui/hooks/useSessionSocket.js +101 -130
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/hooks/useSettingToggle.js +1 -1
- package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/module/ui/index.js +1 -10
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +54 -2
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +15 -14
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +96 -111
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +19 -18
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +189 -145
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js +15 -15
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +14 -15
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js +936 -0
- package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -0
- package/lib/module/ui/screens/CreateManagedAccountScreen.js +11 -18
- package/lib/module/ui/screens/CreateManagedAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +8 -11
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/FAQScreen.js +3 -3
- package/lib/module/ui/screens/FAQScreen.js.map +1 -1
- package/lib/module/ui/screens/FeedbackScreen.js +14 -12
- package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +764 -125
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/HelpSupportScreen.js +2 -3
- package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
- package/lib/module/ui/screens/HistoryViewScreen.js +26 -17
- package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js +1 -3
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/module/ui/screens/LegalDocumentsScreen.js +3 -3
- package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js +2 -2
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +60 -57
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +27 -16
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +4 -3
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SavesCollectionsScreen.js +2 -2
- package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js +48 -47
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/UserLinksScreen.js +2 -3
- package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
- package/lib/module/ui/screens/UserListScreen.js +40 -23
- package/lib/module/ui/screens/UserListScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +16 -19
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaAboutScreen.js +0 -3
- package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js +6 -10
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaFAQScreen.js +1 -1
- package/lib/module/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +6 -6
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +13 -19
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js +5 -5
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
- package/lib/module/ui/server.js +0 -4
- package/lib/module/ui/server.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +0 -5
- package/lib/module/ui/styles/authStyles.js.map +1 -1
- package/lib/module/ui/styles/index.js +0 -1
- package/lib/module/ui/styles/index.js.map +1 -1
- package/lib/module/ui/styles/spacing.js +0 -42
- package/lib/module/ui/styles/spacing.js.map +1 -1
- package/lib/module/ui/styles/theme.js +0 -4
- package/lib/module/ui/styles/theme.js.map +1 -1
- package/lib/module/ui/utils/fileManagement.js +54 -36
- package/lib/module/ui/utils/fileManagement.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js +3 -1
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/ui/utils/userUtils.js +34 -16
- package/lib/module/ui/utils/userUtils.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +7 -3
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +0 -2
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ActingAsBanner.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Avatar.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/BottomSheet.d.ts +27 -27
- package/lib/typescript/commonjs/ui/components/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Header.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Icon.d.ts +3 -2
- package/lib/typescript/commonjs/ui/components/Icon.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/IconButton/IconButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyPayButton.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/ProfileCard.d.ts +4 -1
- package/lib/typescript/commonjs/ui/components/ProfileCard.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/StepBasedScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/TextField/Addons/Outline.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/TextField/helpers.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/TextField/types.d.ts +0 -1
- package/lib/typescript/commonjs/ui/components/TextField/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/TextField.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/TextField.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/feedback/feedbackStyles.d.ts +0 -1
- package/lib/typescript/commonjs/ui/components/feedback/feedbackStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts +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 +0 -2
- package/lib/typescript/commonjs/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts +3 -2
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts +200 -17
- package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/logo/LogoIcon.d.ts +22 -0
- package/lib/typescript/commonjs/ui/components/logo/LogoIcon.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/logo/LogoText.d.ts +22 -0
- package/lib/typescript/commonjs/ui/components/logo/LogoText.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/paymentStyles.d.ts +0 -7
- package/lib/typescript/commonjs/ui/components/payment/paymentStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/styles/overlay.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/styles/overlay.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/theming.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/index.d.ts +5 -3
- package/lib/typescript/commonjs/ui/hooks/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/mutationFactory.d.ts +11 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/mutationFactory.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/mutationKeys.d.ts +25 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/mutationKeys.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +161 -9
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/index.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts +1 -0
- package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +22 -8
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts +13 -3
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts +7 -5
- package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts +57 -9
- package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +0 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts +10 -2
- package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts +2 -6
- package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useMutationStatus.d.ts +23 -0
- package/lib/typescript/commonjs/ui/hooks/useMutationStatus.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useOnlineStatus.d.ts +13 -0
- package/lib/typescript/commonjs/ui/hooks/useOnlineStatus.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts +1 -2
- package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -4
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +48 -2
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.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 +49 -0
- package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/CreateManagedAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FeedbackScreen.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/PremiumSubscriptionScreen.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/SessionManagementScreen.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/karma/KarmaAboutScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts +0 -2
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/styles/authStyles.d.ts +0 -4
- package/lib/typescript/commonjs/ui/styles/authStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/styles/index.d.ts +0 -1
- package/lib/typescript/commonjs/ui/styles/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/styles/spacing.d.ts +0 -36
- package/lib/typescript/commonjs/ui/styles/spacing.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/styles/theme.d.ts +0 -1
- package/lib/typescript/commonjs/ui/styles/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +39 -12
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +3 -1
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/userUtils.d.ts +19 -18
- package/lib/typescript/commonjs/ui/utils/userUtils.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +7 -3
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +0 -2
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ActingAsBanner.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Avatar.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/BottomSheet.d.ts +27 -27
- package/lib/typescript/module/ui/components/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Header.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Icon.d.ts +3 -2
- package/lib/typescript/module/ui/components/Icon.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/IconButton/IconButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyPayButton.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/ProfileCard.d.ts +4 -1
- package/lib/typescript/module/ui/components/ProfileCard.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/StepBasedScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/TextField/Addons/Outline.d.ts +2 -2
- package/lib/typescript/module/ui/components/TextField/helpers.d.ts +2 -2
- package/lib/typescript/module/ui/components/TextField/types.d.ts +0 -1
- package/lib/typescript/module/ui/components/TextField/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/TextField.d.ts +1 -1
- package/lib/typescript/module/ui/components/TextField.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/feedback/feedbackStyles.d.ts +0 -1
- package/lib/typescript/module/ui/components/feedback/feedbackStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts +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 +0 -2
- package/lib/typescript/module/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts +3 -2
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/styles.d.ts +200 -17
- package/lib/typescript/module/ui/components/fileManagement/styles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/logo/LogoIcon.d.ts +22 -0
- package/lib/typescript/module/ui/components/logo/LogoIcon.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/logo/LogoText.d.ts +22 -0
- package/lib/typescript/module/ui/components/logo/LogoText.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts +2 -2
- package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/paymentStyles.d.ts +0 -7
- package/lib/typescript/module/ui/components/payment/paymentStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/styles/overlay.d.ts +3 -1
- package/lib/typescript/module/ui/components/styles/overlay.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/theming.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/index.d.ts +5 -3
- package/lib/typescript/module/ui/hooks/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/mutationFactory.d.ts +11 -0
- package/lib/typescript/module/ui/hooks/mutations/mutationFactory.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/mutationKeys.d.ts +25 -0
- package/lib/typescript/module/ui/hooks/mutations/mutationKeys.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +161 -9
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/index.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/queries/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts +1 -0
- package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +22 -8
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts +13 -3
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts +7 -5
- package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queryClient.d.ts +57 -9
- package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAssets.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts +0 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts +10 -2
- package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts +2 -6
- package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useMutationStatus.d.ts +23 -0
- package/lib/typescript/module/ui/hooks/useMutationStatus.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useOnlineStatus.d.ts +13 -0
- package/lib/typescript/module/ui/hooks/useOnlineStatus.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts +1 -2
- package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -4
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +48 -2
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSwitcherScreen.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 +49 -0
- package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/CreateManagedAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FeedbackScreen.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/PremiumSubscriptionScreen.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/SessionManagementScreen.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/karma/KarmaAboutScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/server.d.ts +0 -2
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/styles/authStyles.d.ts +0 -4
- package/lib/typescript/module/ui/styles/authStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/styles/index.d.ts +0 -1
- package/lib/typescript/module/ui/styles/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/styles/spacing.d.ts +0 -36
- package/lib/typescript/module/ui/styles/spacing.d.ts.map +1 -1
- package/lib/typescript/module/ui/styles/theme.d.ts +0 -1
- package/lib/typescript/module/ui/styles/theme.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/fileManagement.d.ts +39 -12
- package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +3 -1
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/userUtils.d.ts +19 -18
- package/lib/typescript/module/ui/utils/userUtils.d.ts.map +1 -1
- package/lib/typescript/types/expo-vector-icons.d.ts +7 -4
- package/package.json +64 -31
- package/src/index.ts +11 -4
- package/src/types/expo-vector-icons.d.ts +7 -4
- package/src/ui/client.ts +0 -6
- package/src/ui/components/ActingAsBanner.tsx +0 -4
- package/src/ui/components/ActivityIndicator.tsx +1 -1
- package/src/ui/components/Avatar.tsx +0 -2
- package/src/ui/components/BottomSheet.tsx +41 -440
- package/src/ui/components/BottomSheetRouter.tsx +12 -1
- package/src/ui/components/FollowButton.tsx +2 -4
- package/src/ui/components/Header.tsx +0 -5
- package/src/ui/components/Icon.tsx +12 -6
- package/src/ui/components/IconButton/IconButton.tsx +0 -3
- package/src/ui/components/OxyPayButton.tsx +0 -2
- package/src/ui/components/OxyProvider.tsx +127 -48
- package/src/ui/components/OxySignInButton.tsx +21 -9
- package/src/ui/components/ProfileCard.tsx +13 -10
- package/src/ui/components/QuickActions.tsx +2 -2
- package/src/ui/components/SignInModal.tsx +80 -199
- package/src/ui/components/StepBasedScreen.tsx +0 -2
- package/src/ui/components/TextField/Addons/Outline.tsx +2 -2
- package/src/ui/components/TextField/helpers.tsx +2 -2
- package/src/ui/components/TextField/types.tsx +0 -1
- package/src/ui/components/TextField.tsx +1 -2
- package/src/ui/components/TouchableRipple/TouchableRipple.native.tsx +1 -1
- package/src/ui/components/feedback/feedbackStyles.ts +0 -2
- package/src/ui/components/fileManagement/AnimatedButton.tsx +0 -1
- package/src/ui/components/fileManagement/FileDetailsModal.tsx +71 -65
- package/src/ui/components/fileManagement/FileViewer.tsx +6 -9
- package/src/ui/components/fileManagement/UploadPreview.tsx +21 -25
- package/src/ui/components/fileManagement/styles.ts +206 -18
- package/src/ui/components/logo/LogoIcon.tsx +70 -0
- package/src/ui/components/logo/LogoText.tsx +70 -0
- package/src/ui/components/modals/DeleteAccountModal.tsx +56 -91
- package/src/ui/components/payment/PaymentDetailsStep.tsx +1 -1
- package/src/ui/components/payment/PaymentReviewStep.tsx +1 -1
- package/src/ui/components/payment/PaymentSuccessStep.tsx +1 -1
- package/src/ui/components/payment/paymentStyles.ts +0 -8
- package/src/ui/components/styles/overlay.tsx +17 -8
- package/src/ui/components/theming.tsx +0 -1
- package/src/ui/components/types.tsx +3 -2
- package/src/ui/context/OxyContext.tsx +34 -19
- package/src/ui/context/hooks/useAuthOperations.ts +1 -3
- package/src/ui/hooks/index.ts +11 -4
- package/src/ui/hooks/mutations/mutationFactory.ts +16 -1
- package/src/ui/hooks/mutations/mutationKeys.ts +28 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +251 -59
- package/src/ui/hooks/mutations/useServicesMutations.ts +10 -4
- package/src/ui/hooks/queries/index.ts +1 -0
- package/src/ui/hooks/queries/queryKeys.ts +2 -0
- package/src/ui/hooks/queries/useAccountQueries.ts +83 -3
- package/src/ui/hooks/queries/useSecurityQueries.ts +42 -2
- package/src/ui/hooks/queryClient.ts +194 -69
- package/src/ui/hooks/useAsyncAction.ts +3 -3
- package/src/ui/hooks/useAuth.ts +0 -2
- package/src/ui/hooks/useAvatarPicker.ts +108 -27
- package/src/ui/hooks/useFileDownloadUrl.ts +15 -39
- package/src/ui/hooks/useMutationStatus.ts +111 -0
- package/src/ui/hooks/useOnlineStatus.ts +29 -0
- package/src/ui/hooks/useSessionSocket.ts +136 -126
- package/src/ui/hooks/useSettingToggle.ts +1 -1
- package/src/ui/index.ts +0 -12
- package/src/ui/navigation/routes.ts +93 -2
- package/src/ui/screens/AccountCenterScreen.tsx +15 -13
- package/src/ui/screens/AccountOverviewScreen.tsx +94 -104
- package/src/ui/screens/AccountSettingsScreen.tsx +18 -17
- package/src/ui/screens/AccountSwitcherScreen.tsx +331 -298
- package/src/ui/screens/AccountVerificationScreen.tsx +15 -15
- package/src/ui/screens/AppInfoScreen.tsx +8 -13
- package/src/ui/screens/AvatarCropScreen.tsx +1073 -0
- package/src/ui/screens/CreateManagedAccountScreen.tsx +5 -16
- package/src/ui/screens/EditProfileFieldScreen.tsx +5 -10
- package/src/ui/screens/FAQScreen.tsx +3 -3
- package/src/ui/screens/FeedbackScreen.tsx +14 -12
- package/src/ui/screens/FileManagementScreen.tsx +885 -124
- package/src/ui/screens/HelpSupportScreen.tsx +2 -3
- package/src/ui/screens/HistoryViewScreen.tsx +24 -9
- package/src/ui/screens/LanguageSelectorScreen.tsx +1 -3
- package/src/ui/screens/LegalDocumentsScreen.tsx +3 -3
- package/src/ui/screens/PaymentGatewayScreen.tsx +2 -2
- package/src/ui/screens/PremiumSubscriptionScreen.tsx +51 -56
- package/src/ui/screens/PrivacySettingsScreen.tsx +22 -20
- package/src/ui/screens/ProfileScreen.tsx +1 -2
- package/src/ui/screens/SavesCollectionsScreen.tsx +2 -2
- package/src/ui/screens/SessionManagementScreen.tsx +35 -47
- package/src/ui/screens/UserLinksScreen.tsx +1 -2
- package/src/ui/screens/UserListScreen.tsx +30 -19
- package/src/ui/screens/WelcomeNewUserScreen.tsx +14 -18
- package/src/ui/screens/karma/KarmaAboutScreen.tsx +0 -3
- package/src/ui/screens/karma/KarmaCenterScreen.tsx +4 -8
- package/src/ui/screens/karma/KarmaFAQScreen.tsx +1 -1
- package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +3 -4
- package/src/ui/screens/karma/KarmaRewardsScreen.tsx +13 -18
- package/src/ui/screens/karma/KarmaRulesScreen.tsx +3 -4
- package/src/ui/server.ts +0 -4
- package/src/ui/styles/authStyles.ts +0 -5
- package/src/ui/styles/index.ts +0 -1
- package/src/ui/styles/spacing.ts +0 -43
- package/src/ui/styles/theme.ts +0 -4
- package/src/ui/utils/fileManagement.ts +66 -38
- package/src/ui/utils/sessionHelpers.ts +3 -1
- package/src/ui/utils/userUtils.ts +45 -29
- package/lib/commonjs/lib/sonner-safe.js +0 -32
- package/lib/commonjs/lib/sonner-safe.js.map +0 -1
- package/lib/commonjs/lib/sonner.js +0 -19
- package/lib/commonjs/lib/sonner.js.map +0 -1
- package/lib/commonjs/lib/sonner.native.js +0 -24
- package/lib/commonjs/lib/sonner.native.js.map +0 -1
- package/lib/commonjs/lib/sonner.web.js +0 -24
- package/lib/commonjs/lib/sonner.web.js.map +0 -1
- package/lib/commonjs/ui/hooks/useThemeColors.js +0 -33
- package/lib/commonjs/ui/hooks/useThemeColors.js.map +0 -1
- package/lib/commonjs/ui/hooks/useThemeStyles.js +0 -38
- package/lib/commonjs/ui/hooks/useThemeStyles.js.map +0 -1
- package/lib/commonjs/ui/styles/fonts.js +0 -84
- package/lib/commonjs/ui/styles/fonts.js.map +0 -1
- package/lib/module/lib/sonner-safe.js +0 -29
- package/lib/module/lib/sonner-safe.js.map +0 -1
- package/lib/module/lib/sonner.js +0 -14
- package/lib/module/lib/sonner.js.map +0 -1
- package/lib/module/lib/sonner.native.js +0 -19
- package/lib/module/lib/sonner.native.js.map +0 -1
- package/lib/module/lib/sonner.web.js +0 -19
- package/lib/module/lib/sonner.web.js.map +0 -1
- package/lib/module/ui/hooks/useThemeColors.js +0 -29
- package/lib/module/ui/hooks/useThemeColors.js.map +0 -1
- package/lib/module/ui/hooks/useThemeStyles.js +0 -33
- package/lib/module/ui/hooks/useThemeStyles.js.map +0 -1
- package/lib/module/ui/styles/fonts.js +0 -81
- package/lib/module/ui/styles/fonts.js.map +0 -1
- package/lib/typescript/commonjs/lib/sonner-safe.d.ts +0 -9
- package/lib/typescript/commonjs/lib/sonner-safe.d.ts.map +0 -1
- package/lib/typescript/commonjs/lib/sonner.d.ts +0 -12
- package/lib/typescript/commonjs/lib/sonner.d.ts.map +0 -1
- package/lib/typescript/commonjs/lib/sonner.native.d.ts +0 -15
- package/lib/typescript/commonjs/lib/sonner.native.d.ts.map +0 -1
- package/lib/typescript/commonjs/lib/sonner.web.d.ts +0 -15
- package/lib/typescript/commonjs/lib/sonner.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useThemeColors.d.ts +0 -87
- package/lib/typescript/commonjs/ui/hooks/useThemeColors.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useThemeStyles.d.ts +0 -22
- package/lib/typescript/commonjs/ui/hooks/useThemeStyles.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/styles/fonts.d.ts +0 -21
- package/lib/typescript/commonjs/ui/styles/fonts.d.ts.map +0 -1
- package/lib/typescript/module/lib/sonner-safe.d.ts +0 -9
- package/lib/typescript/module/lib/sonner-safe.d.ts.map +0 -1
- package/lib/typescript/module/lib/sonner.d.ts +0 -12
- package/lib/typescript/module/lib/sonner.d.ts.map +0 -1
- package/lib/typescript/module/lib/sonner.native.d.ts +0 -15
- package/lib/typescript/module/lib/sonner.native.d.ts.map +0 -1
- package/lib/typescript/module/lib/sonner.web.d.ts +0 -15
- package/lib/typescript/module/lib/sonner.web.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useThemeColors.d.ts +0 -87
- package/lib/typescript/module/ui/hooks/useThemeColors.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useThemeStyles.d.ts +0 -22
- package/lib/typescript/module/ui/hooks/useThemeStyles.d.ts.map +0 -1
- package/lib/typescript/module/ui/styles/fonts.d.ts +0 -21
- package/lib/typescript/module/ui/styles/fonts.d.ts.map +0 -1
- package/src/lib/sonner-safe.ts +0 -31
- package/src/lib/sonner.native.ts +0 -28
- package/src/lib/sonner.ts +0 -11
- package/src/lib/sonner.web.ts +0 -28
- package/src/ui/hooks/useThemeColors.ts +0 -27
- package/src/ui/hooks/useThemeStyles.ts +0 -50
- package/src/ui/styles/fonts.ts +0 -77
|
@@ -11,8 +11,20 @@ import {
|
|
|
11
11
|
Image,
|
|
12
12
|
Animated,
|
|
13
13
|
Easing,
|
|
14
|
+
FlatList,
|
|
15
|
+
AccessibilityInfo,
|
|
16
|
+
Platform,
|
|
17
|
+
useWindowDimensions,
|
|
14
18
|
} from 'react-native';
|
|
15
19
|
import { Image as ExpoImage } from 'expo-image';
|
|
20
|
+
import Reanimated, {
|
|
21
|
+
FadeIn,
|
|
22
|
+
useAnimatedStyle,
|
|
23
|
+
useSharedValue,
|
|
24
|
+
withSequence,
|
|
25
|
+
withSpring,
|
|
26
|
+
withTiming,
|
|
27
|
+
} from 'react-native-reanimated';
|
|
16
28
|
import type { FileManagementScreenProps } from '../types/fileManagement';
|
|
17
29
|
|
|
18
30
|
// Lazy load expo-document-picker (optional dependency)
|
|
@@ -27,13 +39,10 @@ const loadDocumentPicker = async () => {
|
|
|
27
39
|
throw new Error('expo-document-picker is not installed. Please install it: npx expo install expo-document-picker');
|
|
28
40
|
}
|
|
29
41
|
};
|
|
30
|
-
import { toast } from '
|
|
31
|
-
import
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
// @ts-ignore - MaterialCommunityIcons is available at runtime
|
|
35
|
-
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
36
|
-
import type { FileMetadata } from '@oxyhq/core';
|
|
42
|
+
import { Dialog, toast, useDialogControl } from '@oxyhq/bloom';
|
|
43
|
+
import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';
|
|
44
|
+
import type { AssetUploadInput, FileMetadata, RNFileDescriptor } from '@oxyhq/core';
|
|
45
|
+
import { getErrorMessage as getOxyErrorMessage } from '@oxyhq/core';
|
|
37
46
|
import { useFileStore, useFiles, useUploading as useUploadingStore, useUploadAggregateProgress, useDeleting as useDeletingStore } from '../stores/fileStore';
|
|
38
47
|
import Header from '../components/Header';
|
|
39
48
|
import JustifiedPhotoGrid from '../components/photogrid/JustifiedPhotoGrid';
|
|
@@ -51,13 +60,123 @@ import {
|
|
|
51
60
|
import { FileViewer } from '../components/fileManagement/FileViewer';
|
|
52
61
|
import { FileDetailsModal } from '../components/fileManagement/FileDetailsModal';
|
|
53
62
|
import { UploadPreview } from '../components/fileManagement/UploadPreview';
|
|
54
|
-
import {
|
|
55
|
-
import { fileManagementStyles } from '../components/fileManagement/styles';
|
|
63
|
+
import { fileManagementStyles, photoPickerStyles } from '../components/fileManagement/styles';
|
|
56
64
|
import type { OnConfirmFileSelection } from '../types/fileManagement';
|
|
57
65
|
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Extract error message from unknown error type.
|
|
68
|
+
* Delegates to the canonical `getErrorMessage` in `@oxyhq/core` and returns
|
|
69
|
+
* `undefined` for empty results (so callers can fall back to a translated
|
|
70
|
+
* message via `||`).
|
|
71
|
+
*/
|
|
72
|
+
const getErrorMessage = (error: unknown): string | undefined => {
|
|
73
|
+
if (error == null) return undefined;
|
|
74
|
+
const message = getOxyErrorMessage(error, '');
|
|
75
|
+
return message ? message : undefined;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A picker-produced file ready to upload. On web this is a real `File`
|
|
80
|
+
* (carrying an optional `uri` for preview). On native, it's an
|
|
81
|
+
* {@link RNFileDescriptor} — passed straight to FormData by `assetUpload`.
|
|
82
|
+
*/
|
|
83
|
+
type UploadCandidate = (File & { uri?: string }) | RNFileDescriptor;
|
|
84
|
+
|
|
85
|
+
/** Returns the display name for either a web File or an RN descriptor. */
|
|
86
|
+
const candidateName = (candidate: UploadCandidate, fallback: string): string =>
|
|
87
|
+
(candidate.name && typeof candidate.name === 'string' ? candidate.name : fallback);
|
|
88
|
+
|
|
89
|
+
/** Returns the byte size for either a web File or an RN descriptor (0 if unknown). */
|
|
90
|
+
const candidateSize = (candidate: UploadCandidate): number => {
|
|
91
|
+
const size = (candidate as { size?: number }).size;
|
|
92
|
+
return typeof size === 'number' && Number.isFinite(size) ? size : 0;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/** Returns the mime type for either a web File or an RN descriptor. */
|
|
96
|
+
const candidateType = (candidate: UploadCandidate): string => {
|
|
97
|
+
const value = (candidate as { type?: string }).type;
|
|
98
|
+
return typeof value === 'string' && value.length > 0 ? value : 'application/octet-stream';
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** Returns the preview URI for an upload candidate, if available. */
|
|
102
|
+
const candidateUri = (candidate: UploadCandidate): string | undefined => {
|
|
103
|
+
const uri = (candidate as { uri?: string }).uri;
|
|
104
|
+
return typeof uri === 'string' && uri.length > 0 ? uri : undefined;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Haptic feedback wrapper. `expo-haptics` is an optional dependency — when not
|
|
109
|
+
* installed (or on web), all calls degrade silently. We resolve the module
|
|
110
|
+
* once and cache the promise so subsequent calls don't repeat the dynamic
|
|
111
|
+
* import. Matches the pattern used by AvatarCropScreen.
|
|
112
|
+
*/
|
|
113
|
+
type HapticImpact = 'light' | 'medium' | 'heavy';
|
|
114
|
+
type HapticNotification = 'success' | 'warning' | 'error';
|
|
115
|
+
interface HapticsModule {
|
|
116
|
+
impactAsync: (style: unknown) => Promise<void>;
|
|
117
|
+
notificationAsync: (type: unknown) => Promise<void>;
|
|
118
|
+
selectionAsync: () => Promise<void>;
|
|
119
|
+
ImpactFeedbackStyle: { Light: unknown; Medium: unknown; Heavy: unknown };
|
|
120
|
+
NotificationFeedbackType: { Success: unknown; Warning: unknown; Error: unknown };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let hapticsModulePromise: Promise<HapticsModule | null> | null = null;
|
|
124
|
+
const getHaptics = (): Promise<HapticsModule | null> => {
|
|
125
|
+
if (Platform.OS === 'web') return Promise.resolve(null);
|
|
126
|
+
if (hapticsModulePromise) return hapticsModulePromise;
|
|
127
|
+
hapticsModulePromise = (async () => {
|
|
128
|
+
try {
|
|
129
|
+
const mod = (await import('expo-haptics')) as unknown as HapticsModule;
|
|
130
|
+
if (!mod || typeof mod.impactAsync !== 'function') return null;
|
|
131
|
+
return mod;
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
})();
|
|
136
|
+
return hapticsModulePromise;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const hapticImpact = async (style: HapticImpact): Promise<void> => {
|
|
140
|
+
const h = await getHaptics();
|
|
141
|
+
if (!h) return;
|
|
142
|
+
const styleEnum =
|
|
143
|
+
style === 'heavy'
|
|
144
|
+
? h.ImpactFeedbackStyle.Heavy
|
|
145
|
+
: style === 'medium'
|
|
146
|
+
? h.ImpactFeedbackStyle.Medium
|
|
147
|
+
: h.ImpactFeedbackStyle.Light;
|
|
148
|
+
try {
|
|
149
|
+
await h.impactAsync(styleEnum);
|
|
150
|
+
} catch {
|
|
151
|
+
// Silent — haptics are non-critical UX polish.
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const hapticSelection = async (): Promise<void> => {
|
|
156
|
+
const h = await getHaptics();
|
|
157
|
+
if (!h) return;
|
|
158
|
+
try {
|
|
159
|
+
await h.selectionAsync();
|
|
160
|
+
} catch {
|
|
161
|
+
// Silent.
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const hapticNotification = async (type: HapticNotification): Promise<void> => {
|
|
166
|
+
const h = await getHaptics();
|
|
167
|
+
if (!h) return;
|
|
168
|
+
const typeEnum =
|
|
169
|
+
type === 'error'
|
|
170
|
+
? h.NotificationFeedbackType.Error
|
|
171
|
+
: type === 'warning'
|
|
172
|
+
? h.NotificationFeedbackType.Warning
|
|
173
|
+
: h.NotificationFeedbackType.Success;
|
|
174
|
+
try {
|
|
175
|
+
await h.notificationAsync(typeEnum);
|
|
176
|
+
} catch {
|
|
177
|
+
// Silent.
|
|
178
|
+
}
|
|
179
|
+
};
|
|
61
180
|
|
|
62
181
|
// Animated button component for smooth transitions
|
|
63
182
|
const AnimatedButton: React.FC<{
|
|
@@ -67,7 +186,8 @@ const AnimatedButton: React.FC<{
|
|
|
67
186
|
primaryColor: string;
|
|
68
187
|
textColor: string;
|
|
69
188
|
style: Record<string, unknown>;
|
|
70
|
-
|
|
189
|
+
accessibilityLabel: string;
|
|
190
|
+
}> = ({ isSelected, onPress, icon, primaryColor, textColor, style, accessibilityLabel }) => {
|
|
71
191
|
const animatedValue = useRef(new Animated.Value(isSelected ? 1 : 0)).current;
|
|
72
192
|
|
|
73
193
|
useEffect(() => {
|
|
@@ -84,13 +204,14 @@ const AnimatedButton: React.FC<{
|
|
|
84
204
|
outputRange: ['transparent', primaryColor],
|
|
85
205
|
});
|
|
86
206
|
|
|
87
|
-
const iconColor = animatedValue.interpolate({
|
|
88
|
-
inputRange: [0, 1],
|
|
89
|
-
outputRange: [textColor, '#FFFFFF'],
|
|
90
|
-
});
|
|
91
|
-
|
|
92
207
|
return (
|
|
93
|
-
<TouchableOpacity
|
|
208
|
+
<TouchableOpacity
|
|
209
|
+
onPress={onPress}
|
|
210
|
+
activeOpacity={0.7}
|
|
211
|
+
accessibilityRole="button"
|
|
212
|
+
accessibilityLabel={accessibilityLabel}
|
|
213
|
+
accessibilityState={{ selected: isSelected }}
|
|
214
|
+
>
|
|
94
215
|
<Animated.View
|
|
95
216
|
style={[
|
|
96
217
|
style,
|
|
@@ -111,6 +232,518 @@ const AnimatedButton: React.FC<{
|
|
|
111
232
|
);
|
|
112
233
|
};
|
|
113
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Props for the dedicated photo picker view. Used by FileManagementScreen
|
|
237
|
+
* only when `selectMode + image-only` is active. All callbacks are wired by
|
|
238
|
+
* the parent — this component is purely presentational.
|
|
239
|
+
*/
|
|
240
|
+
interface PhotoPickerViewProps {
|
|
241
|
+
photos: FileMetadata[];
|
|
242
|
+
selectedIds: Set<string>;
|
|
243
|
+
multiSelect: boolean;
|
|
244
|
+
maxSelection?: number;
|
|
245
|
+
allowUpload: boolean;
|
|
246
|
+
refreshing: boolean;
|
|
247
|
+
uploading: boolean;
|
|
248
|
+
isPickingDocument: boolean;
|
|
249
|
+
uploadProgress: { current: number; total: number } | null;
|
|
250
|
+
hasMore: boolean;
|
|
251
|
+
loadingMore: boolean;
|
|
252
|
+
reduceMotion: boolean;
|
|
253
|
+
getThumbUrl: (file: FileMetadata, variant?: string) => string;
|
|
254
|
+
primaryColor: string;
|
|
255
|
+
isOwner: boolean;
|
|
256
|
+
onTogglePhoto: (photo: FileMetadata) => void;
|
|
257
|
+
onPreviewPhoto: (photo: FileMetadata) => void;
|
|
258
|
+
onUpload: () => void;
|
|
259
|
+
onRefresh: () => void;
|
|
260
|
+
onLoadMore: () => void;
|
|
261
|
+
onCancel: () => void;
|
|
262
|
+
onConfirm: () => void;
|
|
263
|
+
t: (key: string, vars?: Record<string, string | number>) => string;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* A single photo cell. Memoized so re-renders during selection only touch
|
|
268
|
+
* affected cells — selection of one photo must not redraw the whole grid.
|
|
269
|
+
*
|
|
270
|
+
* Apple Photos pattern: when any cell is selected, *non-selected* siblings
|
|
271
|
+
* fade to 0.6 opacity to focus attention on the active selection.
|
|
272
|
+
*/
|
|
273
|
+
const PhotoPickerCell = React.memo(function PhotoPickerCell(props: {
|
|
274
|
+
photo: FileMetadata;
|
|
275
|
+
size: number;
|
|
276
|
+
marginRight: number;
|
|
277
|
+
marginBottom: number;
|
|
278
|
+
isSelected: boolean;
|
|
279
|
+
selectionIndex: number; // 1-based for badge; 0 if not selected
|
|
280
|
+
dim: boolean; // any selection exists and this cell is not selected
|
|
281
|
+
primaryColor: string;
|
|
282
|
+
thumbUrl: string;
|
|
283
|
+
enterIndex: number;
|
|
284
|
+
reduceMotion: boolean;
|
|
285
|
+
onPress: () => void;
|
|
286
|
+
onLongPress: () => void;
|
|
287
|
+
a11yLabel: string;
|
|
288
|
+
}) {
|
|
289
|
+
const {
|
|
290
|
+
photo, size, marginRight, marginBottom, isSelected, selectionIndex,
|
|
291
|
+
dim, primaryColor, thumbUrl, enterIndex, reduceMotion, onPress,
|
|
292
|
+
onLongPress, a11yLabel,
|
|
293
|
+
} = props;
|
|
294
|
+
|
|
295
|
+
// Cap the cumulative stagger at ~800ms total so the very long grid does
|
|
296
|
+
// not keep fading in late tiles. Beyond ~53 tiles the delay maxes out.
|
|
297
|
+
const STAGGER_PER_CELL_MS = 15;
|
|
298
|
+
const MAX_TOTAL_STAGGER_MS = 800;
|
|
299
|
+
const delay = Math.min(enterIndex * STAGGER_PER_CELL_MS, MAX_TOTAL_STAGGER_MS);
|
|
300
|
+
|
|
301
|
+
// Selection ring pulse animation: 1.0 → 1.05 → 1.0 on transition to
|
|
302
|
+
// selected. Plays at most once per selection change; reduce-motion skips.
|
|
303
|
+
const ringScale = useSharedValue(1);
|
|
304
|
+
const prevSelected = useRef(isSelected);
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
if (prevSelected.current === isSelected) return;
|
|
307
|
+
prevSelected.current = isSelected;
|
|
308
|
+
if (!isSelected) {
|
|
309
|
+
ringScale.value = 1;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (reduceMotion) {
|
|
313
|
+
ringScale.value = 1;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
ringScale.value = withSequence(
|
|
317
|
+
withTiming(1.05, { duration: 110 }),
|
|
318
|
+
withSpring(1, { damping: 14, stiffness: 200 }),
|
|
319
|
+
);
|
|
320
|
+
}, [isSelected, reduceMotion, ringScale]);
|
|
321
|
+
|
|
322
|
+
const ringAnimatedStyle = useAnimatedStyle(() => ({
|
|
323
|
+
transform: [{ scale: ringScale.value }],
|
|
324
|
+
}));
|
|
325
|
+
|
|
326
|
+
const inner = (
|
|
327
|
+
<>
|
|
328
|
+
<View style={[photoPickerStyles.cellInner, dim && photoPickerStyles.cellDim]}>
|
|
329
|
+
<ExpoImage
|
|
330
|
+
source={{ uri: thumbUrl }}
|
|
331
|
+
style={photoPickerStyles.cellImage}
|
|
332
|
+
contentFit="cover"
|
|
333
|
+
transition={120}
|
|
334
|
+
cachePolicy="memory-disk"
|
|
335
|
+
accessibilityLabel={photo.filename}
|
|
336
|
+
/>
|
|
337
|
+
</View>
|
|
338
|
+
{isSelected && (
|
|
339
|
+
<Reanimated.View
|
|
340
|
+
pointerEvents="none"
|
|
341
|
+
style={[
|
|
342
|
+
photoPickerStyles.cellRing,
|
|
343
|
+
{ borderColor: primaryColor },
|
|
344
|
+
ringAnimatedStyle,
|
|
345
|
+
]}
|
|
346
|
+
/>
|
|
347
|
+
)}
|
|
348
|
+
{isSelected && (
|
|
349
|
+
<View
|
|
350
|
+
pointerEvents="none"
|
|
351
|
+
style={[photoPickerStyles.cellBadge, { backgroundColor: primaryColor }]}
|
|
352
|
+
>
|
|
353
|
+
<Text style={photoPickerStyles.cellBadgeText}>
|
|
354
|
+
{selectionIndex > 0 ? String(selectionIndex) : ''}
|
|
355
|
+
</Text>
|
|
356
|
+
</View>
|
|
357
|
+
)}
|
|
358
|
+
</>
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const cellWrapperStyle = {
|
|
362
|
+
width: size,
|
|
363
|
+
height: size,
|
|
364
|
+
marginRight,
|
|
365
|
+
marginBottom,
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
if (reduceMotion) {
|
|
369
|
+
return (
|
|
370
|
+
<TouchableOpacity
|
|
371
|
+
activeOpacity={0.85}
|
|
372
|
+
onPress={onPress}
|
|
373
|
+
onLongPress={onLongPress}
|
|
374
|
+
style={[photoPickerStyles.cellWrapper, cellWrapperStyle]}
|
|
375
|
+
accessibilityRole="button"
|
|
376
|
+
accessibilityLabel={a11yLabel}
|
|
377
|
+
accessibilityState={{ selected: isSelected }}
|
|
378
|
+
>
|
|
379
|
+
{inner}
|
|
380
|
+
</TouchableOpacity>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<Reanimated.View
|
|
386
|
+
entering={FadeIn.delay(delay).duration(200)}
|
|
387
|
+
style={[photoPickerStyles.cellWrapper, cellWrapperStyle]}
|
|
388
|
+
>
|
|
389
|
+
<TouchableOpacity
|
|
390
|
+
activeOpacity={0.85}
|
|
391
|
+
onPress={onPress}
|
|
392
|
+
onLongPress={onLongPress}
|
|
393
|
+
style={{ flex: 1 }}
|
|
394
|
+
accessibilityRole="button"
|
|
395
|
+
accessibilityLabel={a11yLabel}
|
|
396
|
+
accessibilityState={{ selected: isSelected }}
|
|
397
|
+
>
|
|
398
|
+
{inner}
|
|
399
|
+
</TouchableOpacity>
|
|
400
|
+
</Reanimated.View>
|
|
401
|
+
);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
405
|
+
photos,
|
|
406
|
+
selectedIds,
|
|
407
|
+
multiSelect,
|
|
408
|
+
maxSelection,
|
|
409
|
+
allowUpload,
|
|
410
|
+
refreshing,
|
|
411
|
+
uploading,
|
|
412
|
+
isPickingDocument,
|
|
413
|
+
uploadProgress,
|
|
414
|
+
hasMore,
|
|
415
|
+
loadingMore,
|
|
416
|
+
reduceMotion,
|
|
417
|
+
getThumbUrl,
|
|
418
|
+
primaryColor,
|
|
419
|
+
isOwner,
|
|
420
|
+
onTogglePhoto,
|
|
421
|
+
onPreviewPhoto,
|
|
422
|
+
onUpload,
|
|
423
|
+
onRefresh,
|
|
424
|
+
onLoadMore,
|
|
425
|
+
onCancel,
|
|
426
|
+
onConfirm,
|
|
427
|
+
t,
|
|
428
|
+
}) => {
|
|
429
|
+
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
430
|
+
|
|
431
|
+
// Layout: 3 columns phone portrait, 2 columns phone landscape,
|
|
432
|
+
// 4 columns tablet (>= 600 width). Apple Photos-ish.
|
|
433
|
+
const columns = useMemo(() => {
|
|
434
|
+
if (windowWidth >= 600) return 4;
|
|
435
|
+
// Landscape phone: width > height
|
|
436
|
+
if (windowWidth > windowHeight) return 2;
|
|
437
|
+
return 3;
|
|
438
|
+
}, [windowWidth, windowHeight]);
|
|
439
|
+
|
|
440
|
+
const GUTTER = 2;
|
|
441
|
+
const cellSize = useMemo(() => {
|
|
442
|
+
// (windowWidth - (columns - 1) * GUTTER) / columns, but
|
|
443
|
+
// FlatList in 3-col layout means each row has 2 inter-cell gutters.
|
|
444
|
+
return Math.floor((windowWidth - GUTTER * (columns - 1)) / columns);
|
|
445
|
+
}, [windowWidth, columns]);
|
|
446
|
+
|
|
447
|
+
// Map selectedIds → 1-based selection order for the badge. We freeze a
|
|
448
|
+
// stable order at the time of selection: the order is the insertion
|
|
449
|
+
// order of the Set (which JS preserves natively).
|
|
450
|
+
const selectionOrder = useMemo(() => {
|
|
451
|
+
const map = new Map<string, number>();
|
|
452
|
+
let i = 1;
|
|
453
|
+
for (const id of selectedIds) {
|
|
454
|
+
map.set(id, i++);
|
|
455
|
+
}
|
|
456
|
+
return map;
|
|
457
|
+
}, [selectedIds]);
|
|
458
|
+
|
|
459
|
+
const hasAnySelection = selectedIds.size > 0;
|
|
460
|
+
|
|
461
|
+
// Compact icon-only upload pill on narrow screens; full pill otherwise.
|
|
462
|
+
const showUploadLabel = windowWidth >= 360;
|
|
463
|
+
|
|
464
|
+
// The bottom sheet renders below the status bar already (its `maxHeight`
|
|
465
|
+
// is capped by `SCREEN_HEIGHT - insets.top`), so the picker MUST NOT add
|
|
466
|
+
// an additional safe-area inset to the header. Header layout:
|
|
467
|
+
// • 28dp drag-handle hit area floats at the very top of the sheet
|
|
468
|
+
// • 56dp app bar sits immediately below the handle
|
|
469
|
+
// Total header zone = 28 + 56 = 84dp from sheet top.
|
|
470
|
+
const HANDLE_ZONE = 28;
|
|
471
|
+
const APP_BAR_HEIGHT = 56;
|
|
472
|
+
const headerHeight = HANDLE_ZONE + APP_BAR_HEIGHT;
|
|
473
|
+
const contentPaddingTop = headerHeight + 4;
|
|
474
|
+
|
|
475
|
+
const isEmpty = photos.length === 0;
|
|
476
|
+
const a11yColumnsAnnouncement = useRef<number>(columns);
|
|
477
|
+
|
|
478
|
+
useEffect(() => {
|
|
479
|
+
if (a11yColumnsAnnouncement.current !== columns) {
|
|
480
|
+
a11yColumnsAnnouncement.current = columns;
|
|
481
|
+
}
|
|
482
|
+
}, [columns]);
|
|
483
|
+
|
|
484
|
+
const handleCellPress = useCallback(
|
|
485
|
+
(photo: FileMetadata) => {
|
|
486
|
+
if (Platform.OS !== 'web') {
|
|
487
|
+
void hapticImpact('light');
|
|
488
|
+
}
|
|
489
|
+
onTogglePhoto(photo);
|
|
490
|
+
},
|
|
491
|
+
[onTogglePhoto],
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const handleCellLongPress = useCallback(
|
|
495
|
+
(photo: FileMetadata) => {
|
|
496
|
+
if (Platform.OS !== 'web') {
|
|
497
|
+
void hapticSelection();
|
|
498
|
+
}
|
|
499
|
+
onPreviewPhoto(photo);
|
|
500
|
+
},
|
|
501
|
+
[onPreviewPhoto],
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
const handleConfirm = useCallback(() => {
|
|
505
|
+
if (multiSelect) {
|
|
506
|
+
if (selectedIds.size === 0) return;
|
|
507
|
+
if (Platform.OS !== 'web') {
|
|
508
|
+
void hapticNotification('success');
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
onConfirm();
|
|
512
|
+
}, [multiSelect, selectedIds.size, onConfirm]);
|
|
513
|
+
|
|
514
|
+
// FlatList renderItem: each cell knows its enterIndex (for stagger).
|
|
515
|
+
const renderItem = useCallback(
|
|
516
|
+
({ item, index }: { item: FileMetadata; index: number }) => {
|
|
517
|
+
const isSelected = selectedIds.has(item.id);
|
|
518
|
+
const selIndex = isSelected ? (selectionOrder.get(item.id) || 0) : 0;
|
|
519
|
+
// Last column gets no right margin; last row no bottom margin
|
|
520
|
+
// (FlatList handles row breaks via numColumns).
|
|
521
|
+
const isLastInRow = (index + 1) % columns === 0;
|
|
522
|
+
const a11yLabel = t(
|
|
523
|
+
isSelected
|
|
524
|
+
? 'fileManagement.a11y.photoCellSelected'
|
|
525
|
+
: 'fileManagement.a11y.photoCellUnselected',
|
|
526
|
+
{ name: item.filename || 'photo' },
|
|
527
|
+
);
|
|
528
|
+
return (
|
|
529
|
+
<PhotoPickerCell
|
|
530
|
+
photo={item}
|
|
531
|
+
size={cellSize}
|
|
532
|
+
marginRight={isLastInRow ? 0 : GUTTER}
|
|
533
|
+
marginBottom={GUTTER}
|
|
534
|
+
isSelected={isSelected}
|
|
535
|
+
selectionIndex={selIndex}
|
|
536
|
+
dim={multiSelect && hasAnySelection && !isSelected}
|
|
537
|
+
primaryColor={primaryColor}
|
|
538
|
+
thumbUrl={getThumbUrl(item, 'thumb')}
|
|
539
|
+
enterIndex={index}
|
|
540
|
+
reduceMotion={reduceMotion}
|
|
541
|
+
onPress={() => handleCellPress(item)}
|
|
542
|
+
onLongPress={() => handleCellLongPress(item)}
|
|
543
|
+
a11yLabel={a11yLabel}
|
|
544
|
+
/>
|
|
545
|
+
);
|
|
546
|
+
},
|
|
547
|
+
[
|
|
548
|
+
selectedIds, selectionOrder, columns, cellSize, multiSelect, hasAnySelection,
|
|
549
|
+
primaryColor, getThumbUrl, reduceMotion, handleCellPress, handleCellLongPress, t,
|
|
550
|
+
],
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
const keyExtractor = useCallback((item: FileMetadata) => item.id, []);
|
|
554
|
+
|
|
555
|
+
const listFooter = useMemo(() => {
|
|
556
|
+
if (!loadingMore) return null;
|
|
557
|
+
return (
|
|
558
|
+
<View style={{ paddingVertical: 16, alignItems: 'center' }}>
|
|
559
|
+
<ActivityIndicator size="small" color="#FFFFFF" />
|
|
560
|
+
</View>
|
|
561
|
+
);
|
|
562
|
+
}, [loadingMore]);
|
|
563
|
+
|
|
564
|
+
const handleEndReached = useCallback(() => {
|
|
565
|
+
if (loadingMore || !hasMore) return;
|
|
566
|
+
onLoadMore();
|
|
567
|
+
}, [loadingMore, hasMore, onLoadMore]);
|
|
568
|
+
|
|
569
|
+
const confirmDisabled = multiSelect && selectedIds.size === 0;
|
|
570
|
+
const confirmLabel = multiSelect
|
|
571
|
+
? t('fileManagement.doneWithCount', { count: selectedIds.size })
|
|
572
|
+
: t('fileManagement.done');
|
|
573
|
+
|
|
574
|
+
// The progress fill width. Guard against zero division.
|
|
575
|
+
const progressFraction = uploadProgress && uploadProgress.total > 0
|
|
576
|
+
? Math.min(1, Math.max(0, uploadProgress.current / uploadProgress.total))
|
|
577
|
+
: 0;
|
|
578
|
+
|
|
579
|
+
return (
|
|
580
|
+
<View style={photoPickerStyles.root}>
|
|
581
|
+
{/* Photo grid (renders behind translucent header) */}
|
|
582
|
+
{isEmpty ? (
|
|
583
|
+
<View style={[photoPickerStyles.empty, { paddingTop: contentPaddingTop }]}>
|
|
584
|
+
<View style={photoPickerStyles.emptyIconWrap}>
|
|
585
|
+
<MaterialCommunityIcons name="image-outline" size={64} color="#FFFFFF" />
|
|
586
|
+
</View>
|
|
587
|
+
<Text style={[photoPickerStyles.emptyTitle, { color: '#FFFFFF' }]}>
|
|
588
|
+
{t('fileManagement.photoPicker.emptyTitle')}
|
|
589
|
+
</Text>
|
|
590
|
+
<Text style={[photoPickerStyles.emptySubtitle, { color: '#FFFFFF' }]}>
|
|
591
|
+
{t('fileManagement.photoPicker.emptySubtitle')}
|
|
592
|
+
</Text>
|
|
593
|
+
{isOwner && allowUpload && (
|
|
594
|
+
<TouchableOpacity
|
|
595
|
+
style={[photoPickerStyles.emptyCta, { backgroundColor: primaryColor }]}
|
|
596
|
+
onPress={onUpload}
|
|
597
|
+
disabled={uploading || isPickingDocument}
|
|
598
|
+
accessibilityRole="button"
|
|
599
|
+
accessibilityLabel={t('fileManagement.uploadPhoto')}
|
|
600
|
+
accessibilityState={{ busy: uploading || isPickingDocument }}
|
|
601
|
+
>
|
|
602
|
+
{(uploading || isPickingDocument) ? (
|
|
603
|
+
<ActivityIndicator size="small" color="#FFFFFF" />
|
|
604
|
+
) : (
|
|
605
|
+
<>
|
|
606
|
+
<Ionicons name="cloud-upload" size={18} color="#FFFFFF" />
|
|
607
|
+
<Text style={photoPickerStyles.emptyCtaText}>
|
|
608
|
+
{t('fileManagement.uploadPhoto')}
|
|
609
|
+
</Text>
|
|
610
|
+
</>
|
|
611
|
+
)}
|
|
612
|
+
</TouchableOpacity>
|
|
613
|
+
)}
|
|
614
|
+
</View>
|
|
615
|
+
) : (
|
|
616
|
+
<FlatList
|
|
617
|
+
key={`cols-${columns}`}
|
|
618
|
+
data={photos}
|
|
619
|
+
renderItem={renderItem}
|
|
620
|
+
keyExtractor={keyExtractor}
|
|
621
|
+
numColumns={columns}
|
|
622
|
+
contentContainerStyle={[
|
|
623
|
+
photoPickerStyles.gridContent,
|
|
624
|
+
{ paddingTop: contentPaddingTop },
|
|
625
|
+
]}
|
|
626
|
+
style={photoPickerStyles.grid}
|
|
627
|
+
showsVerticalScrollIndicator={false}
|
|
628
|
+
refreshControl={
|
|
629
|
+
<RefreshControl
|
|
630
|
+
refreshing={refreshing}
|
|
631
|
+
onRefresh={onRefresh}
|
|
632
|
+
tintColor="#FFFFFF"
|
|
633
|
+
colors={[primaryColor]}
|
|
634
|
+
progressViewOffset={contentPaddingTop}
|
|
635
|
+
/>
|
|
636
|
+
}
|
|
637
|
+
onEndReached={handleEndReached}
|
|
638
|
+
onEndReachedThreshold={0.4}
|
|
639
|
+
ListFooterComponent={listFooter}
|
|
640
|
+
removeClippedSubviews
|
|
641
|
+
initialNumToRender={Math.max(12, columns * 6)}
|
|
642
|
+
windowSize={9}
|
|
643
|
+
/>
|
|
644
|
+
)}
|
|
645
|
+
|
|
646
|
+
{/* Translucent black header. The bottom sheet already sits below
|
|
647
|
+
the status bar, so we do NOT add `insets.top` here — that would
|
|
648
|
+
double-pad. `paddingTop: HANDLE_ZONE` clears the 28dp drag
|
|
649
|
+
handle floating at the top of the sheet. */}
|
|
650
|
+
<View
|
|
651
|
+
style={[
|
|
652
|
+
photoPickerStyles.header,
|
|
653
|
+
{ paddingTop: HANDLE_ZONE, minHeight: headerHeight },
|
|
654
|
+
]}
|
|
655
|
+
>
|
|
656
|
+
<View style={photoPickerStyles.headerRow}>
|
|
657
|
+
<View style={[photoPickerStyles.headerSide, photoPickerStyles.headerSideLeft]}>
|
|
658
|
+
<TouchableOpacity
|
|
659
|
+
onPress={onCancel}
|
|
660
|
+
style={photoPickerStyles.headerCancel}
|
|
661
|
+
accessibilityRole="button"
|
|
662
|
+
accessibilityLabel={t('fileManagement.a11y.cancelPicker')}
|
|
663
|
+
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
664
|
+
>
|
|
665
|
+
<Text style={photoPickerStyles.headerCancelText}>
|
|
666
|
+
{t('fileManagement.cancel')}
|
|
667
|
+
</Text>
|
|
668
|
+
</TouchableOpacity>
|
|
669
|
+
</View>
|
|
670
|
+
<View pointerEvents="none">
|
|
671
|
+
<Text style={photoPickerStyles.headerTitle} numberOfLines={1}>
|
|
672
|
+
{t('fileManagement.choosePhoto')}
|
|
673
|
+
</Text>
|
|
674
|
+
</View>
|
|
675
|
+
<View style={[photoPickerStyles.headerSide, photoPickerStyles.headerSideRight]}>
|
|
676
|
+
{multiSelect ? (
|
|
677
|
+
<TouchableOpacity
|
|
678
|
+
onPress={handleConfirm}
|
|
679
|
+
disabled={confirmDisabled}
|
|
680
|
+
style={[
|
|
681
|
+
photoPickerStyles.headerPrimaryPill,
|
|
682
|
+
{
|
|
683
|
+
backgroundColor: confirmDisabled
|
|
684
|
+
? 'rgba(255,255,255,0.18)'
|
|
685
|
+
: primaryColor,
|
|
686
|
+
opacity: confirmDisabled ? 0.6 : 1,
|
|
687
|
+
},
|
|
688
|
+
]}
|
|
689
|
+
accessibilityRole="button"
|
|
690
|
+
accessibilityLabel={t('fileManagement.a11y.confirmSelection')}
|
|
691
|
+
accessibilityState={{ disabled: confirmDisabled }}
|
|
692
|
+
>
|
|
693
|
+
<Text style={photoPickerStyles.headerPrimaryText}>
|
|
694
|
+
{confirmLabel}
|
|
695
|
+
</Text>
|
|
696
|
+
</TouchableOpacity>
|
|
697
|
+
) : (
|
|
698
|
+
isOwner && allowUpload && (
|
|
699
|
+
<TouchableOpacity
|
|
700
|
+
onPress={onUpload}
|
|
701
|
+
disabled={uploading || isPickingDocument}
|
|
702
|
+
style={[
|
|
703
|
+
photoPickerStyles.headerPrimaryPill,
|
|
704
|
+
!showUploadLabel && photoPickerStyles.headerPrimaryPillIconOnly,
|
|
705
|
+
{ backgroundColor: primaryColor },
|
|
706
|
+
]}
|
|
707
|
+
accessibilityRole="button"
|
|
708
|
+
accessibilityLabel={t('fileManagement.a11y.uploadFromDevice')}
|
|
709
|
+
accessibilityState={{ busy: uploading || isPickingDocument }}
|
|
710
|
+
>
|
|
711
|
+
{(uploading || isPickingDocument) ? (
|
|
712
|
+
<ActivityIndicator size="small" color="#FFFFFF" />
|
|
713
|
+
) : (
|
|
714
|
+
<>
|
|
715
|
+
<Ionicons name="cloud-upload" size={16} color="#FFFFFF" />
|
|
716
|
+
{showUploadLabel && (
|
|
717
|
+
<Text style={photoPickerStyles.headerPrimaryText}>
|
|
718
|
+
{t('fileManagement.upload')}
|
|
719
|
+
</Text>
|
|
720
|
+
)}
|
|
721
|
+
</>
|
|
722
|
+
)}
|
|
723
|
+
</TouchableOpacity>
|
|
724
|
+
)
|
|
725
|
+
)}
|
|
726
|
+
</View>
|
|
727
|
+
</View>
|
|
728
|
+
{/* Subtle top progress bar during upload (non-blocking). */}
|
|
729
|
+
{uploading && (
|
|
730
|
+
<View style={photoPickerStyles.headerProgressBarTrack}>
|
|
731
|
+
<View
|
|
732
|
+
style={[
|
|
733
|
+
photoPickerStyles.headerProgressBarFill,
|
|
734
|
+
{
|
|
735
|
+
width: `${Math.round(progressFraction * 100)}%`,
|
|
736
|
+
backgroundColor: primaryColor,
|
|
737
|
+
},
|
|
738
|
+
]}
|
|
739
|
+
/>
|
|
740
|
+
</View>
|
|
741
|
+
)}
|
|
742
|
+
</View>
|
|
743
|
+
</View>
|
|
744
|
+
);
|
|
745
|
+
};
|
|
746
|
+
|
|
114
747
|
const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
115
748
|
onClose,
|
|
116
749
|
theme,
|
|
@@ -135,9 +768,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
135
768
|
const { t } = useI18n();
|
|
136
769
|
const uploadFileMutation = useUploadFile();
|
|
137
770
|
// Prompt controls
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
771
|
+
const fileDeleteDialog = useDialogControl();
|
|
772
|
+
const bulkDeleteDialog = useDialogControl();
|
|
773
|
+
const visibilityChangeDialog = useDialogControl();
|
|
141
774
|
const [pendingDeleteFile, setPendingDeleteFile] = useState<{ id: string; name: string } | null>(null);
|
|
142
775
|
const files = useFiles();
|
|
143
776
|
// Ensure containerWidth is a number (TypeScript guard)
|
|
@@ -156,11 +789,50 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
156
789
|
const [loadingFileContent, setLoadingFileContent] = useState(false);
|
|
157
790
|
const [showFileDetailsInViewer, setShowFileDetailsInViewer] = useState(false);
|
|
158
791
|
const [isPickingDocument, setIsPickingDocument] = useState(false);
|
|
159
|
-
const [
|
|
792
|
+
const [reduceMotion, setReduceMotion] = useState(false);
|
|
793
|
+
|
|
794
|
+
// Detect reduce-motion preference once on mount + subscribe to changes.
|
|
795
|
+
// Used by `PhotoPickerView` to skip cell stagger animations.
|
|
796
|
+
useEffect(() => {
|
|
797
|
+
let cancelled = false;
|
|
798
|
+
AccessibilityInfo.isReduceMotionEnabled()
|
|
799
|
+
.then((enabled) => {
|
|
800
|
+
if (!cancelled) setReduceMotion(enabled);
|
|
801
|
+
})
|
|
802
|
+
.catch(() => {
|
|
803
|
+
// Defaults to false; no action needed.
|
|
804
|
+
});
|
|
805
|
+
const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', (enabled) => {
|
|
806
|
+
setReduceMotion(enabled);
|
|
807
|
+
});
|
|
808
|
+
return () => {
|
|
809
|
+
cancelled = true;
|
|
810
|
+
sub.remove();
|
|
811
|
+
};
|
|
812
|
+
}, []);
|
|
813
|
+
|
|
814
|
+
// Image-only picker mode: when the consumer restricts to image MIME types
|
|
815
|
+
// (e.g. avatar picker), photos grid is the more useful default view.
|
|
816
|
+
const isImageOnlyPicker = useMemo(() => {
|
|
817
|
+
if (!selectMode) return false;
|
|
818
|
+
if (disabledMimeTypes.length === 0) return false;
|
|
819
|
+
const blocksVideos = disabledMimeTypes.some(mt => mt === 'video/' || mt.startsWith('video/'));
|
|
820
|
+
const blocksAudio = disabledMimeTypes.some(mt => mt === 'audio/' || mt.startsWith('audio/'));
|
|
821
|
+
const blocksDocs = disabledMimeTypes.some(mt =>
|
|
822
|
+
mt === 'application/pdf' ||
|
|
823
|
+
mt === 'application/' ||
|
|
824
|
+
mt.startsWith('application/')
|
|
825
|
+
);
|
|
826
|
+
return blocksVideos && blocksAudio && blocksDocs;
|
|
827
|
+
}, [disabledMimeTypes, selectMode]);
|
|
828
|
+
|
|
829
|
+
const [viewMode, setViewMode] = useState<'all' | 'photos' | 'videos' | 'documents' | 'audio'>(
|
|
830
|
+
isImageOnlyPicker ? 'photos' : 'all',
|
|
831
|
+
);
|
|
160
832
|
const [searchQuery, setSearchQuery] = useState('');
|
|
161
833
|
const [sortBy, setSortBy] = useState<'date' | 'size' | 'name' | 'type'>('date');
|
|
162
834
|
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
|
|
163
|
-
const [pendingFiles, setPendingFiles] = useState<Array<{ file:
|
|
835
|
+
const [pendingFiles, setPendingFiles] = useState<Array<{ file: UploadCandidate; preview?: string; size: number; name: string; type: string }>>([]);
|
|
164
836
|
const [showUploadPreview, setShowUploadPreview] = useState(false);
|
|
165
837
|
// Derived filtered and sorted files (avoid setState loops)
|
|
166
838
|
const filteredFiles = useMemo(() => {
|
|
@@ -263,7 +935,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
263
935
|
linkContext.entityType,
|
|
264
936
|
linkContext.entityId,
|
|
265
937
|
defaultVisibility,
|
|
266
|
-
|
|
938
|
+
linkContext.webhookUrl
|
|
267
939
|
);
|
|
268
940
|
} catch (error) {
|
|
269
941
|
// Continue anyway - selection shouldn't fail if linking fails
|
|
@@ -322,7 +994,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
322
994
|
linkContext.entityType,
|
|
323
995
|
linkContext.entityId,
|
|
324
996
|
defaultVisibility,
|
|
325
|
-
|
|
997
|
+
linkContext.webhookUrl
|
|
326
998
|
);
|
|
327
999
|
} catch (error) {
|
|
328
1000
|
// File linking failed, continue with selection
|
|
@@ -383,15 +1055,15 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
383
1055
|
const currentPaging = mode === 'more' ? (prevPagingRef.current ?? paging) : paging;
|
|
384
1056
|
const effectiveOffset = mode === 'more' ? currentPaging.offset + currentPaging.limit : 0;
|
|
385
1057
|
const response = await oxyServices.listUserFiles(currentPaging.limit, effectiveOffset);
|
|
386
|
-
const assets: FileMetadata[] = (response.files || []).map((f: Record<string, unknown
|
|
1058
|
+
const assets: FileMetadata[] = (response.files || []).map((f: { id: string; originalName?: string; sha256?: string; mime?: string; size?: number; createdAt?: string; metadata?: Record<string, unknown>; variants?: unknown[] }) => ({
|
|
387
1059
|
id: f.id,
|
|
388
|
-
filename: f.originalName
|
|
389
|
-
contentType: f.mime,
|
|
390
|
-
length: f.size,
|
|
1060
|
+
filename: f.originalName ?? f.sha256 ?? '',
|
|
1061
|
+
contentType: f.mime ?? '',
|
|
1062
|
+
length: f.size ?? 0,
|
|
391
1063
|
chunkSize: 0,
|
|
392
|
-
uploadDate: f.createdAt,
|
|
393
|
-
metadata: f.metadata
|
|
394
|
-
variants: f.variants
|
|
1064
|
+
uploadDate: f.createdAt ?? '',
|
|
1065
|
+
metadata: f.metadata ?? {},
|
|
1066
|
+
variants: (f.variants ?? []) as FileMetadata['variants'],
|
|
395
1067
|
}));
|
|
396
1068
|
if (mode === 'more') {
|
|
397
1069
|
// append
|
|
@@ -500,16 +1172,16 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
500
1172
|
return rows;
|
|
501
1173
|
}, []);
|
|
502
1174
|
|
|
503
|
-
const processFileUploads = async (selectedFiles:
|
|
1175
|
+
const processFileUploads = async (selectedFiles: UploadCandidate[]): Promise<FileMetadata[]> => {
|
|
504
1176
|
if (selectedFiles.length === 0) return [];
|
|
505
1177
|
if (!targetUserId) return []; // Guard clause to ensure userId is defined
|
|
506
1178
|
const uploadedFiles: FileMetadata[] = [];
|
|
507
1179
|
try {
|
|
508
1180
|
storeSetUploadProgress({ current: 0, total: selectedFiles.length });
|
|
509
1181
|
const maxSize = 50 * 1024 * 1024; // 50MB
|
|
510
|
-
const oversizedFiles = selectedFiles.filter(file => file
|
|
1182
|
+
const oversizedFiles = selectedFiles.filter(file => candidateSize(file) > maxSize);
|
|
511
1183
|
if (oversizedFiles.length > 0) {
|
|
512
|
-
const fileList = oversizedFiles.map(f => f
|
|
1184
|
+
const fileList = oversizedFiles.map(f => candidateName(f, 'file')).join(', ');
|
|
513
1185
|
toast.error(t('fileManagement.toasts.filesTooLarge', { files: fileList }));
|
|
514
1186
|
return [];
|
|
515
1187
|
}
|
|
@@ -519,12 +1191,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
519
1191
|
for (let i = 0; i < selectedFiles.length; i++) {
|
|
520
1192
|
storeSetUploadProgress({ current: i + 1, total: selectedFiles.length });
|
|
521
1193
|
const raw = selectedFiles[i];
|
|
522
|
-
const fileName = raw
|
|
1194
|
+
const fileName = candidateName(raw, `file-${i + 1}`);
|
|
1195
|
+
const fileSize = candidateSize(raw);
|
|
1196
|
+
const fileType = candidateType(raw);
|
|
523
1197
|
const optimisticId = `temp-${Date.now()}-${i}-${Math.random().toString(36).substr(2, 9)}`; // Unique ID per file
|
|
524
1198
|
|
|
525
1199
|
try {
|
|
526
1200
|
// Validate file before upload
|
|
527
|
-
if (!raw || !
|
|
1201
|
+
if (!raw || !fileName || fileSize <= 0) {
|
|
528
1202
|
const errorMsg = `Invalid file: ${fileName}`;
|
|
529
1203
|
if (__DEV__) {
|
|
530
1204
|
console.error('Upload validation failed:', { file: raw, error: errorMsg });
|
|
@@ -536,9 +1210,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
536
1210
|
|
|
537
1211
|
const optimisticFile: FileMetadata = {
|
|
538
1212
|
id: optimisticId,
|
|
539
|
-
filename:
|
|
540
|
-
contentType:
|
|
541
|
-
length:
|
|
1213
|
+
filename: fileName,
|
|
1214
|
+
contentType: fileType,
|
|
1215
|
+
length: fileSize,
|
|
542
1216
|
chunkSize: 0,
|
|
543
1217
|
uploadDate: new Date().toISOString(),
|
|
544
1218
|
metadata: { uploading: true },
|
|
@@ -548,7 +1222,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
548
1222
|
|
|
549
1223
|
// Use the mutation hook with authentication handling
|
|
550
1224
|
const result = await uploadFileMutation.mutateAsync({
|
|
551
|
-
file: raw,
|
|
1225
|
+
file: raw as AssetUploadInput,
|
|
552
1226
|
visibility: defaultVisibility,
|
|
553
1227
|
});
|
|
554
1228
|
|
|
@@ -557,9 +1231,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
557
1231
|
if (f) {
|
|
558
1232
|
const merged: FileMetadata = {
|
|
559
1233
|
id: f.id,
|
|
560
|
-
filename: f.originalName || f.sha256 ||
|
|
561
|
-
contentType: f.mime ||
|
|
562
|
-
length: f.size ||
|
|
1234
|
+
filename: f.originalName || f.sha256 || fileName,
|
|
1235
|
+
contentType: f.mime || fileType,
|
|
1236
|
+
length: f.size || fileSize,
|
|
563
1237
|
chunkSize: 0,
|
|
564
1238
|
uploadDate: f.createdAt || new Date().toISOString(),
|
|
565
1239
|
metadata: f.metadata || {},
|
|
@@ -581,14 +1255,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
581
1255
|
}
|
|
582
1256
|
} catch (error: unknown) {
|
|
583
1257
|
failureCount++;
|
|
584
|
-
const errorMessage = getErrorMessage(error) ||
|
|
1258
|
+
const errorMessage = getErrorMessage(error) || 'Upload failed';
|
|
585
1259
|
const fullError = `${fileName}: ${errorMessage}`;
|
|
586
1260
|
errors.push(fullError);
|
|
587
1261
|
if (__DEV__) {
|
|
588
1262
|
console.error('File upload failed:', {
|
|
589
1263
|
fileName,
|
|
590
|
-
fileSize
|
|
591
|
-
fileType
|
|
1264
|
+
fileSize,
|
|
1265
|
+
fileType,
|
|
592
1266
|
error: errorMessage,
|
|
593
1267
|
stack: (error instanceof Error) ? error.stack : undefined
|
|
594
1268
|
});
|
|
@@ -620,10 +1294,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
620
1294
|
return uploadedFiles;
|
|
621
1295
|
};
|
|
622
1296
|
|
|
623
|
-
|
|
624
|
-
const handleFileSelection = useCallback(async (selectedFiles: Array<File | any>) => {
|
|
1297
|
+
const handleFileSelection = useCallback(async (selectedFiles: UploadCandidate[]) => {
|
|
625
1298
|
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
|
|
626
|
-
const processedFiles: Array<{ file:
|
|
1299
|
+
const processedFiles: Array<{ file: UploadCandidate; preview?: string; size: number; name: string; type: string }> = [];
|
|
627
1300
|
|
|
628
1301
|
for (const file of selectedFiles) {
|
|
629
1302
|
// Validate file has required properties
|
|
@@ -635,7 +1308,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
635
1308
|
continue;
|
|
636
1309
|
}
|
|
637
1310
|
|
|
638
|
-
|
|
1311
|
+
const name = candidateName(file, '');
|
|
1312
|
+
if (!name) {
|
|
639
1313
|
if (__DEV__) {
|
|
640
1314
|
console.error('Invalid file: missing or invalid name property', file);
|
|
641
1315
|
}
|
|
@@ -643,46 +1317,47 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
643
1317
|
continue;
|
|
644
1318
|
}
|
|
645
1319
|
|
|
646
|
-
|
|
1320
|
+
const size = (file as { size?: number }).size;
|
|
1321
|
+
if (size === undefined || size === null || Number.isNaN(size)) {
|
|
647
1322
|
if (__DEV__) {
|
|
648
1323
|
console.error('Invalid file: missing or invalid size property', file);
|
|
649
1324
|
}
|
|
650
|
-
toast.error(t('fileManagement.toasts.invalidFileSize', { name
|
|
1325
|
+
toast.error(t('fileManagement.toasts.invalidFileSize', { name }));
|
|
651
1326
|
continue;
|
|
652
1327
|
}
|
|
653
1328
|
|
|
654
|
-
if (
|
|
1329
|
+
if (size <= 0) {
|
|
655
1330
|
if (__DEV__) {
|
|
656
1331
|
console.error('Invalid file: file size is zero or negative', file);
|
|
657
1332
|
}
|
|
658
|
-
toast.error(t('fileManagement.toasts.fileEmpty', { name
|
|
1333
|
+
toast.error(t('fileManagement.toasts.fileEmpty', { name }));
|
|
659
1334
|
continue;
|
|
660
1335
|
}
|
|
661
1336
|
|
|
662
1337
|
// Validate file size
|
|
663
|
-
if (
|
|
664
|
-
toast.error(t('fileManagement.toasts.fileTooLarge', { name
|
|
1338
|
+
if (size > MAX_FILE_SIZE) {
|
|
1339
|
+
toast.error(t('fileManagement.toasts.fileTooLarge', { name, maxSize: formatFileSize(MAX_FILE_SIZE) }));
|
|
665
1340
|
continue;
|
|
666
1341
|
}
|
|
667
1342
|
|
|
668
|
-
|
|
669
|
-
const fileType = file.type || 'application/octet-stream';
|
|
1343
|
+
const fileType = candidateType(file);
|
|
670
1344
|
|
|
671
1345
|
// Generate preview for images - unified approach
|
|
672
1346
|
let preview: string | undefined;
|
|
673
1347
|
if (fileType.startsWith('image/')) {
|
|
674
1348
|
// Try to use file URI from expo-document-picker if available (works on all platforms)
|
|
675
|
-
const fileUri = (file
|
|
676
|
-
if (fileUri &&
|
|
1349
|
+
const fileUri = candidateUri(file);
|
|
1350
|
+
if (fileUri &&
|
|
677
1351
|
(fileUri.startsWith('file://') || fileUri.startsWith('content://') ||
|
|
678
1352
|
fileUri.startsWith('http://') || fileUri.startsWith('https://') ||
|
|
679
1353
|
fileUri.startsWith('blob:'))) {
|
|
680
1354
|
preview = fileUri;
|
|
681
1355
|
} else {
|
|
682
|
-
// Fallback: create blob URL if possible (works on web)
|
|
1356
|
+
// Fallback: create blob URL if possible (works on web only)
|
|
683
1357
|
try {
|
|
684
|
-
if ((
|
|
685
|
-
|
|
1358
|
+
if ((typeof File !== 'undefined' && file instanceof File) ||
|
|
1359
|
+
(typeof Blob !== 'undefined' && file instanceof Blob)) {
|
|
1360
|
+
preview = URL.createObjectURL(file as Blob);
|
|
686
1361
|
}
|
|
687
1362
|
} catch (error: unknown) {
|
|
688
1363
|
if (__DEV__) {
|
|
@@ -696,8 +1371,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
696
1371
|
processedFiles.push({
|
|
697
1372
|
file,
|
|
698
1373
|
preview,
|
|
699
|
-
size
|
|
700
|
-
name
|
|
1374
|
+
size,
|
|
1375
|
+
name,
|
|
701
1376
|
type: fileType
|
|
702
1377
|
});
|
|
703
1378
|
}
|
|
@@ -710,7 +1385,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
710
1385
|
// Show preview modal for user to review files before upload
|
|
711
1386
|
setPendingFiles(processedFiles);
|
|
712
1387
|
setShowUploadPreview(true);
|
|
713
|
-
}, []);
|
|
1388
|
+
}, [t]);
|
|
714
1389
|
|
|
715
1390
|
const handleConfirmUpload = async () => {
|
|
716
1391
|
if (pendingFiles.length === 0) return;
|
|
@@ -721,7 +1396,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
721
1396
|
storeSetUploadProgress(null);
|
|
722
1397
|
|
|
723
1398
|
try {
|
|
724
|
-
const filesToUpload = pendingFiles.map(pf => pf.file
|
|
1399
|
+
const filesToUpload = pendingFiles.map(pf => pf.file);
|
|
725
1400
|
storeSetUploadProgress({ current: 0, total: filesToUpload.length });
|
|
726
1401
|
const uploadedFiles = await processFileUploads(filesToUpload);
|
|
727
1402
|
|
|
@@ -830,17 +1505,17 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
830
1505
|
// - size: file size in bytes
|
|
831
1506
|
// - mimeType: MIME type of the file
|
|
832
1507
|
// - file: (optional) native File object (usually only on web)
|
|
833
|
-
const files:
|
|
1508
|
+
const files: UploadCandidate[] = [];
|
|
834
1509
|
const errors: string[] = [];
|
|
835
1510
|
|
|
836
1511
|
// Process files in parallel for better performance
|
|
837
1512
|
// This allows multiple files to be converted simultaneously
|
|
838
1513
|
const conversionPromises = result.assets.map((doc, index) =>
|
|
839
1514
|
convertDocumentPickerAssetToFile(doc, index)
|
|
840
|
-
.then((file) => {
|
|
1515
|
+
.then((file): UploadCandidate | null => {
|
|
841
1516
|
if (file) {
|
|
842
1517
|
// Validate file has required properties before adding
|
|
843
|
-
if (!file.name || file.size === undefined) {
|
|
1518
|
+
if (!file.name || (file as { size?: number }).size === undefined) {
|
|
844
1519
|
errors.push(`File "${doc.name || 'file'}" is invalid: missing required properties`);
|
|
845
1520
|
return null;
|
|
846
1521
|
}
|
|
@@ -897,8 +1572,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
897
1572
|
|
|
898
1573
|
const confirmFileDelete = useCallback((fileId: string, filename: string) => {
|
|
899
1574
|
setPendingDeleteFile({ id: fileId, name: filename });
|
|
900
|
-
|
|
901
|
-
}, [
|
|
1575
|
+
fileDeleteDialog.open();
|
|
1576
|
+
}, [fileDeleteDialog]);
|
|
902
1577
|
|
|
903
1578
|
const handleFileDelete = useCallback(async () => {
|
|
904
1579
|
if (!pendingDeleteFile) return;
|
|
@@ -935,8 +1610,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
935
1610
|
|
|
936
1611
|
const confirmBulkDelete = useCallback(() => {
|
|
937
1612
|
if (selectedIds.size === 0) return;
|
|
938
|
-
|
|
939
|
-
}, [selectedIds.size,
|
|
1613
|
+
bulkDeleteDialog.open();
|
|
1614
|
+
}, [selectedIds.size, bulkDeleteDialog]);
|
|
940
1615
|
|
|
941
1616
|
const handleBulkDelete = useCallback(async () => {
|
|
942
1617
|
if (selectedIds.size === 0) return;
|
|
@@ -1913,6 +2588,59 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1913
2588
|
return <SkeletonLoader />;
|
|
1914
2589
|
}
|
|
1915
2590
|
|
|
2591
|
+
// Dedicated flagship-style photo picker view used in the avatar-picker
|
|
2592
|
+
// context (selectMode + image-only filter). The picker is intentionally
|
|
2593
|
+
// minimal: black backdrop, edge-to-edge grid, translucent header. The
|
|
2594
|
+
// standalone file manager (non-picker browse) keeps the existing UI.
|
|
2595
|
+
if (isImageOnlyPicker && !showUploadPreview) {
|
|
2596
|
+
const photosOnly = filteredFiles.filter(
|
|
2597
|
+
(file) => file.contentType.startsWith('image/'),
|
|
2598
|
+
);
|
|
2599
|
+
const isOwner = user?.id === targetUserId;
|
|
2600
|
+
const allowUpload = isOwner && allowUploadInSelectMode;
|
|
2601
|
+
|
|
2602
|
+
return (
|
|
2603
|
+
<>
|
|
2604
|
+
<PhotoPickerView
|
|
2605
|
+
photos={photosOnly}
|
|
2606
|
+
selectedIds={selectedIds}
|
|
2607
|
+
multiSelect={multiSelect}
|
|
2608
|
+
maxSelection={maxSelection}
|
|
2609
|
+
allowUpload={allowUpload}
|
|
2610
|
+
refreshing={refreshing}
|
|
2611
|
+
uploading={uploading}
|
|
2612
|
+
isPickingDocument={isPickingDocument}
|
|
2613
|
+
uploadProgress={uploadProgress}
|
|
2614
|
+
hasMore={paging.hasMore}
|
|
2615
|
+
loadingMore={paging.loadingMore}
|
|
2616
|
+
reduceMotion={reduceMotion}
|
|
2617
|
+
getThumbUrl={getSafeDownloadUrlCallback}
|
|
2618
|
+
primaryColor={colors.primary}
|
|
2619
|
+
isOwner={isOwner}
|
|
2620
|
+
onTogglePhoto={toggleSelect}
|
|
2621
|
+
onPreviewPhoto={(file) => showFileDetailsModal(file)}
|
|
2622
|
+
onUpload={handleFileUpload}
|
|
2623
|
+
onRefresh={() => loadFiles('refresh')}
|
|
2624
|
+
onLoadMore={() => loadFiles('more')}
|
|
2625
|
+
onCancel={() => {
|
|
2626
|
+
if (onClose) onClose();
|
|
2627
|
+
else goBack?.();
|
|
2628
|
+
}}
|
|
2629
|
+
onConfirm={confirmMultiSelection}
|
|
2630
|
+
t={t}
|
|
2631
|
+
/>
|
|
2632
|
+
{/* Long-press preview surfaces the existing details modal. */}
|
|
2633
|
+
<FileDetailsModal
|
|
2634
|
+
control={fileDetailsControl}
|
|
2635
|
+
file={selectedFile}
|
|
2636
|
+
onDownload={handleFileDownload}
|
|
2637
|
+
onDelete={confirmFileDelete}
|
|
2638
|
+
isOwner={isOwner}
|
|
2639
|
+
/>
|
|
2640
|
+
</>
|
|
2641
|
+
);
|
|
2642
|
+
}
|
|
2643
|
+
|
|
1916
2644
|
// If a file is opened, show the file viewer
|
|
1917
2645
|
if (!selectMode && openedFile) {
|
|
1918
2646
|
return (
|
|
@@ -1997,7 +2725,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1997
2725
|
key: 'visibility',
|
|
1998
2726
|
text: t('fileManagement.visibility'),
|
|
1999
2727
|
onPress: () => {
|
|
2000
|
-
|
|
2728
|
+
visibilityChangeDialog.open();
|
|
2001
2729
|
},
|
|
2002
2730
|
icon: 'eye',
|
|
2003
2731
|
}
|
|
@@ -2029,6 +2757,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
2029
2757
|
primaryColor={colors.primary}
|
|
2030
2758
|
textColor={colors.text}
|
|
2031
2759
|
style={fileManagementStyles.viewModeButton}
|
|
2760
|
+
accessibilityLabel={t('fileManagement.a11y.viewAll') || 'Show all files'}
|
|
2032
2761
|
/>
|
|
2033
2762
|
<AnimatedButton
|
|
2034
2763
|
isSelected={viewMode === 'photos'}
|
|
@@ -2037,37 +2766,50 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
2037
2766
|
primaryColor={colors.primary}
|
|
2038
2767
|
textColor={colors.text}
|
|
2039
2768
|
style={fileManagementStyles.viewModeButton}
|
|
2769
|
+
accessibilityLabel={t('fileManagement.a11y.viewPhotos') || 'Show photos only'}
|
|
2040
2770
|
/>
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2771
|
+
{!isImageOnlyPicker && (
|
|
2772
|
+
<>
|
|
2773
|
+
<AnimatedButton
|
|
2774
|
+
isSelected={viewMode === 'videos'}
|
|
2775
|
+
onPress={() => setViewMode('videos')}
|
|
2776
|
+
icon={viewMode === 'videos' ? 'video' : 'video-outline'}
|
|
2777
|
+
primaryColor={colors.primary}
|
|
2778
|
+
textColor={colors.text}
|
|
2779
|
+
style={fileManagementStyles.viewModeButton}
|
|
2780
|
+
accessibilityLabel={t('fileManagement.a11y.viewVideos') || 'Show videos only'}
|
|
2781
|
+
/>
|
|
2782
|
+
<AnimatedButton
|
|
2783
|
+
isSelected={viewMode === 'documents'}
|
|
2784
|
+
onPress={() => setViewMode('documents')}
|
|
2785
|
+
icon={viewMode === 'documents' ? 'file-document' : 'file-document-outline'}
|
|
2786
|
+
primaryColor={colors.primary}
|
|
2787
|
+
textColor={colors.text}
|
|
2788
|
+
style={fileManagementStyles.viewModeButton}
|
|
2789
|
+
accessibilityLabel={t('fileManagement.a11y.viewDocuments') || 'Show documents only'}
|
|
2790
|
+
/>
|
|
2791
|
+
<AnimatedButton
|
|
2792
|
+
isSelected={viewMode === 'audio'}
|
|
2793
|
+
onPress={() => setViewMode('audio')}
|
|
2794
|
+
icon={viewMode === 'audio' ? 'music-note' : 'music-note-outline'}
|
|
2795
|
+
primaryColor={colors.primary}
|
|
2796
|
+
textColor={colors.text}
|
|
2797
|
+
style={fileManagementStyles.viewModeButton}
|
|
2798
|
+
accessibilityLabel={t('fileManagement.a11y.viewAudio') || 'Show audio only'}
|
|
2799
|
+
/>
|
|
2800
|
+
</>
|
|
2801
|
+
)}
|
|
2065
2802
|
</View>
|
|
2066
2803
|
</ScrollView>
|
|
2067
2804
|
<TouchableOpacity
|
|
2068
2805
|
style={[fileManagementStyles.sortButton, {
|
|
2069
2806
|
backgroundColor: colors.card,
|
|
2070
2807
|
}]}
|
|
2808
|
+
accessibilityRole="button"
|
|
2809
|
+
accessibilityLabel={t('fileManagement.a11y.sortBy', {
|
|
2810
|
+
field: sortBy,
|
|
2811
|
+
order: sortOrder === 'asc' ? 'ascending' : 'descending',
|
|
2812
|
+
}) || `Sort by ${sortBy}, ${sortOrder === 'asc' ? 'ascending' : 'descending'}`}
|
|
2071
2813
|
onPress={() => {
|
|
2072
2814
|
// Cycle through sort options: date -> size -> name -> type -> date
|
|
2073
2815
|
const sortOrder: Array<'date' | 'size' | 'name' | 'type'> = ['date', 'size', 'name', 'type'];
|
|
@@ -2097,9 +2839,20 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
2097
2839
|
</TouchableOpacity>
|
|
2098
2840
|
{user?.id === targetUserId && (!selectMode || (selectMode && allowUploadInSelectMode)) && (
|
|
2099
2841
|
<TouchableOpacity
|
|
2100
|
-
style={[
|
|
2842
|
+
style={[
|
|
2843
|
+
fileManagementStyles.uploadButton,
|
|
2844
|
+
isImageOnlyPicker && fileManagementStyles.uploadButtonExtended,
|
|
2845
|
+
{ backgroundColor: colors.primary },
|
|
2846
|
+
]}
|
|
2101
2847
|
onPress={handleFileUpload}
|
|
2102
2848
|
disabled={uploading || isPickingDocument}
|
|
2849
|
+
accessibilityRole="button"
|
|
2850
|
+
accessibilityLabel={
|
|
2851
|
+
isImageOnlyPicker
|
|
2852
|
+
? (t('fileManagement.a11y.uploadFromDevice') || 'Upload photo from device')
|
|
2853
|
+
: (t('fileManagement.a11y.uploadFile') || 'Upload file')
|
|
2854
|
+
}
|
|
2855
|
+
accessibilityState={{ busy: uploading || isPickingDocument }}
|
|
2103
2856
|
>
|
|
2104
2857
|
{uploading ? (
|
|
2105
2858
|
<View style={fileManagementStyles.uploadProgress}>
|
|
@@ -2112,6 +2865,13 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
2112
2865
|
</View>
|
|
2113
2866
|
) : isPickingDocument ? (
|
|
2114
2867
|
<ActivityIndicator size="small" color="#FFFFFF" />
|
|
2868
|
+
) : isImageOnlyPicker ? (
|
|
2869
|
+
<View style={fileManagementStyles.uploadButtonContent}>
|
|
2870
|
+
<Ionicons name="cloud-upload" size={18} color="#FFFFFF" />
|
|
2871
|
+
<Text style={fileManagementStyles.uploadButtonLabel} numberOfLines={1}>
|
|
2872
|
+
{t('fileManagement.upload') || 'Upload'}
|
|
2873
|
+
</Text>
|
|
2874
|
+
</View>
|
|
2115
2875
|
) : (
|
|
2116
2876
|
<Ionicons name="add" size={22} color="#FFFFFF" />
|
|
2117
2877
|
)}
|
|
@@ -2284,34 +3044,35 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
2284
3044
|
|
|
2285
3045
|
{/* Selection bar removed; actions are now in header */}
|
|
2286
3046
|
{/* Global loadingMore bar removed; now inline in scroll areas */}
|
|
2287
|
-
<
|
|
2288
|
-
control={
|
|
3047
|
+
<Dialog
|
|
3048
|
+
control={fileDeleteDialog}
|
|
2289
3049
|
title={t('fileManagement.deleteFile') || 'Delete File'}
|
|
2290
3050
|
description={pendingDeleteFile ? t('fileManagement.confirms.deleteFile', { filename: pendingDeleteFile.name }) : ''}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
3051
|
+
actions={[
|
|
3052
|
+
{ label: t('fileManagement.confirm') || 'Delete', color: 'destructive', onPress: handleFileDelete },
|
|
3053
|
+
{ label: t('common.cancel') || 'Cancel', color: 'cancel' },
|
|
3054
|
+
]}
|
|
2294
3055
|
/>
|
|
2295
|
-
<
|
|
2296
|
-
control={
|
|
3056
|
+
<Dialog
|
|
3057
|
+
control={bulkDeleteDialog}
|
|
2297
3058
|
title={t('fileManagement.deleteFiles') || 'Delete Files'}
|
|
2298
3059
|
description={t('fileManagement.confirms.deleteFiles', { count: selectedIds.size })}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
3060
|
+
actions={[
|
|
3061
|
+
{ label: t('fileManagement.confirm') || 'Delete', color: 'destructive', onPress: handleBulkDelete },
|
|
3062
|
+
{ label: t('common.cancel') || 'Cancel', color: 'cancel' },
|
|
3063
|
+
]}
|
|
3064
|
+
/>
|
|
3065
|
+
<Dialog
|
|
3066
|
+
control={visibilityChangeDialog}
|
|
3067
|
+
title={t('fileManagement.changeVisibility') || 'Change Visibility'}
|
|
3068
|
+
description={t('fileManagement.changeVisibilityConfirm', { count: selectedIds.size })}
|
|
3069
|
+
actions={[
|
|
3070
|
+
{ label: t('fileManagement.private') || 'Private', onPress: () => handleBulkVisibilityChange('private') },
|
|
3071
|
+
{ label: t('fileManagement.public') || 'Public', onPress: () => handleBulkVisibilityChange('public') },
|
|
3072
|
+
{ label: t('fileManagement.unlisted') || 'Unlisted', onPress: () => handleBulkVisibilityChange('unlisted') },
|
|
3073
|
+
{ label: t('common.cancel') || 'Cancel', color: 'cancel' },
|
|
3074
|
+
]}
|
|
2302
3075
|
/>
|
|
2303
|
-
<Prompt.Outer control={visibilityChangePrompt}>
|
|
2304
|
-
<Prompt.Content>
|
|
2305
|
-
<Prompt.TitleText>{t('fileManagement.changeVisibility') || 'Change Visibility'}</Prompt.TitleText>
|
|
2306
|
-
<Prompt.DescriptionText>{t('fileManagement.changeVisibilityConfirm', { count: selectedIds.size })}</Prompt.DescriptionText>
|
|
2307
|
-
</Prompt.Content>
|
|
2308
|
-
<Prompt.Actions>
|
|
2309
|
-
<Prompt.Action cta={t('fileManagement.private') || 'Private'} onPress={() => handleBulkVisibilityChange('private')} color='primary' />
|
|
2310
|
-
<Prompt.Action cta={t('fileManagement.public') || 'Public'} onPress={() => handleBulkVisibilityChange('public')} color='primary_subtle' />
|
|
2311
|
-
<Prompt.Action cta={t('fileManagement.unlisted') || 'Unlisted'} onPress={() => handleBulkVisibilityChange('unlisted')} color='primary_subtle' />
|
|
2312
|
-
<Prompt.Cancel cta={t('common.cancel') || 'Cancel'} />
|
|
2313
|
-
</Prompt.Actions>
|
|
2314
|
-
</Prompt.Outer>
|
|
2315
3076
|
</View>
|
|
2316
3077
|
);
|
|
2317
3078
|
};
|