@oxyhq/services 5.5.9 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/lib/commonjs/core/index.js +69 -82
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +24 -183
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/node/index.js +0 -2
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +100 -229
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyPayButton.js +131 -0
- package/lib/commonjs/ui/components/OxyPayButton.js.map +1 -0
- package/lib/commonjs/ui/components/OxyProvider.js +41 -198
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +15 -2
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/icon/FAIRWalletIcon.js +66 -0
- package/lib/commonjs/ui/components/icon/FAIRWalletIcon.js.map +1 -0
- package/lib/commonjs/ui/components/icon/index.js +7 -0
- package/lib/commonjs/ui/components/icon/index.js.map +1 -1
- package/lib/commonjs/ui/components/index.js +7 -0
- package/lib/commonjs/ui/components/index.js.map +1 -1
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +14 -7
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/commonjs/ui/components/internal/PinInput.js +108 -0
- package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -0
- package/lib/commonjs/ui/components/internal/TextField.js +20 -0
- package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +26 -36
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/index.js +2 -15
- package/lib/commonjs/ui/hooks/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFollow.js +52 -136
- package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +52 -0
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -0
- package/lib/commonjs/ui/index.js +8 -191
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/OxyRouter.js +52 -60
- package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +18 -30
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +4 -22
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +6 -14
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +37 -66
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js +21 -44
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FeedbackScreen.js +44 -23
- package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +59 -78
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +1588 -0
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +22 -36
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js +269 -0
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +47 -69
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +99 -333
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +136 -340
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +192 -0
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -0
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +135 -0
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -0
- package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +108 -0
- package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +1 -0
- package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +126 -0
- package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +1 -0
- package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js +84 -0
- package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js.map +1 -0
- package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js +59 -0
- package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js.map +1 -0
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +15 -2
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +31 -0
- package/lib/commonjs/ui/stores/authStore.js.map +1 -0
- package/lib/commonjs/ui/stores/followStore.js +124 -0
- package/lib/commonjs/ui/stores/followStore.js.map +1 -0
- package/lib/commonjs/ui/styles/index.js +0 -11
- package/lib/commonjs/ui/styles/index.js.map +1 -1
- package/lib/commonjs/ui/utils/confirmAction.js +28 -0
- package/lib/commonjs/ui/utils/confirmAction.js.map +1 -0
- package/lib/module/core/index.js +69 -81
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +14 -17
- package/lib/module/index.js.map +1 -1
- package/lib/module/node/index.js +0 -3
- package/lib/module/node/index.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +100 -229
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/OxyPayButton.js +125 -0
- package/lib/module/ui/components/OxyPayButton.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +42 -199
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +15 -2
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/icon/FAIRWalletIcon.js +60 -0
- package/lib/module/ui/components/icon/FAIRWalletIcon.js.map +1 -0
- package/lib/module/ui/components/icon/index.js +1 -0
- package/lib/module/ui/components/icon/index.js.map +1 -1
- package/lib/module/ui/components/index.js +1 -0
- package/lib/module/ui/components/index.js.map +1 -1
- package/lib/module/ui/components/internal/GroupedPillButtons.js +15 -8
- package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/module/ui/components/internal/PinInput.js +103 -0
- package/lib/module/ui/components/internal/PinInput.js.map +1 -0
- package/lib/module/ui/components/internal/TextField.js +20 -0
- package/lib/module/ui/components/internal/TextField.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +26 -36
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/index.js +1 -2
- package/lib/module/ui/hooks/index.js.map +1 -1
- package/lib/module/ui/hooks/useFollow.js +52 -137
- package/lib/module/ui/hooks/useFollow.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +47 -0
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -0
- package/lib/module/ui/index.js +2 -13
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/OxyRouter.js +53 -61
- package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +6 -18
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +5 -23
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +7 -15
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +38 -67
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +22 -45
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/FeedbackScreen.js +44 -23
- package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +59 -78
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js +1583 -0
- package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -0
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +23 -37
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/RecoverAccountScreen.js +263 -0
- package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -0
- package/lib/module/ui/screens/SessionManagementScreen.js +47 -69
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +100 -334
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +137 -341
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +186 -0
- package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -0
- package/lib/module/ui/screens/internal/SignInUsernameStep.js +129 -0
- package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -0
- package/lib/module/ui/screens/internal/SignUpIdentityStep.js +102 -0
- package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +1 -0
- package/lib/module/ui/screens/internal/SignUpSecurityStep.js +120 -0
- package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +1 -0
- package/lib/module/ui/screens/internal/SignUpSummaryStep.js +79 -0
- package/lib/module/ui/screens/internal/SignUpSummaryStep.js.map +1 -0
- package/lib/module/ui/screens/internal/SignUpWelcomeStep.js +54 -0
- package/lib/module/ui/screens/internal/SignUpWelcomeStep.js.map +1 -0
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +16 -3
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +27 -0
- package/lib/module/ui/stores/authStore.js.map +1 -0
- package/lib/module/ui/stores/followStore.js +120 -0
- package/lib/module/ui/stores/followStore.js.map +1 -0
- package/lib/module/ui/styles/index.js +0 -1
- package/lib/module/ui/styles/index.js.map +1 -1
- package/lib/module/ui/utils/confirmAction.js +25 -0
- package/lib/module/ui/utils/confirmAction.js.map +1 -0
- package/lib/typescript/core/index.d.ts +28 -10
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +5 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/secureSession.d.ts +0 -1
- package/lib/typescript/models/secureSession.d.ts.map +1 -1
- package/lib/typescript/node/index.d.ts +0 -1
- package/lib/typescript/node/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/FollowButton.d.ts +1 -77
- package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyPayButton.d.ts +29 -0
- package/lib/typescript/ui/components/OxyPayButton.d.ts.map +1 -0
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/ui/components/icon/FAIRWalletIcon.d.ts +8 -0
- package/lib/typescript/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -0
- package/lib/typescript/ui/components/icon/index.d.ts +1 -0
- package/lib/typescript/ui/components/icon/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/index.d.ts +1 -0
- package/lib/typescript/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/PinInput.d.ts +12 -0
- package/lib/typescript/ui/components/internal/PinInput.d.ts.map +1 -0
- package/lib/typescript/ui/components/internal/TextField.d.ts +1 -0
- package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +0 -2
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/index.d.ts +1 -2
- package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useFollow.d.ts +14 -15
- package/lib/typescript/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts +11 -0
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -0
- package/lib/typescript/ui/index.d.ts +2 -5
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/types.d.ts +5 -23
- package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts +27 -0
- package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +8 -0
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +27 -0
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +26 -0
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts +20 -0
- package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts +24 -0
- package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts +15 -0
- package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts +13 -0
- package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +14 -0
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -0
- package/lib/typescript/ui/stores/followStore.d.ts +15 -0
- package/lib/typescript/ui/stores/followStore.d.ts.map +1 -0
- package/lib/typescript/ui/styles/index.d.ts +0 -1
- package/lib/typescript/ui/styles/index.d.ts.map +1 -1
- package/lib/typescript/ui/utils/confirmAction.d.ts +7 -0
- package/lib/typescript/ui/utils/confirmAction.d.ts.map +1 -0
- package/package.json +12 -7
- package/src/core/index.ts +78 -88
- package/src/index.ts +8 -45
- package/src/models/secureSession.ts +1 -2
- package/src/node/index.ts +0 -3
- package/src/ui/components/FollowButton.tsx +100 -322
- package/src/ui/components/OxyPayButton.tsx +133 -0
- package/src/ui/components/OxyProvider.tsx +39 -201
- package/src/ui/components/OxySignInButton.tsx +13 -2
- package/src/ui/components/icon/FAIRWalletIcon.tsx +49 -0
- package/src/ui/components/icon/index.ts +1 -0
- package/src/ui/components/index.ts +1 -0
- package/src/ui/components/internal/GroupedPillButtons.tsx +12 -8
- package/src/ui/components/internal/PinInput.tsx +102 -0
- package/src/ui/components/internal/TextField.tsx +9 -0
- package/src/ui/context/OxyContext.tsx +26 -40
- package/src/ui/hooks/index.ts +1 -2
- package/src/ui/hooks/useFollow.ts +58 -129
- package/src/ui/hooks/useSessionSocket.ts +50 -0
- package/src/ui/index.ts +2 -37
- package/src/ui/navigation/OxyRouter.tsx +47 -63
- package/src/ui/navigation/types.ts +5 -26
- package/src/ui/screens/AccountCenterScreen.tsx +12 -21
- package/src/ui/screens/AccountOverviewScreen.tsx +6 -30
- package/src/ui/screens/AccountSettingsScreen.tsx +7 -22
- package/src/ui/screens/AccountSwitcherScreen.tsx +46 -88
- package/src/ui/screens/AppInfoScreen.tsx +27 -47
- package/src/ui/screens/FeedbackScreen.tsx +34 -19
- package/src/ui/screens/FileManagementScreen.tsx +293 -321
- package/src/ui/screens/PaymentGatewayScreen.tsx +1315 -0
- package/src/ui/screens/PremiumSubscriptionScreen.tsx +109 -124
- package/src/ui/screens/RecoverAccountScreen.tsx +260 -0
- package/src/ui/screens/SessionManagementScreen.tsx +65 -137
- package/src/ui/screens/SignInScreen.tsx +89 -283
- package/src/ui/screens/SignUpScreen.tsx +138 -291
- package/src/ui/screens/internal/SignInPasswordStep.tsx +179 -0
- package/src/ui/screens/internal/SignInUsernameStep.tsx +139 -0
- package/src/ui/screens/internal/SignUpIdentityStep.tsx +114 -0
- package/src/ui/screens/internal/SignUpSecurityStep.tsx +132 -0
- package/src/ui/screens/internal/SignUpSummaryStep.tsx +66 -0
- package/src/ui/screens/internal/SignUpWelcomeStep.tsx +52 -0
- package/src/ui/screens/karma/KarmaRewardsScreen.tsx +13 -3
- package/src/ui/stores/authStore.ts +24 -0
- package/src/ui/stores/followStore.ts +80 -0
- package/src/ui/styles/index.ts +0 -1
- package/src/ui/utils/confirmAction.ts +23 -0
- package/lib/commonjs/ui/components/bottomSheet/index.js +0 -37
- package/lib/commonjs/ui/components/bottomSheet/index.js.map +0 -1
- package/lib/commonjs/ui/hooks/useAuthFetch.js +0 -217
- package/lib/commonjs/ui/hooks/useAuthFetch.js.map +0 -1
- package/lib/commonjs/ui/hooks/useOxyFollow.js +0 -190
- package/lib/commonjs/ui/hooks/useOxyFollow.js.map +0 -1
- package/lib/commonjs/ui/screens/BillingManagementScreen.js +0 -636
- package/lib/commonjs/ui/screens/BillingManagementScreen.js.map +0 -1
- package/lib/commonjs/ui/store/index.js +0 -67
- package/lib/commonjs/ui/store/index.js.map +0 -1
- package/lib/commonjs/ui/store/setupOxyStore.js +0 -63
- package/lib/commonjs/ui/store/setupOxyStore.js.map +0 -1
- package/lib/commonjs/ui/store/slices/authSlice.js +0 -56
- package/lib/commonjs/ui/store/slices/authSlice.js.map +0 -1
- package/lib/commonjs/ui/store/slices/followSlice.js +0 -238
- package/lib/commonjs/ui/store/slices/followSlice.js.map +0 -1
- package/lib/commonjs/ui/store/slices/index.js +0 -129
- package/lib/commonjs/ui/store/slices/index.js.map +0 -1
- package/lib/commonjs/ui/store/slices/types.js +0 -19
- package/lib/commonjs/ui/store/slices/types.js.map +0 -1
- package/lib/commonjs/ui/styles/shadows.js +0 -123
- package/lib/commonjs/ui/styles/shadows.js.map +0 -1
- package/lib/commonjs/utils/polyfills.js +0 -42
- package/lib/commonjs/utils/polyfills.js.map +0 -1
- package/lib/module/ui/components/bottomSheet/index.js +0 -5
- package/lib/module/ui/components/bottomSheet/index.js.map +0 -1
- package/lib/module/ui/hooks/useAuthFetch.js +0 -212
- package/lib/module/ui/hooks/useAuthFetch.js.map +0 -1
- package/lib/module/ui/hooks/useOxyFollow.js +0 -186
- package/lib/module/ui/hooks/useOxyFollow.js.map +0 -1
- package/lib/module/ui/screens/BillingManagementScreen.js +0 -631
- package/lib/module/ui/screens/BillingManagementScreen.js.map +0 -1
- package/lib/module/ui/store/index.js +0 -33
- package/lib/module/ui/store/index.js.map +0 -1
- package/lib/module/ui/store/setupOxyStore.js +0 -59
- package/lib/module/ui/store/setupOxyStore.js.map +0 -1
- package/lib/module/ui/store/slices/authSlice.js +0 -48
- package/lib/module/ui/store/slices/authSlice.js.map +0 -1
- package/lib/module/ui/store/slices/followSlice.js +0 -232
- package/lib/module/ui/store/slices/followSlice.js.map +0 -1
- package/lib/module/ui/store/slices/index.js +0 -11
- package/lib/module/ui/store/slices/index.js.map +0 -1
- package/lib/module/ui/store/slices/types.js +0 -15
- package/lib/module/ui/store/slices/types.js.map +0 -1
- package/lib/module/ui/styles/shadows.js +0 -119
- package/lib/module/ui/styles/shadows.js.map +0 -1
- package/lib/module/utils/polyfills.js +0 -36
- package/lib/module/utils/polyfills.js.map +0 -1
- package/lib/typescript/types/react-redux.d.ts +0 -5
- package/lib/typescript/ui/components/bottomSheet/index.d.ts +0 -4
- package/lib/typescript/ui/components/bottomSheet/index.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts +0 -34
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useOxyFollow.d.ts +0 -81
- package/lib/typescript/ui/hooks/useOxyFollow.d.ts.map +0 -1
- package/lib/typescript/ui/screens/BillingManagementScreen.d.ts +0 -5
- package/lib/typescript/ui/screens/BillingManagementScreen.d.ts.map +0 -1
- package/lib/typescript/ui/store/index.d.ts +0 -27
- package/lib/typescript/ui/store/index.d.ts.map +0 -1
- package/lib/typescript/ui/store/setupOxyStore.d.ts +0 -29
- package/lib/typescript/ui/store/setupOxyStore.d.ts.map +0 -1
- package/lib/typescript/ui/store/slices/authSlice.d.ts +0 -32
- package/lib/typescript/ui/store/slices/authSlice.d.ts.map +0 -1
- package/lib/typescript/ui/store/slices/followSlice.d.ts +0 -120
- package/lib/typescript/ui/store/slices/followSlice.d.ts.map +0 -1
- package/lib/typescript/ui/store/slices/index.d.ts +0 -9
- package/lib/typescript/ui/store/slices/index.d.ts.map +0 -1
- package/lib/typescript/ui/store/slices/types.d.ts +0 -16
- package/lib/typescript/ui/store/slices/types.d.ts.map +0 -1
- package/lib/typescript/ui/styles/shadows.d.ts +0 -233
- package/lib/typescript/ui/styles/shadows.d.ts.map +0 -1
- package/lib/typescript/utils/polyfills.d.ts +0 -6
- package/lib/typescript/utils/polyfills.d.ts.map +0 -1
- package/src/__tests__/backend-middleware.test.ts +0 -209
- package/src/__tests__/polyfills.test.ts +0 -30
- package/src/__tests__/setup.ts +0 -43
- package/src/__tests__/ui/hooks/authfetch-integration.test.ts +0 -197
- package/src/__tests__/ui/hooks/backward-compatibility.test.ts +0 -159
- package/src/__tests__/ui/hooks/real-world-scenarios.test.ts +0 -224
- package/src/__tests__/ui/hooks/url-resolution.test.ts +0 -129
- package/src/__tests__/ui/hooks/useAuthFetch-separation.test.ts +0 -69
- package/src/__tests__/ui/hooks/useAuthFetch.test.ts +0 -70
- package/src/__tests__/ui/hooks/useOxyFollow.test.tsx +0 -92
- package/src/__tests__/ui/screens/AccountSettingsScreen.test.tsx +0 -112
- package/src/__tests__/ui/store/setupOxyStore.test.ts +0 -50
- package/src/__tests__/validate-structure.js +0 -91
- package/src/__tests__/validation.js +0 -42
- package/src/types/react-redux.d.ts +0 -5
- package/src/ui/components/bottomSheet/index.tsx +0 -14
- package/src/ui/hooks/useAuthFetch.ts +0 -238
- package/src/ui/hooks/useOxyFollow.ts +0 -188
- package/src/ui/screens/BillingManagementScreen.tsx +0 -589
- package/src/ui/store/index.ts +0 -36
- package/src/ui/store/setupOxyStore.ts +0 -58
- package/src/ui/store/slices/authSlice.ts +0 -43
- package/src/ui/store/slices/followSlice.ts +0 -207
- package/src/ui/store/slices/index.ts +0 -31
- package/src/ui/store/slices/types.ts +0 -33
- package/src/ui/styles/shadows.ts +0 -112
- package/src/utils/polyfills.ts +0 -34
|
@@ -24,6 +24,28 @@ interface FileManagementScreenProps extends BaseScreenProps {
|
|
|
24
24
|
userId?: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Add this helper function near the top (after imports):
|
|
28
|
+
async function uploadFileRaw(file: File | Blob, userId: string) {
|
|
29
|
+
const fileName = (file as any).name || 'upload.bin';
|
|
30
|
+
const mimeType = (file as any).type || 'application/octet-stream';
|
|
31
|
+
|
|
32
|
+
const res = await fetch('/api/files/upload-raw', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': mimeType,
|
|
36
|
+
'X-File-Name': encodeURIComponent(fileName),
|
|
37
|
+
'X-User-Id': userId,
|
|
38
|
+
},
|
|
39
|
+
body: file,
|
|
40
|
+
credentials: 'include', // if you use cookies/session
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!res.ok) {
|
|
44
|
+
throw new Error(await res.text());
|
|
45
|
+
}
|
|
46
|
+
return await res.json();
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
28
50
|
onClose,
|
|
29
51
|
theme,
|
|
@@ -33,7 +55,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
33
55
|
containerWidth = 400, // Fallback for when not provided by the router
|
|
34
56
|
}) => {
|
|
35
57
|
const { user, oxyServices } = useOxy();
|
|
36
|
-
|
|
58
|
+
|
|
37
59
|
// Debug: log the actual container width
|
|
38
60
|
useEffect(() => {
|
|
39
61
|
console.log('[FileManagementScreen] Container width (full):', containerWidth);
|
|
@@ -49,7 +71,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
49
71
|
const [loading, setLoading] = useState(true);
|
|
50
72
|
const [refreshing, setRefreshing] = useState(false);
|
|
51
73
|
const [uploading, setUploading] = useState(false);
|
|
52
|
-
const [uploadProgress, setUploadProgress] = useState<{current: number, total: number} | null>(null);
|
|
74
|
+
const [uploadProgress, setUploadProgress] = useState<{ current: number, total: number } | null>(null);
|
|
53
75
|
const [deleting, setDeleting] = useState<string | null>(null);
|
|
54
76
|
const [selectedFile, setSelectedFile] = useState<FileMetadata | null>(null);
|
|
55
77
|
const [showFileDetails, setShowFileDetails] = useState(false);
|
|
@@ -61,7 +83,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
61
83
|
const [searchQuery, setSearchQuery] = useState('');
|
|
62
84
|
const [filteredFiles, setFilteredFiles] = useState<FileMetadata[]>([]);
|
|
63
85
|
const [isDragging, setIsDragging] = useState(false);
|
|
64
|
-
const [photoDimensions, setPhotoDimensions] = useState<{[key: string]: {width: number, height: number}}>({});
|
|
86
|
+
const [photoDimensions, setPhotoDimensions] = useState<{ [key: string]: { width: number, height: number } }>({});
|
|
65
87
|
const [loadingDimensions, setLoadingDimensions] = useState(false);
|
|
66
88
|
const [hoveredPreview, setHoveredPreview] = useState<string | null>(null);
|
|
67
89
|
|
|
@@ -110,18 +132,18 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
110
132
|
// Filter files based on search query and view mode
|
|
111
133
|
useEffect(() => {
|
|
112
134
|
let filteredByMode = files;
|
|
113
|
-
|
|
135
|
+
|
|
114
136
|
// Filter by view mode first
|
|
115
137
|
if (viewMode === 'photos') {
|
|
116
138
|
filteredByMode = files.filter(file => file.contentType.startsWith('image/'));
|
|
117
139
|
}
|
|
118
|
-
|
|
140
|
+
|
|
119
141
|
// Then filter by search query
|
|
120
142
|
if (!searchQuery.trim()) {
|
|
121
143
|
setFilteredFiles(filteredByMode);
|
|
122
144
|
} else {
|
|
123
145
|
const query = searchQuery.toLowerCase();
|
|
124
|
-
const filtered = filteredByMode.filter(file =>
|
|
146
|
+
const filtered = filteredByMode.filter(file =>
|
|
125
147
|
file.filename.toLowerCase().includes(query) ||
|
|
126
148
|
file.contentType.toLowerCase().includes(query) ||
|
|
127
149
|
(file.metadata?.description && file.metadata.description.toLowerCase().includes(query))
|
|
@@ -133,14 +155,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
133
155
|
// Load photo dimensions for justified grid
|
|
134
156
|
const loadPhotoDimensions = useCallback(async (photos: FileMetadata[]) => {
|
|
135
157
|
if (photos.length === 0) return;
|
|
136
|
-
|
|
158
|
+
|
|
137
159
|
setLoadingDimensions(true);
|
|
138
|
-
const newDimensions: {[key: string]: {width: number, height: number}} = { ...photoDimensions };
|
|
160
|
+
const newDimensions: { [key: string]: { width: number, height: number } } = { ...photoDimensions };
|
|
139
161
|
let hasNewDimensions = false;
|
|
140
162
|
|
|
141
163
|
// Only load dimensions for photos we don't have yet
|
|
142
164
|
const photosToLoad = photos.filter(photo => !newDimensions[photo.id]);
|
|
143
|
-
|
|
165
|
+
|
|
144
166
|
if (photosToLoad.length === 0) {
|
|
145
167
|
setLoadingDimensions(false);
|
|
146
168
|
return;
|
|
@@ -151,7 +173,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
151
173
|
photosToLoad.map(async (photo) => {
|
|
152
174
|
try {
|
|
153
175
|
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
154
|
-
|
|
176
|
+
|
|
155
177
|
if (Platform.OS === 'web') {
|
|
156
178
|
const img = new (window as any).Image();
|
|
157
179
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -211,89 +233,53 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
211
233
|
// Create justified rows from photos with responsive algorithm
|
|
212
234
|
const createJustifiedRows = useCallback((photos: FileMetadata[], containerWidth: number) => {
|
|
213
235
|
if (photos.length === 0) return [];
|
|
214
|
-
|
|
236
|
+
|
|
215
237
|
const rows: FileMetadata[][] = [];
|
|
216
238
|
const photosPerRow = 3; // Fixed 3 photos per row for consistency
|
|
217
|
-
|
|
239
|
+
|
|
218
240
|
for (let i = 0; i < photos.length; i += photosPerRow) {
|
|
219
241
|
const rowPhotos = photos.slice(i, i + photosPerRow);
|
|
220
242
|
rows.push(rowPhotos);
|
|
221
243
|
}
|
|
222
|
-
|
|
244
|
+
|
|
223
245
|
return rows;
|
|
224
246
|
}, []);
|
|
225
247
|
|
|
226
248
|
const processFileUploads = async (selectedFiles: File[]) => {
|
|
227
249
|
if (selectedFiles.length === 0) return;
|
|
228
|
-
|
|
250
|
+
if (!targetUserId) return; // Guard clause to ensure userId is defined
|
|
229
251
|
try {
|
|
230
|
-
// Show initial progress
|
|
231
252
|
setUploadProgress({ current: 0, total: selectedFiles.length });
|
|
232
|
-
|
|
233
|
-
// Validate file sizes (example: 50MB limit per file)
|
|
234
253
|
const maxSize = 50 * 1024 * 1024; // 50MB
|
|
235
254
|
const oversizedFiles = selectedFiles.filter(file => file.size > maxSize);
|
|
236
|
-
|
|
237
255
|
if (oversizedFiles.length > 0) {
|
|
238
256
|
const fileList = oversizedFiles.map(f => f.name).join('\n');
|
|
239
257
|
window.alert(`File Size Limit\n\nThe following files are too large (max 50MB):\n${fileList}`);
|
|
240
258
|
return;
|
|
241
259
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
toast.success(`${response.files.length} file(s) uploaded successfully`);
|
|
256
|
-
// Small delay to ensure backend processing is complete
|
|
257
|
-
setTimeout(async () => {
|
|
258
|
-
await loadFiles();
|
|
259
|
-
}, 500);
|
|
260
|
-
} else {
|
|
261
|
-
// Option 2: Individual uploads for better progress and error handling
|
|
262
|
-
let successCount = 0;
|
|
263
|
-
let failureCount = 0;
|
|
264
|
-
const errors: string[] = [];
|
|
265
|
-
|
|
266
|
-
for (let i = 0; i < selectedFiles.length; i++) {
|
|
267
|
-
const file = selectedFiles[i];
|
|
268
|
-
setUploadProgress({ current: i + 1, total: selectedFiles.length });
|
|
269
|
-
|
|
270
|
-
try {
|
|
271
|
-
await oxyServices.uploadFile(file, file.name, {
|
|
272
|
-
userId: targetUserId,
|
|
273
|
-
uploadDate: new Date().toISOString(),
|
|
274
|
-
});
|
|
275
|
-
successCount++;
|
|
276
|
-
} catch (error: any) {
|
|
277
|
-
failureCount++;
|
|
278
|
-
errors.push(`${file.name}: ${error.message || 'Upload failed'}`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Show results summary
|
|
283
|
-
if (successCount > 0) {
|
|
284
|
-
toast.success(`${successCount} file(s) uploaded successfully`);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (failureCount > 0) {
|
|
288
|
-
const errorMessage = `${failureCount} file(s) failed to upload${errors.length > 0 ? ':\n' + errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : '') : ''}`;
|
|
289
|
-
toast.error(errorMessage);
|
|
260
|
+
let successCount = 0;
|
|
261
|
+
let failureCount = 0;
|
|
262
|
+
const errors: string[] = [];
|
|
263
|
+
for (let i = 0; i < selectedFiles.length; i++) {
|
|
264
|
+
setUploadProgress({ current: i + 1, total: selectedFiles.length });
|
|
265
|
+
try {
|
|
266
|
+
await uploadFileRaw(selectedFiles[i], targetUserId);
|
|
267
|
+
successCount++;
|
|
268
|
+
} catch (error: any) {
|
|
269
|
+
failureCount++;
|
|
270
|
+
errors.push(`${selectedFiles[i].name}: ${error.message || 'Upload failed'}`);
|
|
290
271
|
}
|
|
291
|
-
|
|
292
|
-
// Small delay to ensure backend processing is complete
|
|
293
|
-
setTimeout(async () => {
|
|
294
|
-
await loadFiles();
|
|
295
|
-
}, 500);
|
|
296
272
|
}
|
|
273
|
+
if (successCount > 0) {
|
|
274
|
+
toast.success(`${successCount} file(s) uploaded successfully`);
|
|
275
|
+
}
|
|
276
|
+
if (failureCount > 0) {
|
|
277
|
+
const errorMessage = `${failureCount} file(s) failed to upload${errors.length > 0 ? ':\n' + errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : '') : ''}`;
|
|
278
|
+
toast.error(errorMessage);
|
|
279
|
+
}
|
|
280
|
+
setTimeout(async () => {
|
|
281
|
+
await loadFiles();
|
|
282
|
+
}, 500);
|
|
297
283
|
} catch (error: any) {
|
|
298
284
|
console.error('Upload error:', error);
|
|
299
285
|
toast.error(error.message || 'Failed to upload files');
|
|
@@ -313,7 +299,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
313
299
|
input.type = 'file';
|
|
314
300
|
input.multiple = true;
|
|
315
301
|
input.accept = '*/*';
|
|
316
|
-
|
|
302
|
+
|
|
317
303
|
input.onchange = async (e: any) => {
|
|
318
304
|
const selectedFiles = Array.from(e.target.files) as File[];
|
|
319
305
|
await processFileUploads(selectedFiles);
|
|
@@ -324,7 +310,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
324
310
|
// Mobile - show info that file picker can be added
|
|
325
311
|
const installCommand = 'npm install expo-document-picker';
|
|
326
312
|
const message = `Mobile File Upload\n\nTo enable file uploads on mobile, install expo-document-picker:\n\n${installCommand}\n\nThen import and use DocumentPicker.getDocumentAsync() in this method.`;
|
|
327
|
-
|
|
313
|
+
|
|
328
314
|
if (window.confirm(`${message}\n\nWould you like to copy the install command?`)) {
|
|
329
315
|
toast.info(`Install: ${installCommand}`);
|
|
330
316
|
} else {
|
|
@@ -342,7 +328,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
342
328
|
const handleFileDelete = async (fileId: string, filename: string) => {
|
|
343
329
|
// Use web-compatible confirmation dialog
|
|
344
330
|
const confirmed = window.confirm(`Are you sure you want to delete "${filename}"? This action cannot be undone.`);
|
|
345
|
-
|
|
331
|
+
|
|
346
332
|
if (!confirmed) {
|
|
347
333
|
console.log('Delete cancelled by user');
|
|
348
334
|
return;
|
|
@@ -353,12 +339,12 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
353
339
|
console.log('Target user ID:', targetUserId);
|
|
354
340
|
console.log('Current user ID:', user?.id);
|
|
355
341
|
setDeleting(fileId);
|
|
356
|
-
|
|
342
|
+
|
|
357
343
|
const result = await oxyServices.deleteFile(fileId);
|
|
358
344
|
console.log('Delete result:', result);
|
|
359
|
-
|
|
345
|
+
|
|
360
346
|
toast.success('File deleted successfully');
|
|
361
|
-
|
|
347
|
+
|
|
362
348
|
// Reload files after successful deletion
|
|
363
349
|
setTimeout(async () => {
|
|
364
350
|
await loadFiles();
|
|
@@ -366,7 +352,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
366
352
|
} catch (error: any) {
|
|
367
353
|
console.error('Delete error:', error);
|
|
368
354
|
console.error('Error details:', error.response?.data || error.message);
|
|
369
|
-
|
|
355
|
+
|
|
370
356
|
// Provide specific error messages
|
|
371
357
|
if (error.message?.includes('File not found') || error.message?.includes('404')) {
|
|
372
358
|
toast.error('File not found. It may have already been deleted.');
|
|
@@ -420,11 +406,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
420
406
|
try {
|
|
421
407
|
if (Platform.OS === 'web') {
|
|
422
408
|
console.log('Downloading file:', { fileId, filename });
|
|
423
|
-
|
|
409
|
+
|
|
424
410
|
// Use the public download URL method
|
|
425
411
|
const downloadUrl = oxyServices.getFileDownloadUrl(fileId);
|
|
426
412
|
console.log('Download URL:', downloadUrl);
|
|
427
|
-
|
|
413
|
+
|
|
428
414
|
try {
|
|
429
415
|
// Method 1: Try simple link download first
|
|
430
416
|
const link = document.createElement('a');
|
|
@@ -434,34 +420,25 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
434
420
|
document.body.appendChild(link);
|
|
435
421
|
link.click();
|
|
436
422
|
document.body.removeChild(link);
|
|
437
|
-
|
|
423
|
+
|
|
438
424
|
toast.success('File download started');
|
|
439
425
|
} catch (linkError) {
|
|
440
426
|
console.warn('Link download failed, trying fetch method:', linkError);
|
|
441
|
-
|
|
442
|
-
// Method 2: Fallback to
|
|
443
|
-
const
|
|
444
|
-
if (!response.ok) {
|
|
445
|
-
if (response.status === 404) {
|
|
446
|
-
throw new Error('File not found. It may have been deleted.');
|
|
447
|
-
} else {
|
|
448
|
-
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const blob = await response.blob();
|
|
427
|
+
|
|
428
|
+
// Method 2: Fallback to authenticated download
|
|
429
|
+
const blob = await oxyServices.getFileContentAsBlob(fileId);
|
|
453
430
|
const url = window.URL.createObjectURL(blob);
|
|
454
|
-
|
|
431
|
+
|
|
455
432
|
const link = document.createElement('a');
|
|
456
433
|
link.href = url;
|
|
457
434
|
link.download = filename;
|
|
458
435
|
document.body.appendChild(link);
|
|
459
436
|
link.click();
|
|
460
437
|
document.body.removeChild(link);
|
|
461
|
-
|
|
438
|
+
|
|
462
439
|
// Clean up the blob URL
|
|
463
440
|
window.URL.revokeObjectURL(url);
|
|
464
|
-
|
|
441
|
+
|
|
465
442
|
toast.success('File downloaded successfully');
|
|
466
443
|
}
|
|
467
444
|
} else {
|
|
@@ -496,10 +473,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
496
473
|
try {
|
|
497
474
|
setLoadingFileContent(true);
|
|
498
475
|
setOpenedFile(file);
|
|
499
|
-
|
|
476
|
+
|
|
500
477
|
// For text files, images, and other viewable content, try to load the content
|
|
501
|
-
if (file.contentType.startsWith('text/') ||
|
|
502
|
-
file.contentType.includes('json') ||
|
|
478
|
+
if (file.contentType.startsWith('text/') ||
|
|
479
|
+
file.contentType.includes('json') ||
|
|
503
480
|
file.contentType.includes('xml') ||
|
|
504
481
|
file.contentType.includes('javascript') ||
|
|
505
482
|
file.contentType.includes('typescript') ||
|
|
@@ -507,30 +484,19 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
507
484
|
file.contentType.includes('pdf') ||
|
|
508
485
|
file.contentType.startsWith('video/') ||
|
|
509
486
|
file.contentType.startsWith('audio/')) {
|
|
510
|
-
|
|
487
|
+
|
|
511
488
|
try {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
file.contentType.startsWith('audio/')) {
|
|
520
|
-
// For images, PDFs, videos, and audio, we'll use the URL directly
|
|
521
|
-
setFileContent(downloadUrl);
|
|
522
|
-
} else {
|
|
523
|
-
// For text files, get the content
|
|
524
|
-
const content = await response.text();
|
|
525
|
-
setFileContent(content);
|
|
526
|
-
}
|
|
489
|
+
if (file.contentType.startsWith('image/') ||
|
|
490
|
+
file.contentType.includes('pdf') ||
|
|
491
|
+
file.contentType.startsWith('video/') ||
|
|
492
|
+
file.contentType.startsWith('audio/')) {
|
|
493
|
+
// For images, PDFs, videos, and audio, we'll use the URL directly
|
|
494
|
+
const downloadUrl = oxyServices.getFileDownloadUrl(file.id);
|
|
495
|
+
setFileContent(downloadUrl);
|
|
527
496
|
} else {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
toast.error(`Failed to load file: ${response.status} ${response.statusText}`);
|
|
532
|
-
}
|
|
533
|
-
setFileContent(null);
|
|
497
|
+
// For text files, get the content using authenticated request
|
|
498
|
+
const content = await oxyServices.getFileContentAsText(file.id);
|
|
499
|
+
setFileContent(content);
|
|
534
500
|
}
|
|
535
501
|
} catch (error: any) {
|
|
536
502
|
console.error('Failed to load file content:', error);
|
|
@@ -567,18 +533,18 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
567
533
|
|
|
568
534
|
const renderSimplePhotoItem = useCallback((photo: FileMetadata, index: number) => {
|
|
569
535
|
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
570
|
-
|
|
536
|
+
|
|
571
537
|
// Calculate photo item width based on actual container size from bottom sheet
|
|
572
538
|
let itemsPerRow = 3; // Default for mobile
|
|
573
539
|
if (containerWidth > 768) itemsPerRow = 4; // Desktop/tablet
|
|
574
540
|
else if (containerWidth > 480) itemsPerRow = 3; // Large mobile
|
|
575
|
-
|
|
541
|
+
|
|
576
542
|
// Account for the photoScrollContainer padding (16px on each side = 32px total)
|
|
577
543
|
const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
|
|
578
544
|
const gaps = (itemsPerRow - 1) * 4; // Gap between items (4px)
|
|
579
545
|
const availableWidth = containerWidth - scrollContainerPadding;
|
|
580
546
|
const itemWidth = (availableWidth - gaps) / itemsPerRow;
|
|
581
|
-
|
|
547
|
+
|
|
582
548
|
return (
|
|
583
549
|
<TouchableOpacity
|
|
584
550
|
key={photo.id}
|
|
@@ -633,7 +599,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
633
599
|
|
|
634
600
|
const renderJustifiedPhotoItem = useCallback((photo: FileMetadata, width: number, height: number, isLast: boolean) => {
|
|
635
601
|
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
636
|
-
|
|
602
|
+
|
|
637
603
|
return (
|
|
638
604
|
<TouchableOpacity
|
|
639
605
|
key={photo.id}
|
|
@@ -713,7 +679,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
713
679
|
{/* Preview Thumbnail */}
|
|
714
680
|
<View style={styles.filePreviewContainer}>
|
|
715
681
|
{hasPreview ? (
|
|
716
|
-
<View
|
|
682
|
+
<View
|
|
717
683
|
style={styles.filePreview}
|
|
718
684
|
{...(Platform.OS === 'web' && {
|
|
719
685
|
onMouseEnter: () => setHoveredPreview(file.id),
|
|
@@ -766,7 +732,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
766
732
|
</View>
|
|
767
733
|
)}
|
|
768
734
|
{/* Fallback icon (hidden by default for images) */}
|
|
769
|
-
<View
|
|
735
|
+
<View
|
|
770
736
|
style={[styles.fallbackIcon, { display: isImage ? 'none' : 'flex' }]}
|
|
771
737
|
{...(Platform.OS === 'web' && { 'data-fallback': 'true' })}
|
|
772
738
|
>
|
|
@@ -776,7 +742,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
776
742
|
color={themeStyles.primaryColor}
|
|
777
743
|
/>
|
|
778
744
|
</View>
|
|
779
|
-
|
|
745
|
+
|
|
780
746
|
{/* Preview overlay for hover effect */}
|
|
781
747
|
{Platform.OS === 'web' && hoveredPreview === file.id && isImage && (
|
|
782
748
|
<View style={styles.previewOverlay}>
|
|
@@ -794,7 +760,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
794
760
|
</View>
|
|
795
761
|
)}
|
|
796
762
|
</View>
|
|
797
|
-
|
|
763
|
+
|
|
798
764
|
<View style={styles.fileInfo}>
|
|
799
765
|
<Text style={[styles.fileName, { color: themeStyles.textColor }]} numberOfLines={1}>
|
|
800
766
|
{file.filename}
|
|
@@ -823,7 +789,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
823
789
|
<Ionicons name="eye" size={20} color={themeStyles.primaryColor} />
|
|
824
790
|
</TouchableOpacity>
|
|
825
791
|
)}
|
|
826
|
-
|
|
792
|
+
|
|
827
793
|
<TouchableOpacity
|
|
828
794
|
style={[styles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
|
|
829
795
|
onPress={() => handleFileDownload(file.id, file.filename)}
|
|
@@ -852,18 +818,18 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
852
818
|
|
|
853
819
|
const renderPhotoItem = (photo: FileMetadata, index: number) => {
|
|
854
820
|
const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
|
|
855
|
-
|
|
821
|
+
|
|
856
822
|
// Calculate photo item width based on actual container size from bottom sheet
|
|
857
823
|
let itemsPerRow = 3; // Default for mobile
|
|
858
824
|
if (containerWidth > 768) itemsPerRow = 6; // Tablet/Desktop
|
|
859
825
|
else if (containerWidth > 480) itemsPerRow = 4; // Large mobile
|
|
860
|
-
|
|
826
|
+
|
|
861
827
|
// Account for the photoScrollContainer padding (16px on each side = 32px total)
|
|
862
828
|
const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
|
|
863
829
|
const gaps = (itemsPerRow - 1) * 4; // Gap between items
|
|
864
830
|
const availableWidth = containerWidth - scrollContainerPadding;
|
|
865
831
|
const itemWidth = (availableWidth - gaps) / itemsPerRow;
|
|
866
|
-
|
|
832
|
+
|
|
867
833
|
return (
|
|
868
834
|
<TouchableOpacity
|
|
869
835
|
key={photo.id}
|
|
@@ -918,14 +884,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
918
884
|
|
|
919
885
|
const renderPhotoGrid = useCallback(() => {
|
|
920
886
|
const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
|
|
921
|
-
|
|
887
|
+
|
|
922
888
|
if (photos.length === 0) {
|
|
923
889
|
return (
|
|
924
890
|
<View style={styles.emptyState}>
|
|
925
891
|
<Ionicons name="images-outline" size={64} color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'} />
|
|
926
892
|
<Text style={[styles.emptyStateTitle, { color: themeStyles.textColor }]}>No Photos Yet</Text>
|
|
927
893
|
<Text style={[styles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
928
|
-
{user?.id === targetUserId
|
|
894
|
+
{user?.id === targetUserId
|
|
929
895
|
? `Upload photos to get started. You can select multiple photos at once${Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}`
|
|
930
896
|
: "This user hasn't uploaded any photos yet"
|
|
931
897
|
}
|
|
@@ -971,8 +937,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
971
937
|
</Text>
|
|
972
938
|
</View>
|
|
973
939
|
)}
|
|
974
|
-
|
|
975
|
-
<JustifiedPhotoGrid
|
|
940
|
+
|
|
941
|
+
<JustifiedPhotoGrid
|
|
976
942
|
photos={photos}
|
|
977
943
|
photoDimensions={photoDimensions}
|
|
978
944
|
loadPhotoDimensions={loadPhotoDimensions}
|
|
@@ -985,36 +951,36 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
985
951
|
</ScrollView>
|
|
986
952
|
);
|
|
987
953
|
}, [
|
|
988
|
-
filteredFiles,
|
|
989
|
-
themeStyles,
|
|
990
|
-
user?.id,
|
|
991
|
-
targetUserId,
|
|
992
|
-
uploading,
|
|
993
|
-
handleFileUpload,
|
|
994
|
-
refreshing,
|
|
995
|
-
loadFiles,
|
|
996
|
-
loadingDimensions,
|
|
997
|
-
photoDimensions,
|
|
998
|
-
loadPhotoDimensions,
|
|
999
|
-
createJustifiedRows,
|
|
1000
|
-
renderJustifiedPhotoItem,
|
|
1001
|
-
renderPhotoItem,
|
|
954
|
+
filteredFiles,
|
|
955
|
+
themeStyles,
|
|
956
|
+
user?.id,
|
|
957
|
+
targetUserId,
|
|
958
|
+
uploading,
|
|
959
|
+
handleFileUpload,
|
|
960
|
+
refreshing,
|
|
961
|
+
loadFiles,
|
|
962
|
+
loadingDimensions,
|
|
963
|
+
photoDimensions,
|
|
964
|
+
loadPhotoDimensions,
|
|
965
|
+
createJustifiedRows,
|
|
966
|
+
renderJustifiedPhotoItem,
|
|
967
|
+
renderPhotoItem,
|
|
1002
968
|
containerWidth
|
|
1003
969
|
]);
|
|
1004
970
|
|
|
1005
971
|
// Separate component for the photo grid to optimize rendering
|
|
1006
|
-
const JustifiedPhotoGrid = React.memo(({
|
|
1007
|
-
photos,
|
|
1008
|
-
photoDimensions,
|
|
1009
|
-
loadPhotoDimensions,
|
|
1010
|
-
createJustifiedRows,
|
|
1011
|
-
renderJustifiedPhotoItem,
|
|
1012
|
-
renderSimplePhotoItem,
|
|
972
|
+
const JustifiedPhotoGrid = React.memo(({
|
|
973
|
+
photos,
|
|
974
|
+
photoDimensions,
|
|
975
|
+
loadPhotoDimensions,
|
|
976
|
+
createJustifiedRows,
|
|
977
|
+
renderJustifiedPhotoItem,
|
|
978
|
+
renderSimplePhotoItem,
|
|
1013
979
|
textColor,
|
|
1014
980
|
containerWidth
|
|
1015
981
|
}: {
|
|
1016
982
|
photos: FileMetadata[];
|
|
1017
|
-
photoDimensions: {[key: string]: {width: number, height: number}};
|
|
983
|
+
photoDimensions: { [key: string]: { width: number, height: number } };
|
|
1018
984
|
loadPhotoDimensions: (photos: FileMetadata[]) => Promise<void>;
|
|
1019
985
|
createJustifiedRows: (photos: FileMetadata[], containerWidth: number) => FileMetadata[][];
|
|
1020
986
|
renderJustifiedPhotoItem: (photo: FileMetadata, width: number, height: number, isLast: boolean) => JSX.Element;
|
|
@@ -1029,7 +995,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1029
995
|
|
|
1030
996
|
// Group photos by date
|
|
1031
997
|
const photosByDate = React.useMemo(() => {
|
|
1032
|
-
return photos.reduce((groups: {[key: string]: FileMetadata[]}, photo) => {
|
|
998
|
+
return photos.reduce((groups: { [key: string]: FileMetadata[] }, photo) => {
|
|
1033
999
|
const date = new Date(photo.uploadDate).toDateString();
|
|
1034
1000
|
if (!groups[date]) {
|
|
1035
1001
|
groups[date] = [];
|
|
@@ -1040,7 +1006,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1040
1006
|
}, [photos]);
|
|
1041
1007
|
|
|
1042
1008
|
const sortedDates = React.useMemo(() => {
|
|
1043
|
-
return Object.keys(photosByDate).sort((a, b) =>
|
|
1009
|
+
return Object.keys(photosByDate).sort((a, b) =>
|
|
1044
1010
|
new Date(b).getTime() - new Date(a).getTime()
|
|
1045
1011
|
);
|
|
1046
1012
|
}, [photosByDate]);
|
|
@@ -1050,15 +1016,15 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1050
1016
|
{sortedDates.map(date => {
|
|
1051
1017
|
const dayPhotos = photosByDate[date];
|
|
1052
1018
|
const justifiedRows = createJustifiedRows(dayPhotos, containerWidth);
|
|
1053
|
-
|
|
1019
|
+
|
|
1054
1020
|
return (
|
|
1055
1021
|
<View key={date} style={styles.photoDateSection}>
|
|
1056
1022
|
<Text style={[styles.photoDateHeader, { color: themeStyles.textColor }]}>
|
|
1057
|
-
{new Date(date).toLocaleDateString('en-US', {
|
|
1058
|
-
weekday: 'long',
|
|
1059
|
-
year: 'numeric',
|
|
1060
|
-
month: 'long',
|
|
1061
|
-
day: 'numeric'
|
|
1023
|
+
{new Date(date).toLocaleDateString('en-US', {
|
|
1024
|
+
weekday: 'long',
|
|
1025
|
+
year: 'numeric',
|
|
1026
|
+
month: 'long',
|
|
1027
|
+
day: 'numeric'
|
|
1062
1028
|
})}
|
|
1063
1029
|
</Text>
|
|
1064
1030
|
<View style={styles.justifiedPhotoGrid}>
|
|
@@ -1066,31 +1032,31 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1066
1032
|
// Calculate row height based on available width
|
|
1067
1033
|
const gap = 4;
|
|
1068
1034
|
let totalAspectRatio = 0;
|
|
1069
|
-
|
|
1035
|
+
|
|
1070
1036
|
// Calculate total aspect ratio for this row
|
|
1071
1037
|
row.forEach(photo => {
|
|
1072
1038
|
const dimensions = photoDimensions[photo.id];
|
|
1073
|
-
const aspectRatio = dimensions ?
|
|
1074
|
-
(dimensions.width / dimensions.height) :
|
|
1039
|
+
const aspectRatio = dimensions ?
|
|
1040
|
+
(dimensions.width / dimensions.height) :
|
|
1075
1041
|
1.33; // Default 4:3 ratio
|
|
1076
1042
|
totalAspectRatio += aspectRatio;
|
|
1077
1043
|
});
|
|
1078
|
-
|
|
1044
|
+
|
|
1079
1045
|
// Calculate the height that makes the row fill the available width
|
|
1080
1046
|
// Account for photoScrollContainer padding (32px total) and gaps between photos
|
|
1081
1047
|
const scrollContainerPadding = 32;
|
|
1082
1048
|
const availableWidth = containerWidth - scrollContainerPadding - (gap * (row.length - 1));
|
|
1083
1049
|
const calculatedHeight = availableWidth / totalAspectRatio;
|
|
1084
|
-
|
|
1050
|
+
|
|
1085
1051
|
// Clamp height for visual consistency
|
|
1086
1052
|
const rowHeight = Math.max(120, Math.min(calculatedHeight, 300));
|
|
1087
|
-
|
|
1053
|
+
|
|
1088
1054
|
return (
|
|
1089
|
-
<View
|
|
1090
|
-
key={`row-${rowIndex}`}
|
|
1055
|
+
<View
|
|
1056
|
+
key={`row-${rowIndex}`}
|
|
1091
1057
|
style={[
|
|
1092
|
-
styles.justifiedPhotoRow,
|
|
1093
|
-
{
|
|
1058
|
+
styles.justifiedPhotoRow,
|
|
1059
|
+
{
|
|
1094
1060
|
height: rowHeight,
|
|
1095
1061
|
maxWidth: containerWidth - 32, // Account for scroll container padding
|
|
1096
1062
|
gap: 4, // Add horizontal gap between photos in row
|
|
@@ -1099,17 +1065,17 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1099
1065
|
>
|
|
1100
1066
|
{row.map((photo, photoIndex) => {
|
|
1101
1067
|
const dimensions = photoDimensions[photo.id];
|
|
1102
|
-
const aspectRatio = dimensions ?
|
|
1103
|
-
(dimensions.width / dimensions.height) :
|
|
1068
|
+
const aspectRatio = dimensions ?
|
|
1069
|
+
(dimensions.width / dimensions.height) :
|
|
1104
1070
|
1.33; // Default 4:3 ratio
|
|
1105
|
-
|
|
1071
|
+
|
|
1106
1072
|
const photoWidth = rowHeight * aspectRatio;
|
|
1107
1073
|
const isLast = photoIndex === row.length - 1;
|
|
1108
|
-
|
|
1074
|
+
|
|
1109
1075
|
return renderJustifiedPhotoItem(
|
|
1110
|
-
photo,
|
|
1111
|
-
photoWidth,
|
|
1112
|
-
rowHeight,
|
|
1076
|
+
photo,
|
|
1077
|
+
photoWidth,
|
|
1078
|
+
rowHeight,
|
|
1113
1079
|
isLast
|
|
1114
1080
|
);
|
|
1115
1081
|
})}
|
|
@@ -1127,7 +1093,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1127
1093
|
const renderFileDetailsModal = () => {
|
|
1128
1094
|
const backgroundColor = themeStyles.backgroundColor;
|
|
1129
1095
|
const borderColor = themeStyles.borderColor;
|
|
1130
|
-
|
|
1096
|
+
|
|
1131
1097
|
return (
|
|
1132
1098
|
<Modal
|
|
1133
1099
|
visible={showFileDetails}
|
|
@@ -1141,97 +1107,97 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1141
1107
|
style={styles.modalCloseButton}
|
|
1142
1108
|
onPress={() => setShowFileDetails(false)}
|
|
1143
1109
|
>
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
{selectedFile && (
|
|
1151
|
-
<ScrollView style={styles.modalContent}>
|
|
1152
|
-
<View style={[styles.fileDetailCard, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }]}>
|
|
1153
|
-
<View style={styles.fileDetailIcon}>
|
|
1154
|
-
<Ionicons
|
|
1155
|
-
name={getFileIcon(selectedFile.contentType) as any}
|
|
1156
|
-
size={64}
|
|
1157
|
-
color={themeStyles.primaryColor}
|
|
1158
|
-
/>
|
|
1159
|
-
</View>
|
|
1160
|
-
|
|
1161
|
-
<Text style={[styles.fileDetailName, { color: themeStyles.textColor }]}>
|
|
1162
|
-
{selectedFile.filename}
|
|
1163
|
-
</Text>
|
|
1110
|
+
<Ionicons name="close" size={24} color={themeStyles.textColor} />
|
|
1111
|
+
</TouchableOpacity>
|
|
1112
|
+
<Text style={[styles.modalTitle, { color: themeStyles.textColor }]}>File Details</Text>
|
|
1113
|
+
<View style={styles.modalPlaceholder} />
|
|
1114
|
+
</View>
|
|
1164
1115
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
{
|
|
1172
|
-
|
|
1116
|
+
{selectedFile && (
|
|
1117
|
+
<ScrollView style={styles.modalContent}>
|
|
1118
|
+
<View style={[styles.fileDetailCard, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }]}>
|
|
1119
|
+
<View style={styles.fileDetailIcon}>
|
|
1120
|
+
<Ionicons
|
|
1121
|
+
name={getFileIcon(selectedFile.contentType) as any}
|
|
1122
|
+
size={64}
|
|
1123
|
+
color={themeStyles.primaryColor}
|
|
1124
|
+
/>
|
|
1173
1125
|
</View>
|
|
1174
1126
|
|
|
1175
|
-
<
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
</Text>
|
|
1179
|
-
<Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
|
|
1180
|
-
{selectedFile.contentType}
|
|
1181
|
-
</Text>
|
|
1182
|
-
</View>
|
|
1127
|
+
<Text style={[styles.fileDetailName, { color: themeStyles.textColor }]}>
|
|
1128
|
+
{selectedFile.filename}
|
|
1129
|
+
</Text>
|
|
1183
1130
|
|
|
1184
|
-
<View style={styles.
|
|
1185
|
-
<
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
{
|
|
1190
|
-
|
|
1191
|
-
|
|
1131
|
+
<View style={styles.fileDetailInfo}>
|
|
1132
|
+
<View style={styles.detailRow}>
|
|
1133
|
+
<Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
1134
|
+
Size:
|
|
1135
|
+
</Text>
|
|
1136
|
+
<Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
|
|
1137
|
+
{formatFileSize(selectedFile.length)}
|
|
1138
|
+
</Text>
|
|
1139
|
+
</View>
|
|
1192
1140
|
|
|
1193
|
-
{selectedFile.metadata?.description && (
|
|
1194
1141
|
<View style={styles.detailRow}>
|
|
1195
1142
|
<Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
1196
|
-
|
|
1143
|
+
Type:
|
|
1197
1144
|
</Text>
|
|
1198
1145
|
<Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
|
|
1199
|
-
{selectedFile.
|
|
1146
|
+
{selectedFile.contentType}
|
|
1200
1147
|
</Text>
|
|
1201
1148
|
</View>
|
|
1202
|
-
)}
|
|
1203
|
-
</View>
|
|
1204
1149
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
<Ionicons name="download" size={20} color="#FFFFFF" />
|
|
1214
|
-
<Text style={styles.modalActionText}>Download</Text>
|
|
1215
|
-
</TouchableOpacity>
|
|
1150
|
+
<View style={styles.detailRow}>
|
|
1151
|
+
<Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
1152
|
+
Uploaded:
|
|
1153
|
+
</Text>
|
|
1154
|
+
<Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
|
|
1155
|
+
{new Date(selectedFile.uploadDate).toLocaleString()}
|
|
1156
|
+
</Text>
|
|
1157
|
+
</View>
|
|
1216
1158
|
|
|
1217
|
-
|
|
1159
|
+
{selectedFile.metadata?.description && (
|
|
1160
|
+
<View style={styles.detailRow}>
|
|
1161
|
+
<Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
1162
|
+
Description:
|
|
1163
|
+
</Text>
|
|
1164
|
+
<Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
|
|
1165
|
+
{selectedFile.metadata.description}
|
|
1166
|
+
</Text>
|
|
1167
|
+
</View>
|
|
1168
|
+
)}
|
|
1169
|
+
</View>
|
|
1170
|
+
|
|
1171
|
+
<View style={styles.modalActions}>
|
|
1218
1172
|
<TouchableOpacity
|
|
1219
|
-
style={[styles.modalActionButton, { backgroundColor: themeStyles.
|
|
1173
|
+
style={[styles.modalActionButton, { backgroundColor: themeStyles.primaryColor }]}
|
|
1220
1174
|
onPress={() => {
|
|
1175
|
+
handleFileDownload(selectedFile.id, selectedFile.filename);
|
|
1221
1176
|
setShowFileDetails(false);
|
|
1222
|
-
handleFileDelete(selectedFile.id, selectedFile.filename);
|
|
1223
1177
|
}}
|
|
1224
1178
|
>
|
|
1225
|
-
<Ionicons name="
|
|
1226
|
-
<Text style={styles.modalActionText}>
|
|
1179
|
+
<Ionicons name="download" size={20} color="#FFFFFF" />
|
|
1180
|
+
<Text style={styles.modalActionText}>Download</Text>
|
|
1227
1181
|
</TouchableOpacity>
|
|
1228
|
-
|
|
1182
|
+
|
|
1183
|
+
{(user?.id === targetUserId) && (
|
|
1184
|
+
<TouchableOpacity
|
|
1185
|
+
style={[styles.modalActionButton, { backgroundColor: themeStyles.dangerColor }]}
|
|
1186
|
+
onPress={() => {
|
|
1187
|
+
setShowFileDetails(false);
|
|
1188
|
+
handleFileDelete(selectedFile.id, selectedFile.filename);
|
|
1189
|
+
}}
|
|
1190
|
+
>
|
|
1191
|
+
<Ionicons name="trash" size={20} color="#FFFFFF" />
|
|
1192
|
+
<Text style={styles.modalActionText}>Delete</Text>
|
|
1193
|
+
</TouchableOpacity>
|
|
1194
|
+
)}
|
|
1195
|
+
</View>
|
|
1229
1196
|
</View>
|
|
1230
|
-
</
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
</
|
|
1234
|
-
</Modal>
|
|
1197
|
+
</ScrollView>
|
|
1198
|
+
)}
|
|
1199
|
+
</View>
|
|
1200
|
+
</Modal>
|
|
1235
1201
|
);
|
|
1236
1202
|
};
|
|
1237
1203
|
|
|
@@ -1242,11 +1208,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1242
1208
|
const borderColor = themeStyles.borderColor;
|
|
1243
1209
|
|
|
1244
1210
|
const isImage = openedFile.contentType.startsWith('image/');
|
|
1245
|
-
const isText = openedFile.contentType.startsWith('text/') ||
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1211
|
+
const isText = openedFile.contentType.startsWith('text/') ||
|
|
1212
|
+
openedFile.contentType.includes('json') ||
|
|
1213
|
+
openedFile.contentType.includes('xml') ||
|
|
1214
|
+
openedFile.contentType.includes('javascript') ||
|
|
1215
|
+
openedFile.contentType.includes('typescript');
|
|
1250
1216
|
const isPDF = openedFile.contentType.includes('pdf');
|
|
1251
1217
|
const isVideo = openedFile.contentType.startsWith('video/');
|
|
1252
1218
|
const isAudio = openedFile.contentType.startsWith('audio/');
|
|
@@ -1278,19 +1244,19 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1278
1244
|
</TouchableOpacity>
|
|
1279
1245
|
<TouchableOpacity
|
|
1280
1246
|
style={[
|
|
1281
|
-
styles.actionButton,
|
|
1282
|
-
{
|
|
1283
|
-
backgroundColor: showFileDetailsInViewer
|
|
1284
|
-
? themeStyles.primaryColor
|
|
1285
|
-
: (themeStyles.isDarkTheme ? '#333333' : '#F0F0F0')
|
|
1247
|
+
styles.actionButton,
|
|
1248
|
+
{
|
|
1249
|
+
backgroundColor: showFileDetailsInViewer
|
|
1250
|
+
? themeStyles.primaryColor
|
|
1251
|
+
: (themeStyles.isDarkTheme ? '#333333' : '#F0F0F0')
|
|
1286
1252
|
}
|
|
1287
1253
|
]}
|
|
1288
1254
|
onPress={() => setShowFileDetailsInViewer(!showFileDetailsInViewer)}
|
|
1289
1255
|
>
|
|
1290
|
-
<Ionicons
|
|
1291
|
-
name={showFileDetailsInViewer ? "chevron-up" : "information-circle"}
|
|
1292
|
-
size={20}
|
|
1293
|
-
color={showFileDetailsInViewer ? "#FFFFFF" : themeStyles.primaryColor}
|
|
1256
|
+
<Ionicons
|
|
1257
|
+
name={showFileDetailsInViewer ? "chevron-up" : "information-circle"}
|
|
1258
|
+
size={20}
|
|
1259
|
+
color={showFileDetailsInViewer ? "#FFFFFF" : themeStyles.primaryColor}
|
|
1294
1260
|
/>
|
|
1295
1261
|
</TouchableOpacity>
|
|
1296
1262
|
</View>
|
|
@@ -1310,7 +1276,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1310
1276
|
<Ionicons name="chevron-up" size={20} color={themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'} />
|
|
1311
1277
|
</TouchableOpacity>
|
|
1312
1278
|
</View>
|
|
1313
|
-
|
|
1279
|
+
|
|
1314
1280
|
<View style={styles.fileDetailInfo}>
|
|
1315
1281
|
<View style={styles.detailRow}>
|
|
1316
1282
|
<Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
@@ -1395,11 +1361,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1395
1361
|
)}
|
|
1396
1362
|
|
|
1397
1363
|
{/* File Content */}
|
|
1398
|
-
<ScrollView
|
|
1364
|
+
<ScrollView
|
|
1399
1365
|
style={[
|
|
1400
1366
|
styles.fileViewerContent,
|
|
1401
1367
|
showFileDetailsInViewer && styles.fileViewerContentWithDetails
|
|
1402
|
-
]}
|
|
1368
|
+
]}
|
|
1403
1369
|
contentContainerStyle={styles.fileViewerContentContainer}
|
|
1404
1370
|
>
|
|
1405
1371
|
{loadingFileContent ? (
|
|
@@ -1412,8 +1378,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1412
1378
|
) : isImage && fileContent ? (
|
|
1413
1379
|
<View style={styles.imageContainer}>
|
|
1414
1380
|
{Platform.OS === 'web' ? (
|
|
1415
|
-
<img
|
|
1416
|
-
src={fileContent}
|
|
1381
|
+
<img
|
|
1382
|
+
src={fileContent}
|
|
1417
1383
|
alt={openedFile.filename}
|
|
1418
1384
|
style={{
|
|
1419
1385
|
maxWidth: '100%',
|
|
@@ -1499,10 +1465,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1499
1465
|
</View>
|
|
1500
1466
|
) : (
|
|
1501
1467
|
<View style={styles.unsupportedFileContainer}>
|
|
1502
|
-
<Ionicons
|
|
1503
|
-
name={getFileIcon(openedFile.contentType) as any}
|
|
1504
|
-
size={64}
|
|
1505
|
-
color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'}
|
|
1468
|
+
<Ionicons
|
|
1469
|
+
name={getFileIcon(openedFile.contentType) as any}
|
|
1470
|
+
size={64}
|
|
1471
|
+
color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'}
|
|
1506
1472
|
/>
|
|
1507
1473
|
<Text style={[styles.unsupportedFileTitle, { color: themeStyles.textColor }]}>
|
|
1508
1474
|
Preview Not Available
|
|
@@ -1530,7 +1496,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1530
1496
|
<Ionicons name="folder-open-outline" size={64} color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'} />
|
|
1531
1497
|
<Text style={[styles.emptyStateTitle, { color: themeStyles.textColor }]}>No Files Yet</Text>
|
|
1532
1498
|
<Text style={[styles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
1533
|
-
{user?.id === targetUserId
|
|
1499
|
+
{user?.id === targetUserId
|
|
1534
1500
|
? `Upload files to get started. You can select multiple files at once${Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}`
|
|
1535
1501
|
: "This user hasn't uploaded any files yet"
|
|
1536
1502
|
}
|
|
@@ -1574,9 +1540,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1574
1540
|
}
|
|
1575
1541
|
|
|
1576
1542
|
return (
|
|
1577
|
-
<View
|
|
1543
|
+
<View
|
|
1578
1544
|
style={[
|
|
1579
|
-
styles.container,
|
|
1545
|
+
styles.container,
|
|
1580
1546
|
{ backgroundColor },
|
|
1581
1547
|
isDragging && Platform.OS === 'web' && styles.dragOverlay
|
|
1582
1548
|
]}
|
|
@@ -1588,8 +1554,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1588
1554
|
>
|
|
1589
1555
|
{/* Header */}
|
|
1590
1556
|
<View style={[
|
|
1591
|
-
styles.header,
|
|
1592
|
-
{
|
|
1557
|
+
styles.header,
|
|
1558
|
+
{
|
|
1593
1559
|
borderBottomColor: borderColor,
|
|
1594
1560
|
backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
|
|
1595
1561
|
shadowColor: '#000000',
|
|
@@ -1600,21 +1566,27 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1600
1566
|
shadowOpacity: themeStyles.isDarkTheme ? 0.3 : 0.1,
|
|
1601
1567
|
shadowRadius: 8,
|
|
1602
1568
|
elevation: 4,
|
|
1569
|
+
...Platform.select({
|
|
1570
|
+
web: {
|
|
1571
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
|
1572
|
+
},
|
|
1573
|
+
default: {},
|
|
1574
|
+
}),
|
|
1603
1575
|
}
|
|
1604
1576
|
]}>
|
|
1605
|
-
<TouchableOpacity
|
|
1577
|
+
<TouchableOpacity
|
|
1606
1578
|
style={[
|
|
1607
|
-
styles.backButton,
|
|
1608
|
-
{
|
|
1579
|
+
styles.backButton,
|
|
1580
|
+
{
|
|
1609
1581
|
backgroundColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#F8F9FA',
|
|
1610
1582
|
borderRadius: 12,
|
|
1611
1583
|
}
|
|
1612
|
-
]}
|
|
1584
|
+
]}
|
|
1613
1585
|
onPress={onClose || goBack}
|
|
1614
1586
|
>
|
|
1615
1587
|
<Ionicons name="arrow-back" size={22} color={themeStyles.textColor} />
|
|
1616
1588
|
</TouchableOpacity>
|
|
1617
|
-
|
|
1589
|
+
|
|
1618
1590
|
<View style={styles.headerTitleContainer}>
|
|
1619
1591
|
<Text style={[styles.headerTitle, { color: themeStyles.textColor }]}>
|
|
1620
1592
|
{viewMode === 'photos' ? 'Photos' : 'File Management'}
|
|
@@ -1623,12 +1595,12 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1623
1595
|
{filteredFiles.length} {filteredFiles.length === 1 ? 'item' : 'items'}
|
|
1624
1596
|
</Text>
|
|
1625
1597
|
</View>
|
|
1626
|
-
|
|
1598
|
+
|
|
1627
1599
|
<View style={styles.headerActions}>
|
|
1628
1600
|
{/* View Mode Toggle */}
|
|
1629
1601
|
<View style={[
|
|
1630
|
-
styles.viewModeToggle,
|
|
1631
|
-
{
|
|
1602
|
+
styles.viewModeToggle,
|
|
1603
|
+
{
|
|
1632
1604
|
backgroundColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#F8F9FA',
|
|
1633
1605
|
borderWidth: 1,
|
|
1634
1606
|
borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
|
|
@@ -1645,7 +1617,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1645
1617
|
<TouchableOpacity
|
|
1646
1618
|
style={[
|
|
1647
1619
|
styles.viewModeButton,
|
|
1648
|
-
viewMode === 'all' && {
|
|
1620
|
+
viewMode === 'all' && {
|
|
1649
1621
|
backgroundColor: themeStyles.primaryColor,
|
|
1650
1622
|
shadowColor: themeStyles.primaryColor,
|
|
1651
1623
|
shadowOffset: {
|
|
@@ -1659,16 +1631,16 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1659
1631
|
]}
|
|
1660
1632
|
onPress={() => setViewMode('all')}
|
|
1661
1633
|
>
|
|
1662
|
-
<Ionicons
|
|
1663
|
-
name="folder"
|
|
1664
|
-
size={18}
|
|
1665
|
-
color={viewMode === 'all' ? '#FFFFFF' : themeStyles.textColor}
|
|
1634
|
+
<Ionicons
|
|
1635
|
+
name="folder"
|
|
1636
|
+
size={18}
|
|
1637
|
+
color={viewMode === 'all' ? '#FFFFFF' : themeStyles.textColor}
|
|
1666
1638
|
/>
|
|
1667
1639
|
</TouchableOpacity>
|
|
1668
1640
|
<TouchableOpacity
|
|
1669
1641
|
style={[
|
|
1670
1642
|
styles.viewModeButton,
|
|
1671
|
-
viewMode === 'photos' && {
|
|
1643
|
+
viewMode === 'photos' && {
|
|
1672
1644
|
backgroundColor: themeStyles.primaryColor,
|
|
1673
1645
|
shadowColor: themeStyles.primaryColor,
|
|
1674
1646
|
shadowOffset: {
|
|
@@ -1682,19 +1654,19 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1682
1654
|
]}
|
|
1683
1655
|
onPress={() => setViewMode('photos')}
|
|
1684
1656
|
>
|
|
1685
|
-
<Ionicons
|
|
1686
|
-
name="images"
|
|
1687
|
-
size={18}
|
|
1688
|
-
color={viewMode === 'photos' ? '#FFFFFF' : themeStyles.textColor}
|
|
1657
|
+
<Ionicons
|
|
1658
|
+
name="images"
|
|
1659
|
+
size={18}
|
|
1660
|
+
color={viewMode === 'photos' ? '#FFFFFF' : themeStyles.textColor}
|
|
1689
1661
|
/>
|
|
1690
1662
|
</TouchableOpacity>
|
|
1691
1663
|
</View>
|
|
1692
|
-
|
|
1664
|
+
|
|
1693
1665
|
{user?.id === targetUserId && (
|
|
1694
1666
|
<TouchableOpacity
|
|
1695
1667
|
style={[
|
|
1696
|
-
styles.uploadButton,
|
|
1697
|
-
{
|
|
1668
|
+
styles.uploadButton,
|
|
1669
|
+
{
|
|
1698
1670
|
backgroundColor: themeStyles.primaryColor,
|
|
1699
1671
|
shadowColor: themeStyles.primaryColor,
|
|
1700
1672
|
shadowOffset: {
|
|
@@ -1730,9 +1702,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1730
1702
|
{/* Search Bar */}
|
|
1731
1703
|
{files.length > 0 && (viewMode === 'all' || files.some(f => f.contentType.startsWith('image/'))) && (
|
|
1732
1704
|
<View style={[
|
|
1733
|
-
styles.searchContainer,
|
|
1734
|
-
{
|
|
1735
|
-
backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
|
|
1705
|
+
styles.searchContainer,
|
|
1706
|
+
{
|
|
1707
|
+
backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
|
|
1736
1708
|
borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
|
|
1737
1709
|
shadowColor: '#000000',
|
|
1738
1710
|
shadowOffset: {
|
|
@@ -1753,7 +1725,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1753
1725
|
onChangeText={setSearchQuery}
|
|
1754
1726
|
/>
|
|
1755
1727
|
{searchQuery.length > 0 && (
|
|
1756
|
-
<TouchableOpacity
|
|
1728
|
+
<TouchableOpacity
|
|
1757
1729
|
onPress={() => setSearchQuery('')}
|
|
1758
1730
|
style={styles.searchClearButton}
|
|
1759
1731
|
>
|
|
@@ -1766,9 +1738,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1766
1738
|
{/* File Stats */}
|
|
1767
1739
|
{files.length > 0 && (
|
|
1768
1740
|
<View style={[
|
|
1769
|
-
styles.statsContainer,
|
|
1770
|
-
{
|
|
1771
|
-
backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
|
|
1741
|
+
styles.statsContainer,
|
|
1742
|
+
{
|
|
1743
|
+
backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
|
|
1772
1744
|
borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
|
|
1773
1745
|
shadowColor: '#000000',
|
|
1774
1746
|
shadowOffset: {
|
|
@@ -2139,7 +2111,7 @@ const styles = StyleSheet.create({
|
|
|
2139
2111
|
fontSize: 16,
|
|
2140
2112
|
marginTop: 16,
|
|
2141
2113
|
},
|
|
2142
|
-
|
|
2114
|
+
|
|
2143
2115
|
// Modal styles
|
|
2144
2116
|
modalContainer: {
|
|
2145
2117
|
flex: 1,
|
|
@@ -2224,7 +2196,7 @@ const styles = StyleSheet.create({
|
|
|
2224
2196
|
fontSize: 16,
|
|
2225
2197
|
fontWeight: '600',
|
|
2226
2198
|
},
|
|
2227
|
-
|
|
2199
|
+
|
|
2228
2200
|
// Drag and Drop styles
|
|
2229
2201
|
dragDropOverlay: {
|
|
2230
2202
|
position: 'absolute',
|
|
@@ -2256,7 +2228,7 @@ const styles = StyleSheet.create({
|
|
|
2256
2228
|
fontSize: 16,
|
|
2257
2229
|
textAlign: 'center',
|
|
2258
2230
|
},
|
|
2259
|
-
|
|
2231
|
+
|
|
2260
2232
|
// File Viewer styles
|
|
2261
2233
|
fileViewerContainer: {
|
|
2262
2234
|
flex: 1,
|
|
@@ -2372,7 +2344,7 @@ const styles = StyleSheet.create({
|
|
|
2372
2344
|
textAlign: 'center',
|
|
2373
2345
|
fontStyle: 'italic',
|
|
2374
2346
|
},
|
|
2375
|
-
|
|
2347
|
+
|
|
2376
2348
|
// File Details in Viewer styles
|
|
2377
2349
|
fileDetailsSection: {
|
|
2378
2350
|
margin: 16,
|
|
@@ -2415,7 +2387,7 @@ const styles = StyleSheet.create({
|
|
|
2415
2387
|
fontSize: 14,
|
|
2416
2388
|
fontWeight: '600',
|
|
2417
2389
|
},
|
|
2418
|
-
|
|
2390
|
+
|
|
2419
2391
|
// Header styles
|
|
2420
2392
|
headerActions: {
|
|
2421
2393
|
flexDirection: 'row',
|
|
@@ -2437,7 +2409,7 @@ const styles = StyleSheet.create({
|
|
|
2437
2409
|
justifyContent: 'center',
|
|
2438
2410
|
marginHorizontal: 1,
|
|
2439
2411
|
},
|
|
2440
|
-
|
|
2412
|
+
|
|
2441
2413
|
// Photo Grid styles
|
|
2442
2414
|
photoScrollContainer: {
|
|
2443
2415
|
padding: 16,
|
|
@@ -2473,7 +2445,7 @@ const styles = StyleSheet.create({
|
|
|
2473
2445
|
width: '100%',
|
|
2474
2446
|
height: '100%',
|
|
2475
2447
|
},
|
|
2476
|
-
|
|
2448
|
+
|
|
2477
2449
|
// Justified Grid styles
|
|
2478
2450
|
dimensionsLoadingIndicator: {
|
|
2479
2451
|
flexDirection: 'row',
|
|
@@ -2510,7 +2482,7 @@ const styles = StyleSheet.create({
|
|
|
2510
2482
|
height: '100%',
|
|
2511
2483
|
borderRadius: 6,
|
|
2512
2484
|
},
|
|
2513
|
-
|
|
2485
|
+
|
|
2514
2486
|
// Simple Photo Grid styles
|
|
2515
2487
|
simplePhotoItem: {
|
|
2516
2488
|
borderRadius: 8,
|
|
@@ -2529,7 +2501,7 @@ const styles = StyleSheet.create({
|
|
|
2529
2501
|
height: '100%',
|
|
2530
2502
|
borderRadius: 8,
|
|
2531
2503
|
},
|
|
2532
|
-
|
|
2504
|
+
|
|
2533
2505
|
// Loading skeleton styles
|
|
2534
2506
|
photoSkeletonGrid: {
|
|
2535
2507
|
flexDirection: 'row',
|