@oxyhq/services 5.14.0 → 5.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1157) hide show
  1. package/README.md +130 -54
  2. package/lib/commonjs/assets/assets/lottie/welcomeheader_background_op1.json +1 -0
  3. package/lib/commonjs/assets/lottie/welcomeheader_background_op1.json +1 -0
  4. package/lib/commonjs/core/HttpService.js +94 -4
  5. package/lib/commonjs/core/HttpService.js.map +1 -1
  6. package/lib/commonjs/core/OxyServices.base.js +3 -1
  7. package/lib/commonjs/core/OxyServices.base.js.map +1 -1
  8. package/lib/commonjs/core/mixins/OxyServices.assets.js +213 -22
  9. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -1
  10. package/lib/commonjs/core/mixins/OxyServices.auth.js +70 -99
  11. package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -1
  12. package/lib/commonjs/crypto/index.js +33 -0
  13. package/lib/commonjs/crypto/index.js.map +1 -0
  14. package/lib/commonjs/crypto/keyManager.js +208 -0
  15. package/lib/commonjs/crypto/keyManager.js.map +1 -0
  16. package/lib/commonjs/crypto/recoveryPhrase.js +137 -0
  17. package/lib/commonjs/crypto/recoveryPhrase.js.map +1 -0
  18. package/lib/commonjs/crypto/signatureService.js +230 -0
  19. package/lib/commonjs/crypto/signatureService.js.map +1 -0
  20. package/lib/commonjs/i18n/locales/en-US.json +4 -0
  21. package/lib/commonjs/index.js +22 -0
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/commonjs/ui/components/ActivityIndicator.js +203 -0
  24. package/lib/commonjs/ui/components/ActivityIndicator.js.map +1 -0
  25. package/lib/commonjs/ui/components/AutoHeightScrollView.js +46 -0
  26. package/lib/commonjs/ui/components/AutoHeightScrollView.js.map +1 -0
  27. package/lib/commonjs/ui/components/BottomSheet.js +407 -0
  28. package/lib/commonjs/ui/components/BottomSheet.js.map +1 -0
  29. package/lib/commonjs/ui/components/BottomSheetRouter.js +366 -0
  30. package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -0
  31. package/lib/commonjs/ui/components/CrossFadeIcon.js +106 -0
  32. package/lib/commonjs/ui/components/CrossFadeIcon.js.map +1 -0
  33. package/lib/commonjs/ui/components/EmptyState.js +41 -0
  34. package/lib/commonjs/ui/components/EmptyState.js.map +1 -0
  35. package/lib/commonjs/ui/components/GroupedItem.js +87 -82
  36. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  37. package/lib/commonjs/ui/components/GroupedSection.js +25 -25
  38. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
  39. package/lib/commonjs/ui/components/Header.js +111 -47
  40. package/lib/commonjs/ui/components/Header.js.map +1 -1
  41. package/lib/commonjs/ui/components/HelperText.js +103 -0
  42. package/lib/commonjs/ui/components/HelperText.js.map +1 -0
  43. package/lib/commonjs/ui/components/Icon.js +109 -0
  44. package/lib/commonjs/ui/components/Icon.js.map +1 -0
  45. package/lib/commonjs/ui/components/IconButton/IconButton.js +159 -0
  46. package/lib/commonjs/ui/components/IconButton/IconButton.js.map +1 -0
  47. package/lib/commonjs/ui/components/IconButton/utils.js +155 -0
  48. package/lib/commonjs/ui/components/IconButton/utils.js.map +1 -0
  49. package/lib/commonjs/ui/components/LoadingState.js +47 -0
  50. package/lib/commonjs/ui/components/LoadingState.js.map +1 -0
  51. package/lib/commonjs/ui/components/OxyPayButton.js +1 -14
  52. package/lib/commonjs/ui/components/OxyPayButton.js.map +1 -1
  53. package/lib/commonjs/ui/components/OxyProvider.js +41 -391
  54. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  55. package/lib/commonjs/ui/components/OxySignInButton.js +9 -13
  56. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  57. package/lib/commonjs/ui/components/ProfileCard.js +7 -4
  58. package/lib/commonjs/ui/components/ProfileCard.js.map +1 -1
  59. package/lib/commonjs/ui/components/QuickActions.js +7 -4
  60. package/lib/commonjs/ui/components/QuickActions.js.map +1 -1
  61. package/lib/commonjs/ui/components/Section.js +4 -1
  62. package/lib/commonjs/ui/components/Section.js.map +1 -1
  63. package/lib/commonjs/ui/components/SectionTitle.js +6 -3
  64. package/lib/commonjs/ui/components/SectionTitle.js.map +1 -1
  65. package/lib/commonjs/ui/components/SettingRow.js +77 -0
  66. package/lib/commonjs/ui/components/SettingRow.js.map +1 -0
  67. package/lib/commonjs/ui/components/StepBasedScreen.js +188 -172
  68. package/lib/commonjs/ui/components/StepBasedScreen.js.map +1 -1
  69. package/lib/commonjs/ui/components/Surface.js +258 -0
  70. package/lib/commonjs/ui/components/Surface.js.map +1 -0
  71. package/lib/commonjs/ui/components/TextField/Addons/Outline.js +46 -0
  72. package/lib/commonjs/ui/components/TextField/Addons/Outline.js.map +1 -0
  73. package/lib/commonjs/ui/components/TextField/Addons/Underline.js +53 -0
  74. package/lib/commonjs/ui/components/TextField/Addons/Underline.js.map +1 -0
  75. package/lib/commonjs/ui/components/TextField/Adornment/TextFieldAdornment.js +155 -0
  76. package/lib/commonjs/ui/components/TextField/Adornment/TextFieldAdornment.js.map +1 -0
  77. package/lib/commonjs/ui/components/TextField/Adornment/TextFieldAffix.js +144 -0
  78. package/lib/commonjs/ui/components/TextField/Adornment/TextFieldAffix.js.map +1 -0
  79. package/lib/commonjs/ui/components/TextField/Adornment/TextFieldIcon.js +137 -0
  80. package/lib/commonjs/ui/components/TextField/Adornment/TextFieldIcon.js.map +1 -0
  81. package/lib/commonjs/ui/components/TextField/Adornment/enums.js +22 -0
  82. package/lib/commonjs/ui/components/TextField/Adornment/enums.js.map +1 -0
  83. package/lib/commonjs/ui/components/TextField/Adornment/types.js +6 -0
  84. package/lib/commonjs/ui/components/TextField/Adornment/types.js.map +1 -0
  85. package/lib/commonjs/ui/components/TextField/Adornment/utils.js +62 -0
  86. package/lib/commonjs/ui/components/TextField/Adornment/utils.js.map +1 -0
  87. package/lib/commonjs/ui/components/TextField/Label/InputLabel.js +176 -0
  88. package/lib/commonjs/ui/components/TextField/Label/InputLabel.js.map +1 -0
  89. package/lib/commonjs/ui/components/TextField/Label/LabelBackground.js +84 -0
  90. package/lib/commonjs/ui/components/TextField/Label/LabelBackground.js.map +1 -0
  91. package/lib/commonjs/ui/components/TextField/TextFieldFlat.js +377 -0
  92. package/lib/commonjs/ui/components/TextField/TextFieldFlat.js.map +1 -0
  93. package/lib/commonjs/ui/components/TextField/TextFieldOutlined.js +361 -0
  94. package/lib/commonjs/ui/components/TextField/TextFieldOutlined.js.map +1 -0
  95. package/lib/commonjs/ui/components/TextField/constants.js +50 -0
  96. package/lib/commonjs/ui/components/TextField/constants.js.map +1 -0
  97. package/lib/commonjs/ui/components/TextField/helpers.js +490 -0
  98. package/lib/commonjs/ui/components/TextField/helpers.js.map +1 -0
  99. package/lib/commonjs/ui/components/TextField/types.js +6 -0
  100. package/lib/commonjs/ui/components/TextField/types.js.map +1 -0
  101. package/lib/commonjs/ui/components/TextField.js +339 -0
  102. package/lib/commonjs/ui/components/TextField.js.map +1 -0
  103. package/lib/commonjs/ui/components/TouchableRipple/Pressable.js +12 -0
  104. package/lib/commonjs/ui/components/TouchableRipple/Pressable.js.map +1 -0
  105. package/lib/commonjs/ui/components/TouchableRipple/TouchableRipple.js +258 -0
  106. package/lib/commonjs/ui/components/TouchableRipple/TouchableRipple.js.map +1 -0
  107. package/lib/commonjs/ui/components/TouchableRipple/TouchableRipple.native.js +107 -0
  108. package/lib/commonjs/ui/components/TouchableRipple/TouchableRipple.native.js.map +1 -0
  109. package/lib/commonjs/ui/components/TouchableRipple/utils.js +56 -0
  110. package/lib/commonjs/ui/components/TouchableRipple/utils.js.map +1 -0
  111. package/lib/commonjs/ui/components/Typography/AnimatedText.js +62 -0
  112. package/lib/commonjs/ui/components/Typography/AnimatedText.js.map +1 -0
  113. package/lib/commonjs/ui/components/Typography/types.js +26 -0
  114. package/lib/commonjs/ui/components/Typography/types.js.map +1 -0
  115. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js +171 -0
  116. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -0
  117. package/lib/commonjs/ui/components/fileManagement/FileViewer.js +409 -0
  118. package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -0
  119. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +181 -0
  120. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -0
  121. package/lib/commonjs/ui/components/fileManagement/styles.js +868 -0
  122. package/lib/commonjs/ui/components/fileManagement/styles.js.map +1 -0
  123. package/lib/commonjs/ui/components/index.js +29 -1
  124. package/lib/commonjs/ui/components/index.js.map +1 -1
  125. package/lib/commonjs/ui/components/profile/EditBioModal.js +181 -0
  126. package/lib/commonjs/ui/components/profile/EditBioModal.js.map +1 -0
  127. package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js +207 -0
  128. package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js.map +1 -0
  129. package/lib/commonjs/ui/components/profile/EditEmailModal.js +184 -0
  130. package/lib/commonjs/ui/components/profile/EditEmailModal.js.map +1 -0
  131. package/lib/commonjs/ui/components/profile/EditLinksModal.js +315 -0
  132. package/lib/commonjs/ui/components/profile/EditLinksModal.js.map +1 -0
  133. package/lib/commonjs/ui/components/profile/EditLocationModal.js +273 -0
  134. package/lib/commonjs/ui/components/profile/EditLocationModal.js.map +1 -0
  135. package/lib/commonjs/ui/components/profile/EditUsernameModal.js +180 -0
  136. package/lib/commonjs/ui/components/profile/EditUsernameModal.js.map +1 -0
  137. package/lib/commonjs/ui/components/profile/TwoFactorSetupModal.js +467 -0
  138. package/lib/commonjs/ui/components/profile/TwoFactorSetupModal.js.map +1 -0
  139. package/lib/commonjs/ui/components/styles/overlay.js +85 -0
  140. package/lib/commonjs/ui/components/styles/overlay.js.map +1 -0
  141. package/lib/commonjs/ui/components/styles/shadow.js +132 -0
  142. package/lib/commonjs/ui/components/styles/shadow.js.map +1 -0
  143. package/lib/commonjs/ui/components/theming.js +116 -0
  144. package/lib/commonjs/ui/components/theming.js.map +1 -0
  145. package/lib/commonjs/ui/components/types.js +2 -0
  146. package/lib/commonjs/ui/components/types.js.map +1 -0
  147. package/lib/commonjs/ui/components/utils/forwardRef.js +18 -0
  148. package/lib/commonjs/ui/components/utils/forwardRef.js.map +1 -0
  149. package/lib/commonjs/ui/components/utils/hasTouchHandler.js +13 -0
  150. package/lib/commonjs/ui/components/utils/hasTouchHandler.js.map +1 -0
  151. package/lib/commonjs/ui/components/utils/roundLayoutSize.js +9 -0
  152. package/lib/commonjs/ui/components/utils/roundLayoutSize.js.map +1 -0
  153. package/lib/commonjs/ui/components/utils/splitStyles.js +50 -0
  154. package/lib/commonjs/ui/components/utils/splitStyles.js.map +1 -0
  155. package/lib/commonjs/ui/constants/iconColors.js +84 -0
  156. package/lib/commonjs/ui/constants/iconColors.js.map +1 -0
  157. package/lib/commonjs/ui/constants/spacing.js +50 -0
  158. package/lib/commonjs/ui/constants/spacing.js.map +1 -0
  159. package/lib/commonjs/ui/constants/theme.js +123 -0
  160. package/lib/commonjs/ui/constants/theme.js.map +1 -0
  161. package/lib/commonjs/ui/context/OxyContext.js +198 -853
  162. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  163. package/lib/commonjs/ui/context/ThemeContext.js +36 -0
  164. package/lib/commonjs/ui/context/ThemeContext.js.map +1 -0
  165. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +349 -0
  166. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -0
  167. package/lib/commonjs/ui/context/hooks/useDeviceManagement.js +73 -0
  168. package/lib/commonjs/ui/context/hooks/useDeviceManagement.js.map +1 -0
  169. package/lib/commonjs/ui/context/hooks/useLanguageManagement.js +112 -0
  170. package/lib/commonjs/ui/context/hooks/useLanguageManagement.js.map +1 -0
  171. package/lib/commonjs/ui/context/hooks/useSessionManagement.js +261 -0
  172. package/lib/commonjs/ui/context/hooks/useSessionManagement.js.map +1 -0
  173. package/lib/commonjs/ui/context/hooks/useStorage.js +79 -0
  174. package/lib/commonjs/ui/context/hooks/useStorage.js.map +1 -0
  175. package/lib/commonjs/ui/context/utils/errorHandlers.js +90 -0
  176. package/lib/commonjs/ui/context/utils/errorHandlers.js.map +1 -0
  177. package/lib/commonjs/ui/context/utils/sessionHelpers.js +103 -0
  178. package/lib/commonjs/ui/context/utils/sessionHelpers.js.map +1 -0
  179. package/lib/commonjs/ui/context/utils/storageHelpers.js +119 -0
  180. package/lib/commonjs/ui/context/utils/storageHelpers.js.map +1 -0
  181. package/lib/commonjs/ui/hooks/index.js +14 -0
  182. package/lib/commonjs/ui/hooks/index.js.map +1 -1
  183. package/lib/commonjs/ui/hooks/use-color-scheme.js +29 -0
  184. package/lib/commonjs/ui/hooks/use-color-scheme.js.map +1 -0
  185. package/lib/commonjs/ui/hooks/use-haptic-press.js +21 -0
  186. package/lib/commonjs/ui/hooks/use-haptic-press.js.map +1 -0
  187. package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
  188. package/lib/commonjs/ui/hooks/useDeviceManagement.js +73 -0
  189. package/lib/commonjs/ui/hooks/useDeviceManagement.js.map +1 -0
  190. package/lib/commonjs/ui/hooks/useLanguageManagement.js +112 -0
  191. package/lib/commonjs/ui/hooks/useLanguageManagement.js.map +1 -0
  192. package/lib/commonjs/ui/hooks/useProfileEditing.js +123 -0
  193. package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -0
  194. package/lib/commonjs/ui/hooks/useSessionManagement.js +261 -0
  195. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -0
  196. package/lib/commonjs/ui/hooks/useStorage.js +79 -0
  197. package/lib/commonjs/ui/hooks/useStorage.js.map +1 -0
  198. package/lib/commonjs/ui/hooks/useThemeColors.js +31 -0
  199. package/lib/commonjs/ui/hooks/useThemeColors.js.map +1 -0
  200. package/lib/commonjs/ui/hooks/useThemeStyles.js +69 -0
  201. package/lib/commonjs/ui/hooks/useThemeStyles.js.map +1 -0
  202. package/lib/commonjs/ui/index.js +1 -4
  203. package/lib/commonjs/ui/index.js.map +1 -1
  204. package/lib/commonjs/ui/navigation/bottomSheetManager.js +180 -0
  205. package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -0
  206. package/lib/commonjs/ui/navigation/routes.js +80 -154
  207. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  208. package/lib/commonjs/ui/screens/AccountCenterScreen.js +67 -67
  209. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  210. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +217 -98
  211. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  212. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +514 -429
  213. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  214. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +44 -59
  215. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  216. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +12 -19
  217. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  218. package/lib/commonjs/ui/screens/AppInfoScreen.js +36 -43
  219. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  220. package/lib/commonjs/ui/screens/FeedbackScreen.js +14 -14
  221. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  222. package/lib/commonjs/ui/screens/FileManagementScreen.js +674 -1968
  223. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  224. package/lib/commonjs/ui/screens/HelpSupportScreen.js +26 -23
  225. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
  226. package/lib/commonjs/ui/screens/HistoryViewScreen.js +19 -59
  227. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  228. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +79 -170
  229. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  230. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +13 -40
  231. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
  232. package/lib/commonjs/ui/screens/OxyAuthScreen.js +436 -0
  233. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -0
  234. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +18 -24
  235. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  236. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +21 -9
  237. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  238. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +180 -252
  239. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  240. package/lib/commonjs/ui/screens/ProfileScreen.js +7 -5
  241. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  242. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +32 -70
  243. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
  244. package/lib/commonjs/ui/screens/SearchSettingsScreen.js +26 -99
  245. package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
  246. package/lib/commonjs/ui/screens/SessionManagementScreen.js +24 -16
  247. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  248. package/lib/commonjs/ui/screens/UserLinksScreen.js +8 -10
  249. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
  250. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +64 -67
  251. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  252. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js +11 -8
  253. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  254. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +124 -122
  255. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  256. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +99 -112
  257. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  258. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +12 -9
  259. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  260. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +552 -79
  261. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  262. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +12 -8
  263. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  264. package/lib/commonjs/ui/styles/authStyles.js +5 -11
  265. package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
  266. package/lib/commonjs/ui/styles/spacing.js +60 -2
  267. package/lib/commonjs/ui/styles/spacing.js.map +1 -1
  268. package/lib/commonjs/ui/styles/theme.js +1 -1
  269. package/lib/commonjs/ui/types/fileManagement.js +6 -0
  270. package/lib/commonjs/ui/types/fileManagement.js.map +1 -0
  271. package/lib/commonjs/ui/types/navigation.js +6 -0
  272. package/lib/commonjs/ui/types/navigation.js.map +1 -0
  273. package/lib/commonjs/ui/utils/colorUtils.js +52 -0
  274. package/lib/commonjs/ui/utils/colorUtils.js.map +1 -0
  275. package/lib/commonjs/ui/utils/errorHandlers.js +90 -0
  276. package/lib/commonjs/ui/utils/errorHandlers.js.map +1 -0
  277. package/lib/commonjs/ui/utils/fileManagement.js +181 -0
  278. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -0
  279. package/lib/commonjs/ui/utils/sessionHelpers.js +103 -0
  280. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -0
  281. package/lib/commonjs/ui/utils/storageHelpers.js +119 -0
  282. package/lib/commonjs/ui/utils/storageHelpers.js.map +1 -0
  283. package/lib/commonjs/ui/utils/themeUtils.js +47 -0
  284. package/lib/commonjs/ui/utils/themeUtils.js.map +1 -0
  285. package/lib/commonjs/ui/utils/user-utils.js +53 -0
  286. package/lib/commonjs/ui/utils/user-utils.js.map +1 -0
  287. package/lib/commonjs/utils/errorUtils.js +14 -4
  288. package/lib/commonjs/utils/errorUtils.js.map +1 -1
  289. package/lib/module/assets/assets/lottie/welcomeheader_background_op1.json +1 -0
  290. package/lib/module/assets/lottie/welcomeheader_background_op1.json +1 -0
  291. package/lib/module/core/HttpService.js +94 -4
  292. package/lib/module/core/HttpService.js.map +1 -1
  293. package/lib/module/core/OxyServices.base.js +3 -1
  294. package/lib/module/core/OxyServices.base.js.map +1 -1
  295. package/lib/module/core/mixins/OxyServices.assets.js +212 -20
  296. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -1
  297. package/lib/module/core/mixins/OxyServices.auth.js +70 -99
  298. package/lib/module/core/mixins/OxyServices.auth.js.map +1 -1
  299. package/lib/module/crypto/index.js +16 -0
  300. package/lib/module/crypto/index.js.map +1 -0
  301. package/lib/module/crypto/keyManager.js +204 -0
  302. package/lib/module/crypto/keyManager.js.map +1 -0
  303. package/lib/module/crypto/recoveryPhrase.js +131 -0
  304. package/lib/module/crypto/recoveryPhrase.js.map +1 -0
  305. package/lib/module/crypto/signatureService.js +227 -0
  306. package/lib/module/crypto/signatureService.js.map +1 -0
  307. package/lib/module/i18n/locales/en-US.json +4 -0
  308. package/lib/module/index.js +2 -0
  309. package/lib/module/index.js.map +1 -1
  310. package/lib/module/ui/components/ActivityIndicator.js +198 -0
  311. package/lib/module/ui/components/ActivityIndicator.js.map +1 -0
  312. package/lib/module/ui/components/AutoHeightScrollView.js +41 -0
  313. package/lib/module/ui/components/AutoHeightScrollView.js.map +1 -0
  314. package/lib/module/ui/components/BottomSheet.js +402 -0
  315. package/lib/module/ui/components/BottomSheet.js.map +1 -0
  316. package/lib/module/ui/components/BottomSheetRouter.js +356 -0
  317. package/lib/module/ui/components/BottomSheetRouter.js.map +1 -0
  318. package/lib/module/ui/components/CrossFadeIcon.js +101 -0
  319. package/lib/module/ui/components/CrossFadeIcon.js.map +1 -0
  320. package/lib/module/ui/components/EmptyState.js +36 -0
  321. package/lib/module/ui/components/EmptyState.js.map +1 -0
  322. package/lib/module/ui/components/GroupedItem.js +88 -82
  323. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  324. package/lib/module/ui/components/GroupedSection.js +23 -23
  325. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  326. package/lib/module/ui/components/Header.js +109 -46
  327. package/lib/module/ui/components/Header.js.map +1 -1
  328. package/lib/module/ui/components/HelperText.js +99 -0
  329. package/lib/module/ui/components/HelperText.js.map +1 -0
  330. package/lib/module/ui/components/Icon.js +102 -0
  331. package/lib/module/ui/components/Icon.js.map +1 -0
  332. package/lib/module/ui/components/IconButton/IconButton.js +153 -0
  333. package/lib/module/ui/components/IconButton/IconButton.js.map +1 -0
  334. package/lib/module/ui/components/IconButton/utils.js +149 -0
  335. package/lib/module/ui/components/IconButton/utils.js.map +1 -0
  336. package/lib/module/ui/components/LoadingState.js +42 -0
  337. package/lib/module/ui/components/LoadingState.js.map +1 -0
  338. package/lib/module/ui/components/OxyPayButton.js +1 -14
  339. package/lib/module/ui/components/OxyPayButton.js.map +1 -1
  340. package/lib/module/ui/components/OxyProvider.js +45 -396
  341. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  342. package/lib/module/ui/components/OxySignInButton.js +9 -13
  343. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  344. package/lib/module/ui/components/ProfileCard.js +7 -4
  345. package/lib/module/ui/components/ProfileCard.js.map +1 -1
  346. package/lib/module/ui/components/QuickActions.js +7 -4
  347. package/lib/module/ui/components/QuickActions.js.map +1 -1
  348. package/lib/module/ui/components/Section.js +4 -1
  349. package/lib/module/ui/components/Section.js.map +1 -1
  350. package/lib/module/ui/components/SectionTitle.js +6 -3
  351. package/lib/module/ui/components/SectionTitle.js.map +1 -1
  352. package/lib/module/ui/components/SettingRow.js +72 -0
  353. package/lib/module/ui/components/SettingRow.js.map +1 -0
  354. package/lib/module/ui/components/StepBasedScreen.js +190 -174
  355. package/lib/module/ui/components/StepBasedScreen.js.map +1 -1
  356. package/lib/module/ui/components/Surface.js +252 -0
  357. package/lib/module/ui/components/Surface.js.map +1 -0
  358. package/lib/module/ui/components/TextField/Addons/Outline.js +40 -0
  359. package/lib/module/ui/components/TextField/Addons/Outline.js.map +1 -0
  360. package/lib/module/ui/components/TextField/Addons/Underline.js +47 -0
  361. package/lib/module/ui/components/TextField/Addons/Underline.js.map +1 -0
  362. package/lib/module/ui/components/TextField/Adornment/TextFieldAdornment.js +148 -0
  363. package/lib/module/ui/components/TextField/Adornment/TextFieldAdornment.js.map +1 -0
  364. package/lib/module/ui/components/TextField/Adornment/TextFieldAffix.js +141 -0
  365. package/lib/module/ui/components/TextField/Adornment/TextFieldAffix.js.map +1 -0
  366. package/lib/module/ui/components/TextField/Adornment/TextFieldIcon.js +135 -0
  367. package/lib/module/ui/components/TextField/Adornment/TextFieldIcon.js.map +1 -0
  368. package/lib/module/ui/components/TextField/Adornment/enums.js +18 -0
  369. package/lib/module/ui/components/TextField/Adornment/enums.js.map +1 -0
  370. package/lib/module/ui/components/TextField/Adornment/types.js +4 -0
  371. package/lib/module/ui/components/TextField/Adornment/types.js.map +1 -0
  372. package/lib/module/ui/components/TextField/Adornment/utils.js +57 -0
  373. package/lib/module/ui/components/TextField/Adornment/utils.js.map +1 -0
  374. package/lib/module/ui/components/TextField/Label/InputLabel.js +171 -0
  375. package/lib/module/ui/components/TextField/Label/InputLabel.js.map +1 -0
  376. package/lib/module/ui/components/TextField/Label/LabelBackground.js +78 -0
  377. package/lib/module/ui/components/TextField/Label/LabelBackground.js.map +1 -0
  378. package/lib/module/ui/components/TextField/TextFieldFlat.js +372 -0
  379. package/lib/module/ui/components/TextField/TextFieldFlat.js.map +1 -0
  380. package/lib/module/ui/components/TextField/TextFieldOutlined.js +355 -0
  381. package/lib/module/ui/components/TextField/TextFieldOutlined.js.map +1 -0
  382. package/lib/module/ui/components/TextField/constants.js +46 -0
  383. package/lib/module/ui/components/TextField/constants.js.map +1 -0
  384. package/lib/module/ui/components/TextField/helpers.js +472 -0
  385. package/lib/module/ui/components/TextField/helpers.js.map +1 -0
  386. package/lib/module/ui/components/TextField/types.js +4 -0
  387. package/lib/module/ui/components/TextField/types.js.map +1 -0
  388. package/lib/module/ui/components/TextField.js +333 -0
  389. package/lib/module/ui/components/TextField.js.map +1 -0
  390. package/lib/module/ui/components/TouchableRipple/Pressable.js +9 -0
  391. package/lib/module/ui/components/TouchableRipple/Pressable.js.map +1 -0
  392. package/lib/module/ui/components/TouchableRipple/TouchableRipple.js +253 -0
  393. package/lib/module/ui/components/TouchableRipple/TouchableRipple.js.map +1 -0
  394. package/lib/module/ui/components/TouchableRipple/TouchableRipple.native.js +101 -0
  395. package/lib/module/ui/components/TouchableRipple/TouchableRipple.native.js.map +1 -0
  396. package/lib/module/ui/components/TouchableRipple/utils.js +50 -0
  397. package/lib/module/ui/components/TouchableRipple/utils.js.map +1 -0
  398. package/lib/module/ui/components/Typography/AnimatedText.js +56 -0
  399. package/lib/module/ui/components/Typography/AnimatedText.js.map +1 -0
  400. package/lib/module/ui/components/Typography/types.js +22 -0
  401. package/lib/module/ui/components/Typography/types.js.map +1 -0
  402. package/lib/module/ui/components/fileManagement/FileDetailsModal.js +165 -0
  403. package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -0
  404. package/lib/module/ui/components/fileManagement/FileViewer.js +402 -0
  405. package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -0
  406. package/lib/module/ui/components/fileManagement/UploadPreview.js +175 -0
  407. package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -0
  408. package/lib/module/ui/components/fileManagement/styles.js +864 -0
  409. package/lib/module/ui/components/fileManagement/styles.js.map +1 -0
  410. package/lib/module/ui/components/index.js +4 -1
  411. package/lib/module/ui/components/index.js.map +1 -1
  412. package/lib/module/ui/components/profile/EditBioModal.js +175 -0
  413. package/lib/module/ui/components/profile/EditBioModal.js.map +1 -0
  414. package/lib/module/ui/components/profile/EditDisplayNameModal.js +201 -0
  415. package/lib/module/ui/components/profile/EditDisplayNameModal.js.map +1 -0
  416. package/lib/module/ui/components/profile/EditEmailModal.js +178 -0
  417. package/lib/module/ui/components/profile/EditEmailModal.js.map +1 -0
  418. package/lib/module/ui/components/profile/EditLinksModal.js +309 -0
  419. package/lib/module/ui/components/profile/EditLinksModal.js.map +1 -0
  420. package/lib/module/ui/components/profile/EditLocationModal.js +267 -0
  421. package/lib/module/ui/components/profile/EditLocationModal.js.map +1 -0
  422. package/lib/module/ui/components/profile/EditUsernameModal.js +174 -0
  423. package/lib/module/ui/components/profile/EditUsernameModal.js.map +1 -0
  424. package/lib/module/ui/components/profile/TwoFactorSetupModal.js +460 -0
  425. package/lib/module/ui/components/profile/TwoFactorSetupModal.js.map +1 -0
  426. package/lib/module/ui/components/styles/overlay.js +80 -0
  427. package/lib/module/ui/components/styles/overlay.js.map +1 -0
  428. package/lib/module/ui/components/styles/shadow.js +128 -0
  429. package/lib/module/ui/components/styles/shadow.js.map +1 -0
  430. package/lib/module/ui/components/theming.js +111 -0
  431. package/lib/module/ui/components/theming.js.map +1 -0
  432. package/lib/module/ui/components/types.js +2 -0
  433. package/lib/module/ui/components/types.js.map +1 -0
  434. package/lib/module/ui/components/utils/forwardRef.js +13 -0
  435. package/lib/module/ui/components/utils/forwardRef.js.map +1 -0
  436. package/lib/module/ui/components/utils/hasTouchHandler.js +9 -0
  437. package/lib/module/ui/components/utils/hasTouchHandler.js.map +1 -0
  438. package/lib/module/ui/components/utils/roundLayoutSize.js +4 -0
  439. package/lib/module/ui/components/utils/roundLayoutSize.js.map +1 -0
  440. package/lib/module/ui/components/utils/splitStyles.js +46 -0
  441. package/lib/module/ui/components/utils/splitStyles.js.map +1 -0
  442. package/lib/module/ui/constants/iconColors.js +78 -0
  443. package/lib/module/ui/constants/iconColors.js.map +1 -0
  444. package/lib/module/ui/constants/spacing.js +45 -0
  445. package/lib/module/ui/constants/spacing.js.map +1 -0
  446. package/lib/module/ui/constants/theme.js +119 -0
  447. package/lib/module/ui/constants/theme.js.map +1 -0
  448. package/lib/module/ui/context/OxyContext.js +199 -855
  449. package/lib/module/ui/context/OxyContext.js.map +1 -1
  450. package/lib/module/ui/context/ThemeContext.js +29 -0
  451. package/lib/module/ui/context/ThemeContext.js.map +1 -0
  452. package/lib/module/ui/context/hooks/useAuthOperations.js +344 -0
  453. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -0
  454. package/lib/module/ui/context/hooks/useDeviceManagement.js +68 -0
  455. package/lib/module/ui/context/hooks/useDeviceManagement.js.map +1 -0
  456. package/lib/module/ui/context/hooks/useLanguageManagement.js +107 -0
  457. package/lib/module/ui/context/hooks/useLanguageManagement.js.map +1 -0
  458. package/lib/module/ui/context/hooks/useSessionManagement.js +256 -0
  459. package/lib/module/ui/context/hooks/useSessionManagement.js.map +1 -0
  460. package/lib/module/ui/context/hooks/useStorage.js +74 -0
  461. package/lib/module/ui/context/hooks/useStorage.js.map +1 -0
  462. package/lib/module/ui/context/utils/errorHandlers.js +83 -0
  463. package/lib/module/ui/context/utils/errorHandlers.js.map +1 -0
  464. package/lib/module/ui/context/utils/sessionHelpers.js +96 -0
  465. package/lib/module/ui/context/utils/sessionHelpers.js.map +1 -0
  466. package/lib/module/ui/context/utils/storageHelpers.js +111 -0
  467. package/lib/module/ui/context/utils/storageHelpers.js.map +1 -0
  468. package/lib/module/ui/hooks/index.js +2 -0
  469. package/lib/module/ui/hooks/index.js.map +1 -1
  470. package/lib/module/ui/hooks/use-color-scheme.js +26 -0
  471. package/lib/module/ui/hooks/use-color-scheme.js.map +1 -0
  472. package/lib/module/ui/hooks/use-haptic-press.js +17 -0
  473. package/lib/module/ui/hooks/use-haptic-press.js.map +1 -0
  474. package/lib/module/ui/hooks/useAssets.js.map +1 -1
  475. package/lib/module/ui/hooks/useDeviceManagement.js +68 -0
  476. package/lib/module/ui/hooks/useDeviceManagement.js.map +1 -0
  477. package/lib/module/ui/hooks/useLanguageManagement.js +107 -0
  478. package/lib/module/ui/hooks/useLanguageManagement.js.map +1 -0
  479. package/lib/module/ui/hooks/useProfileEditing.js +118 -0
  480. package/lib/module/ui/hooks/useProfileEditing.js.map +1 -0
  481. package/lib/module/ui/hooks/useSessionManagement.js +256 -0
  482. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -0
  483. package/lib/module/ui/hooks/useStorage.js +74 -0
  484. package/lib/module/ui/hooks/useStorage.js.map +1 -0
  485. package/lib/module/ui/hooks/useThemeColors.js +27 -0
  486. package/lib/module/ui/hooks/useThemeColors.js.map +1 -0
  487. package/lib/module/ui/hooks/useThemeStyles.js +64 -0
  488. package/lib/module/ui/hooks/useThemeStyles.js.map +1 -0
  489. package/lib/module/ui/index.js +2 -4
  490. package/lib/module/ui/index.js.map +1 -1
  491. package/lib/module/ui/navigation/bottomSheetManager.js +168 -0
  492. package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -0
  493. package/lib/module/ui/navigation/routes.js +77 -152
  494. package/lib/module/ui/navigation/routes.js.map +1 -1
  495. package/lib/module/ui/screens/AccountCenterScreen.js +67 -67
  496. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  497. package/lib/module/ui/screens/AccountOverviewScreen.js +218 -100
  498. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  499. package/lib/module/ui/screens/AccountSettingsScreen.js +515 -430
  500. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  501. package/lib/module/ui/screens/AccountSwitcherScreen.js +45 -60
  502. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  503. package/lib/module/ui/screens/AccountVerificationScreen.js +12 -19
  504. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  505. package/lib/module/ui/screens/AppInfoScreen.js +36 -43
  506. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  507. package/lib/module/ui/screens/FeedbackScreen.js +14 -14
  508. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  509. package/lib/module/ui/screens/FileManagementScreen.js +670 -1965
  510. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  511. package/lib/module/ui/screens/HelpSupportScreen.js +26 -23
  512. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
  513. package/lib/module/ui/screens/HistoryViewScreen.js +22 -62
  514. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  515. package/lib/module/ui/screens/LanguageSelectorScreen.js +80 -171
  516. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  517. package/lib/module/ui/screens/LegalDocumentsScreen.js +16 -43
  518. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
  519. package/lib/module/ui/screens/OxyAuthScreen.js +432 -0
  520. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -0
  521. package/lib/module/ui/screens/PaymentGatewayScreen.js +18 -24
  522. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  523. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +21 -9
  524. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  525. package/lib/module/ui/screens/PrivacySettingsScreen.js +167 -239
  526. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  527. package/lib/module/ui/screens/ProfileScreen.js +7 -5
  528. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  529. package/lib/module/ui/screens/SavesCollectionsScreen.js +35 -73
  530. package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
  531. package/lib/module/ui/screens/SearchSettingsScreen.js +29 -102
  532. package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
  533. package/lib/module/ui/screens/SessionManagementScreen.js +24 -16
  534. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  535. package/lib/module/ui/screens/UserLinksScreen.js +8 -10
  536. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
  537. package/lib/module/ui/screens/WelcomeNewUserScreen.js +65 -68
  538. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  539. package/lib/module/ui/screens/karma/KarmaAboutScreen.js +11 -8
  540. package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  541. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +124 -121
  542. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  543. package/lib/module/ui/screens/karma/KarmaFAQScreen.js +101 -114
  544. package/lib/module/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  545. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +12 -9
  546. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  547. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +554 -81
  548. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  549. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +12 -8
  550. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  551. package/lib/module/ui/styles/authStyles.js +5 -11
  552. package/lib/module/ui/styles/authStyles.js.map +1 -1
  553. package/lib/module/ui/styles/spacing.js +12 -2
  554. package/lib/module/ui/styles/spacing.js.map +1 -1
  555. package/lib/module/ui/styles/theme.js +1 -1
  556. package/lib/module/ui/types/fileManagement.js +4 -0
  557. package/lib/module/ui/types/fileManagement.js.map +1 -0
  558. package/lib/module/ui/types/navigation.js +4 -0
  559. package/lib/module/ui/types/navigation.js.map +1 -0
  560. package/lib/module/ui/utils/colorUtils.js +46 -0
  561. package/lib/module/ui/utils/colorUtils.js.map +1 -0
  562. package/lib/module/ui/utils/errorHandlers.js +83 -0
  563. package/lib/module/ui/utils/errorHandlers.js.map +1 -0
  564. package/lib/module/ui/utils/fileManagement.js +172 -0
  565. package/lib/module/ui/utils/fileManagement.js.map +1 -0
  566. package/lib/module/ui/utils/sessionHelpers.js +96 -0
  567. package/lib/module/ui/utils/sessionHelpers.js.map +1 -0
  568. package/lib/module/ui/utils/storageHelpers.js +111 -0
  569. package/lib/module/ui/utils/storageHelpers.js.map +1 -0
  570. package/lib/module/ui/utils/themeUtils.js +41 -0
  571. package/lib/module/ui/utils/themeUtils.js.map +1 -0
  572. package/lib/module/ui/utils/user-utils.js +46 -0
  573. package/lib/module/ui/utils/user-utils.js.map +1 -0
  574. package/lib/module/utils/errorUtils.js +14 -4
  575. package/lib/module/utils/errorUtils.js.map +1 -1
  576. package/lib/typescript/core/HttpService.d.ts +48 -0
  577. package/lib/typescript/core/HttpService.d.ts.map +1 -1
  578. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
  579. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
  580. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +23 -1
  581. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
  582. package/lib/typescript/core/mixins/OxyServices.auth.d.ts +70 -42
  583. package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
  584. package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
  585. package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
  586. package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
  587. package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
  588. package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
  589. package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
  590. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
  591. package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -1
  592. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
  593. package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
  594. package/lib/typescript/core/mixins/index.d.ts +26 -34
  595. package/lib/typescript/core/mixins/index.d.ts.map +1 -1
  596. package/lib/typescript/crypto/index.d.ts +11 -0
  597. package/lib/typescript/crypto/index.d.ts.map +1 -0
  598. package/lib/typescript/crypto/keyManager.d.ts +73 -0
  599. package/lib/typescript/crypto/keyManager.d.ts.map +1 -0
  600. package/lib/typescript/crypto/recoveryPhrase.d.ts +57 -0
  601. package/lib/typescript/crypto/recoveryPhrase.d.ts.map +1 -0
  602. package/lib/typescript/crypto/signatureService.d.ts +80 -0
  603. package/lib/typescript/crypto/signatureService.d.ts.map +1 -0
  604. package/lib/typescript/index.d.ts +2 -0
  605. package/lib/typescript/index.d.ts.map +1 -1
  606. package/lib/typescript/models/interfaces.d.ts +1 -0
  607. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  608. package/lib/typescript/types/bip39.d.ts +28 -0
  609. package/lib/typescript/types/color.d.ts +18 -0
  610. package/lib/typescript/types/elliptic.d.ts +60 -0
  611. package/lib/typescript/types/expo-crypto.d.ts +28 -0
  612. package/lib/typescript/types/expo-random.d.ts +8 -0
  613. package/lib/typescript/types/expo-secure-store.d.ts +20 -0
  614. package/lib/typescript/types/expo-vector-icons.d.ts +9 -0
  615. package/lib/typescript/ui/components/ActivityIndicator.d.ts +45 -0
  616. package/lib/typescript/ui/components/ActivityIndicator.d.ts.map +1 -0
  617. package/lib/typescript/ui/components/AutoHeightScrollView.d.ts +23 -0
  618. package/lib/typescript/ui/components/AutoHeightScrollView.d.ts.map +1 -0
  619. package/lib/typescript/ui/components/BottomSheet.d.ts +29 -0
  620. package/lib/typescript/ui/components/BottomSheet.d.ts.map +1 -0
  621. package/lib/typescript/ui/components/BottomSheetRouter.d.ts +14 -0
  622. package/lib/typescript/ui/components/BottomSheetRouter.d.ts.map +1 -0
  623. package/lib/typescript/ui/components/CrossFadeIcon.d.ts +27 -0
  624. package/lib/typescript/ui/components/CrossFadeIcon.d.ts.map +1 -0
  625. package/lib/typescript/ui/components/EmptyState.d.ts +8 -0
  626. package/lib/typescript/ui/components/EmptyState.d.ts.map +1 -0
  627. package/lib/typescript/ui/components/GroupedItem.d.ts +5 -9
  628. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  629. package/lib/typescript/ui/components/GroupedSection.d.ts +6 -7
  630. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -1
  631. package/lib/typescript/ui/components/Header.d.ts +6 -1
  632. package/lib/typescript/ui/components/Header.d.ts.map +1 -1
  633. package/lib/typescript/ui/components/HelperText.d.ts +47 -0
  634. package/lib/typescript/ui/components/HelperText.d.ts.map +1 -0
  635. package/lib/typescript/ui/components/Icon.d.ts +60 -0
  636. package/lib/typescript/ui/components/Icon.d.ts.map +1 -0
  637. package/lib/typescript/ui/components/IconButton/IconButton.d.ts +99 -0
  638. package/lib/typescript/ui/components/IconButton/IconButton.d.ts.map +1 -0
  639. package/lib/typescript/ui/components/IconButton/utils.d.ts +19 -0
  640. package/lib/typescript/ui/components/IconButton/utils.d.ts.map +1 -0
  641. package/lib/typescript/ui/components/LoadingState.d.ts +9 -0
  642. package/lib/typescript/ui/components/LoadingState.d.ts.map +1 -0
  643. package/lib/typescript/ui/components/OxyPayButton.d.ts.map +1 -1
  644. package/lib/typescript/ui/components/OxyProvider.d.ts +4 -5
  645. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  646. package/lib/typescript/ui/components/OxySignInButton.d.ts +3 -8
  647. package/lib/typescript/ui/components/OxySignInButton.d.ts.map +1 -1
  648. package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -1
  649. package/lib/typescript/ui/components/QuickActions.d.ts.map +1 -1
  650. package/lib/typescript/ui/components/Section.d.ts +1 -1
  651. package/lib/typescript/ui/components/Section.d.ts.map +1 -1
  652. package/lib/typescript/ui/components/SectionTitle.d.ts +1 -1
  653. package/lib/typescript/ui/components/SectionTitle.d.ts.map +1 -1
  654. package/lib/typescript/ui/components/SettingRow.d.ts +14 -0
  655. package/lib/typescript/ui/components/SettingRow.d.ts.map +1 -0
  656. package/lib/typescript/ui/components/StepBasedScreen.d.ts +3 -2
  657. package/lib/typescript/ui/components/StepBasedScreen.d.ts.map +1 -1
  658. package/lib/typescript/ui/components/Surface.d.ts +76 -0
  659. package/lib/typescript/ui/components/Surface.d.ts.map +1 -0
  660. package/lib/typescript/ui/components/TextField/Addons/Outline.d.ts +16 -0
  661. package/lib/typescript/ui/components/TextField/Addons/Outline.d.ts.map +1 -0
  662. package/lib/typescript/ui/components/TextField/Addons/Underline.d.ts +19 -0
  663. package/lib/typescript/ui/components/TextField/Addons/Underline.d.ts.map +1 -0
  664. package/lib/typescript/ui/components/TextField/Adornment/TextFieldAdornment.d.ts +45 -0
  665. package/lib/typescript/ui/components/TextField/Adornment/TextFieldAdornment.d.ts.map +1 -0
  666. package/lib/typescript/ui/components/TextField/Adornment/TextFieldAffix.d.ts +73 -0
  667. package/lib/typescript/ui/components/TextField/Adornment/TextFieldAffix.d.ts.map +1 -0
  668. package/lib/typescript/ui/components/TextField/Adornment/TextFieldIcon.d.ts +78 -0
  669. package/lib/typescript/ui/components/TextField/Adornment/TextFieldIcon.d.ts.map +1 -0
  670. package/lib/typescript/ui/components/TextField/Adornment/enums.d.ts +13 -0
  671. package/lib/typescript/ui/components/TextField/Adornment/enums.d.ts.map +1 -0
  672. package/lib/typescript/ui/components/TextField/Adornment/types.d.ts +12 -0
  673. package/lib/typescript/ui/components/TextField/Adornment/types.d.ts.map +1 -0
  674. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts +12 -0
  675. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts.map +1 -0
  676. package/lib/typescript/ui/components/TextField/Label/InputLabel.d.ts +5 -0
  677. package/lib/typescript/ui/components/TextField/Label/InputLabel.d.ts.map +1 -0
  678. package/lib/typescript/ui/components/TextField/Label/LabelBackground.d.ts +4 -0
  679. package/lib/typescript/ui/components/TextField/Label/LabelBackground.d.ts.map +1 -0
  680. package/lib/typescript/ui/components/TextField/TextFieldFlat.d.ts +4 -0
  681. package/lib/typescript/ui/components/TextField/TextFieldFlat.d.ts.map +1 -0
  682. package/lib/typescript/ui/components/TextField/TextFieldOutlined.d.ts +4 -0
  683. package/lib/typescript/ui/components/TextField/TextFieldOutlined.d.ts.map +1 -0
  684. package/lib/typescript/ui/components/TextField/constants.d.ts +32 -0
  685. package/lib/typescript/ui/components/TextField/constants.d.ts.map +1 -0
  686. package/lib/typescript/ui/components/TextField/helpers.d.ts +97 -0
  687. package/lib/typescript/ui/components/TextField/helpers.d.ts.map +1 -0
  688. package/lib/typescript/ui/components/TextField/types.d.ts +156 -0
  689. package/lib/typescript/ui/components/TextField/types.d.ts.map +1 -0
  690. package/lib/typescript/ui/components/TextField.d.ts +192 -0
  691. package/lib/typescript/ui/components/TextField.d.ts.map +1 -0
  692. package/lib/typescript/ui/components/TouchableRipple/Pressable.d.ts +13 -0
  693. package/lib/typescript/ui/components/TouchableRipple/Pressable.d.ts.map +1 -0
  694. package/lib/typescript/ui/components/TouchableRipple/TouchableRipple.d.ts +62 -0
  695. package/lib/typescript/ui/components/TouchableRipple/TouchableRipple.d.ts.map +1 -0
  696. package/lib/typescript/ui/components/TouchableRipple/TouchableRipple.native.d.ts +25 -0
  697. package/lib/typescript/ui/components/TouchableRipple/TouchableRipple.native.d.ts.map +1 -0
  698. package/lib/typescript/ui/components/TouchableRipple/utils.d.ts +11 -0
  699. package/lib/typescript/ui/components/TouchableRipple/utils.d.ts.map +1 -0
  700. package/lib/typescript/ui/components/Typography/AnimatedText.d.ts +35 -0
  701. package/lib/typescript/ui/components/Typography/AnimatedText.d.ts.map +1 -0
  702. package/lib/typescript/ui/components/Typography/types.d.ts +19 -0
  703. package/lib/typescript/ui/components/Typography/types.d.ts.map +1 -0
  704. package/lib/typescript/ui/components/fileManagement/FileDetailsModal.d.ts +15 -0
  705. package/lib/typescript/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -0
  706. package/lib/typescript/ui/components/fileManagement/FileViewer.d.ts +18 -0
  707. package/lib/typescript/ui/components/fileManagement/FileViewer.d.ts.map +1 -0
  708. package/lib/typescript/ui/components/fileManagement/UploadPreview.d.ts +21 -0
  709. package/lib/typescript/ui/components/fileManagement/UploadPreview.d.ts.map +1 -0
  710. package/lib/typescript/ui/components/fileManagement/styles.d.ts +860 -0
  711. package/lib/typescript/ui/components/fileManagement/styles.d.ts.map +1 -0
  712. package/lib/typescript/ui/components/index.d.ts +4 -1
  713. package/lib/typescript/ui/components/index.d.ts.map +1 -1
  714. package/lib/typescript/ui/components/profile/EditBioModal.d.ts +11 -0
  715. package/lib/typescript/ui/components/profile/EditBioModal.d.ts.map +1 -0
  716. package/lib/typescript/ui/components/profile/EditDisplayNameModal.d.ts +12 -0
  717. package/lib/typescript/ui/components/profile/EditDisplayNameModal.d.ts.map +1 -0
  718. package/lib/typescript/ui/components/profile/EditEmailModal.d.ts +11 -0
  719. package/lib/typescript/ui/components/profile/EditEmailModal.d.ts.map +1 -0
  720. package/lib/typescript/ui/components/profile/EditLinksModal.d.ts +18 -0
  721. package/lib/typescript/ui/components/profile/EditLinksModal.d.ts.map +1 -0
  722. package/lib/typescript/ui/components/profile/EditLocationModal.d.ts +20 -0
  723. package/lib/typescript/ui/components/profile/EditLocationModal.d.ts.map +1 -0
  724. package/lib/typescript/ui/components/profile/EditUsernameModal.d.ts +11 -0
  725. package/lib/typescript/ui/components/profile/EditUsernameModal.d.ts.map +1 -0
  726. package/lib/typescript/ui/components/profile/TwoFactorSetupModal.d.ts +11 -0
  727. package/lib/typescript/ui/components/profile/TwoFactorSetupModal.d.ts.map +1 -0
  728. package/lib/typescript/ui/components/styles/overlay.d.ts +4 -0
  729. package/lib/typescript/ui/components/styles/overlay.d.ts.map +1 -0
  730. package/lib/typescript/ui/components/styles/shadow.d.ts +3 -0
  731. package/lib/typescript/ui/components/styles/shadow.d.ts.map +1 -0
  732. package/lib/typescript/ui/components/theming.d.ts +8 -0
  733. package/lib/typescript/ui/components/theming.d.ts.map +1 -0
  734. package/lib/typescript/ui/components/types.d.ts +80 -0
  735. package/lib/typescript/ui/components/types.d.ts.map +1 -0
  736. package/lib/typescript/ui/components/utils/forwardRef.d.ts +12 -0
  737. package/lib/typescript/ui/components/utils/forwardRef.d.ts.map +1 -0
  738. package/lib/typescript/ui/components/utils/hasTouchHandler.d.ts +6 -0
  739. package/lib/typescript/ui/components/utils/hasTouchHandler.d.ts.map +1 -0
  740. package/lib/typescript/ui/components/utils/roundLayoutSize.d.ts +2 -0
  741. package/lib/typescript/ui/components/utils/roundLayoutSize.d.ts.map +1 -0
  742. package/lib/typescript/ui/components/utils/splitStyles.d.ts +20 -0
  743. package/lib/typescript/ui/components/utils/splitStyles.d.ts.map +1 -0
  744. package/lib/typescript/ui/constants/iconColors.d.ts +130 -0
  745. package/lib/typescript/ui/constants/iconColors.d.ts.map +1 -0
  746. package/lib/typescript/ui/constants/spacing.d.ts +33 -0
  747. package/lib/typescript/ui/constants/spacing.d.ts.map +1 -0
  748. package/lib/typescript/ui/constants/theme.d.ts +97 -0
  749. package/lib/typescript/ui/constants/theme.d.ts.map +1 -0
  750. package/lib/typescript/ui/context/OxyContext.d.ts +23 -17
  751. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  752. package/lib/typescript/ui/context/ThemeContext.d.ts +19 -0
  753. package/lib/typescript/ui/context/ThemeContext.d.ts.map +1 -0
  754. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +51 -0
  755. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -0
  756. package/lib/typescript/ui/context/hooks/useDeviceManagement.d.ts +27 -0
  757. package/lib/typescript/ui/context/hooks/useDeviceManagement.d.ts.map +1 -0
  758. package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts +25 -0
  759. package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts.map +1 -0
  760. package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts +39 -0
  761. package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts.map +1 -0
  762. package/lib/typescript/ui/context/hooks/useStorage.d.ts +22 -0
  763. package/lib/typescript/ui/context/hooks/useStorage.d.ts.map +1 -0
  764. package/lib/typescript/ui/context/utils/errorHandlers.d.ts +30 -0
  765. package/lib/typescript/ui/context/utils/errorHandlers.d.ts.map +1 -0
  766. package/lib/typescript/ui/context/utils/sessionHelpers.d.ts +59 -0
  767. package/lib/typescript/ui/context/utils/sessionHelpers.d.ts.map +1 -0
  768. package/lib/typescript/ui/context/utils/storageHelpers.d.ts +31 -0
  769. package/lib/typescript/ui/context/utils/storageHelpers.d.ts.map +1 -0
  770. package/lib/typescript/ui/hooks/index.d.ts +2 -0
  771. package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
  772. package/lib/typescript/ui/hooks/use-color-scheme.d.ts +8 -0
  773. package/lib/typescript/ui/hooks/use-color-scheme.d.ts.map +1 -0
  774. package/lib/typescript/ui/hooks/use-haptic-press.d.ts +8 -0
  775. package/lib/typescript/ui/hooks/use-haptic-press.d.ts.map +1 -0
  776. package/lib/typescript/ui/hooks/useDeviceManagement.d.ts +27 -0
  777. package/lib/typescript/ui/hooks/useDeviceManagement.d.ts.map +1 -0
  778. package/lib/typescript/ui/hooks/useLanguageManagement.d.ts +25 -0
  779. package/lib/typescript/ui/hooks/useLanguageManagement.d.ts.map +1 -0
  780. package/lib/typescript/ui/hooks/useProfileEditing.d.ts +36 -0
  781. package/lib/typescript/ui/hooks/useProfileEditing.d.ts.map +1 -0
  782. package/lib/typescript/ui/hooks/useSessionManagement.d.ts +39 -0
  783. package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -0
  784. package/lib/typescript/ui/hooks/useStorage.d.ts +22 -0
  785. package/lib/typescript/ui/hooks/useStorage.d.ts.map +1 -0
  786. package/lib/typescript/ui/hooks/useThemeColors.d.ts +94 -0
  787. package/lib/typescript/ui/hooks/useThemeColors.d.ts.map +1 -0
  788. package/lib/typescript/ui/hooks/useThemeStyles.d.ts +45 -0
  789. package/lib/typescript/ui/hooks/useThemeStyles.d.ts.map +1 -0
  790. package/lib/typescript/ui/index.d.ts +2 -2
  791. package/lib/typescript/ui/index.d.ts.map +1 -1
  792. package/lib/typescript/ui/navigation/bottomSheetManager.d.ts +74 -0
  793. package/lib/typescript/ui/navigation/bottomSheetManager.d.ts.map +1 -0
  794. package/lib/typescript/ui/navigation/routes.d.ts +4 -7
  795. package/lib/typescript/ui/navigation/routes.d.ts.map +1 -1
  796. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts +1 -1
  797. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  798. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts +1 -1
  799. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  800. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +1 -1
  801. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  802. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts +1 -1
  803. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  804. package/lib/typescript/ui/screens/AccountVerificationScreen.d.ts +1 -1
  805. package/lib/typescript/ui/screens/AccountVerificationScreen.d.ts.map +1 -1
  806. package/lib/typescript/ui/screens/AppInfoScreen.d.ts +1 -1
  807. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  808. package/lib/typescript/ui/screens/FeedbackScreen.d.ts +1 -1
  809. package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
  810. package/lib/typescript/ui/screens/FileManagementScreen.d.ts +1 -36
  811. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
  812. package/lib/typescript/ui/screens/HelpSupportScreen.d.ts +1 -1
  813. package/lib/typescript/ui/screens/HelpSupportScreen.d.ts.map +1 -1
  814. package/lib/typescript/ui/screens/HistoryViewScreen.d.ts +1 -1
  815. package/lib/typescript/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  816. package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts +1 -1
  817. package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  818. package/lib/typescript/ui/screens/LegalDocumentsScreen.d.ts +1 -1
  819. package/lib/typescript/ui/screens/LegalDocumentsScreen.d.ts.map +1 -1
  820. package/lib/typescript/ui/screens/OxyAuthScreen.d.ts +16 -0
  821. package/lib/typescript/ui/screens/OxyAuthScreen.d.ts.map +1 -0
  822. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts +1 -1
  823. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  824. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts +1 -1
  825. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  826. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts +1 -1
  827. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  828. package/lib/typescript/ui/screens/ProfileScreen.d.ts +1 -1
  829. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  830. package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts +1 -1
  831. package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts.map +1 -1
  832. package/lib/typescript/ui/screens/SearchSettingsScreen.d.ts +1 -1
  833. package/lib/typescript/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  834. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts +1 -1
  835. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  836. package/lib/typescript/ui/screens/UserLinksScreen.d.ts +2 -2
  837. package/lib/typescript/ui/screens/UserLinksScreen.d.ts.map +1 -1
  838. package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts +1 -1
  839. package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  840. package/lib/typescript/ui/screens/karma/KarmaAboutScreen.d.ts +1 -1
  841. package/lib/typescript/ui/screens/karma/KarmaAboutScreen.d.ts.map +1 -1
  842. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts +1 -1
  843. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
  844. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts +1 -1
  845. package/lib/typescript/ui/screens/karma/KarmaLeaderboardScreen.d.ts +1 -1
  846. package/lib/typescript/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +1 -1
  847. package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts +1 -1
  848. package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
  849. package/lib/typescript/ui/screens/karma/KarmaRulesScreen.d.ts +1 -1
  850. package/lib/typescript/ui/screens/karma/KarmaRulesScreen.d.ts.map +1 -1
  851. package/lib/typescript/ui/stores/authStore.d.ts +1 -2
  852. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  853. package/lib/typescript/ui/styles/authStyles.d.ts +0 -10
  854. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
  855. package/lib/typescript/ui/styles/spacing.d.ts +7 -1
  856. package/lib/typescript/ui/styles/spacing.d.ts.map +1 -1
  857. package/lib/typescript/ui/types/fileManagement.d.ts +41 -0
  858. package/lib/typescript/ui/types/fileManagement.d.ts.map +1 -0
  859. package/lib/typescript/ui/types/navigation.d.ts +36 -0
  860. package/lib/typescript/ui/types/navigation.d.ts.map +1 -0
  861. package/lib/typescript/ui/utils/colorUtils.d.ts +14 -0
  862. package/lib/typescript/ui/utils/colorUtils.d.ts.map +1 -0
  863. package/lib/typescript/ui/utils/errorHandlers.d.ts +30 -0
  864. package/lib/typescript/ui/utils/errorHandlers.d.ts.map +1 -0
  865. package/lib/typescript/ui/utils/fileManagement.d.ts +39 -0
  866. package/lib/typescript/ui/utils/fileManagement.d.ts.map +1 -0
  867. package/lib/typescript/ui/utils/sessionHelpers.d.ts +59 -0
  868. package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -0
  869. package/lib/typescript/ui/utils/storageHelpers.d.ts +31 -0
  870. package/lib/typescript/ui/utils/storageHelpers.d.ts.map +1 -0
  871. package/lib/typescript/ui/utils/themeUtils.d.ts +24 -0
  872. package/lib/typescript/ui/utils/themeUtils.d.ts.map +1 -0
  873. package/lib/typescript/ui/utils/user-utils.d.ts +29 -0
  874. package/lib/typescript/ui/utils/user-utils.d.ts.map +1 -0
  875. package/lib/typescript/utils/errorUtils.d.ts.map +1 -1
  876. package/package.json +26 -15
  877. package/src/assets/lottie/welcomeheader_background_op1.json +1 -0
  878. package/src/core/HttpService.ts +97 -4
  879. package/src/core/OxyServices.base.ts +3 -1
  880. package/src/core/mixins/OxyServices.assets.ts +225 -27
  881. package/src/core/mixins/OxyServices.auth.ts +144 -80
  882. package/src/crypto/index.ts +20 -0
  883. package/src/crypto/keyManager.ts +213 -0
  884. package/src/crypto/recoveryPhrase.ts +146 -0
  885. package/src/crypto/signatureService.ts +258 -0
  886. package/src/i18n/locales/en-US.json +4 -0
  887. package/src/index.ts +13 -0
  888. package/src/models/interfaces.ts +1 -0
  889. package/src/types/bip39.d.ts +28 -0
  890. package/src/types/color.d.ts +18 -0
  891. package/src/types/elliptic.d.ts +60 -0
  892. package/src/types/expo-crypto.d.ts +28 -0
  893. package/src/types/expo-random.d.ts +8 -0
  894. package/src/types/expo-secure-store.d.ts +20 -0
  895. package/src/types/expo-vector-icons.d.ts +9 -0
  896. package/src/ui/components/ActivityIndicator.tsx +254 -0
  897. package/src/ui/components/AutoHeightScrollView.tsx +50 -0
  898. package/src/ui/components/BottomSheet.tsx +443 -0
  899. package/src/ui/components/BottomSheetRouter.tsx +407 -0
  900. package/src/ui/components/CrossFadeIcon.tsx +140 -0
  901. package/src/ui/components/EmptyState.tsx +39 -0
  902. package/src/ui/components/GroupedItem.tsx +98 -96
  903. package/src/ui/components/GroupedSection.tsx +24 -29
  904. package/src/ui/components/Header.tsx +129 -49
  905. package/src/ui/components/HelperText.tsx +160 -0
  906. package/src/ui/components/Icon.tsx +181 -0
  907. package/src/ui/components/IconButton/IconButton.tsx +235 -0
  908. package/src/ui/components/IconButton/utils.ts +190 -0
  909. package/src/ui/components/LoadingState.tsx +46 -0
  910. package/src/ui/components/OxyPayButton.tsx +1 -12
  911. package/src/ui/components/OxyProvider.tsx +49 -409
  912. package/src/ui/components/OxySignInButton.tsx +11 -18
  913. package/src/ui/components/ProfileCard.tsx +7 -4
  914. package/src/ui/components/QuickActions.tsx +7 -4
  915. package/src/ui/components/Section.tsx +6 -2
  916. package/src/ui/components/SectionTitle.tsx +7 -4
  917. package/src/ui/components/SettingRow.tsx +76 -0
  918. package/src/ui/components/StepBasedScreen.tsx +205 -187
  919. package/src/ui/components/Surface.tsx +384 -0
  920. package/src/ui/components/TextField/Addons/Outline.tsx +64 -0
  921. package/src/ui/components/TextField/Addons/Underline.tsx +78 -0
  922. package/src/ui/components/TextField/Adornment/TextFieldAdornment.tsx +208 -0
  923. package/src/ui/components/TextField/Adornment/TextFieldAffix.tsx +212 -0
  924. package/src/ui/components/TextField/Adornment/TextFieldIcon.tsx +195 -0
  925. package/src/ui/components/TextField/Adornment/enums.tsx +12 -0
  926. package/src/ui/components/TextField/Adornment/types.tsx +11 -0
  927. package/src/ui/components/TextField/Adornment/utils.ts +66 -0
  928. package/src/ui/components/TextField/Label/InputLabel.tsx +219 -0
  929. package/src/ui/components/TextField/Label/LabelBackground.tsx +100 -0
  930. package/src/ui/components/TextField/TextFieldFlat.tsx +488 -0
  931. package/src/ui/components/TextField/TextFieldOutlined.tsx +464 -0
  932. package/src/ui/components/TextField/constants.tsx +48 -0
  933. package/src/ui/components/TextField/helpers.tsx +612 -0
  934. package/src/ui/components/TextField/types.tsx +156 -0
  935. package/src/ui/components/TextField.tsx +578 -0
  936. package/src/ui/components/TouchableRipple/Pressable.tsx +41 -0
  937. package/src/ui/components/TouchableRipple/TouchableRipple.native.tsx +146 -0
  938. package/src/ui/components/TouchableRipple/TouchableRipple.tsx +347 -0
  939. package/src/ui/components/TouchableRipple/utils.ts +66 -0
  940. package/src/ui/components/Typography/AnimatedText.tsx +107 -0
  941. package/src/ui/components/Typography/types.tsx +22 -0
  942. package/src/ui/components/fileManagement/FileDetailsModal.tsx +137 -0
  943. package/src/ui/components/fileManagement/FileViewer.tsx +380 -0
  944. package/src/ui/components/fileManagement/UploadPreview.tsx +175 -0
  945. package/src/ui/components/fileManagement/styles.ts +860 -0
  946. package/src/ui/components/index.ts +4 -1
  947. package/src/ui/components/profile/EditBioModal.tsx +184 -0
  948. package/src/ui/components/profile/EditDisplayNameModal.tsx +205 -0
  949. package/src/ui/components/profile/EditEmailModal.tsx +188 -0
  950. package/src/ui/components/profile/EditLinksModal.tsx +316 -0
  951. package/src/ui/components/profile/EditLocationModal.tsx +278 -0
  952. package/src/ui/components/profile/EditUsernameModal.tsx +183 -0
  953. package/src/ui/components/profile/TwoFactorSetupModal.tsx +442 -0
  954. package/src/ui/components/styles/overlay.tsx +88 -0
  955. package/src/ui/components/styles/shadow.tsx +136 -0
  956. package/src/ui/components/theming.tsx +114 -0
  957. package/src/ui/components/types.tsx +90 -0
  958. package/src/ui/components/utils/forwardRef.tsx +23 -0
  959. package/src/ui/components/utils/hasTouchHandler.tsx +23 -0
  960. package/src/ui/components/utils/roundLayoutSize.ts +2 -0
  961. package/src/ui/components/utils/splitStyles.ts +60 -0
  962. package/src/ui/constants/iconColors.ts +88 -0
  963. package/src/ui/constants/spacing.ts +45 -0
  964. package/src/ui/constants/theme.ts +120 -0
  965. package/src/ui/context/OxyContext.tsx +276 -894
  966. package/src/ui/context/ThemeContext.tsx +41 -0
  967. package/src/ui/context/hooks/useAuthOperations.ts +400 -0
  968. package/src/ui/context/hooks/useDeviceManagement.ts +108 -0
  969. package/src/ui/context/hooks/useLanguageManagement.ts +152 -0
  970. package/src/ui/context/hooks/useSessionManagement.ts +378 -0
  971. package/src/ui/context/hooks/useStorage.ts +104 -0
  972. package/src/ui/context/utils/errorHandlers.ts +136 -0
  973. package/src/ui/context/utils/sessionHelpers.ts +146 -0
  974. package/src/ui/context/utils/storageHelpers.ts +134 -0
  975. package/src/ui/hooks/index.ts +3 -1
  976. package/src/ui/hooks/use-color-scheme.ts +24 -0
  977. package/src/ui/hooks/use-haptic-press.ts +15 -0
  978. package/src/ui/hooks/useAssets.ts +1 -1
  979. package/src/ui/hooks/useDeviceManagement.ts +108 -0
  980. package/src/ui/hooks/useLanguageManagement.ts +152 -0
  981. package/src/ui/hooks/useProfileEditing.ts +151 -0
  982. package/src/ui/hooks/useSessionManagement.ts +378 -0
  983. package/src/ui/hooks/useStorage.ts +104 -0
  984. package/src/ui/hooks/useThemeColors.ts +26 -0
  985. package/src/ui/hooks/useThemeStyles.ts +85 -0
  986. package/src/ui/index.ts +1 -4
  987. package/src/ui/navigation/bottomSheetManager.ts +191 -0
  988. package/src/ui/navigation/routes.ts +107 -189
  989. package/src/ui/screens/AccountCenterScreen.tsx +67 -63
  990. package/src/ui/screens/AccountOverviewScreen.tsx +237 -95
  991. package/src/ui/screens/AccountSettingsScreen.tsx +546 -474
  992. package/src/ui/screens/AccountSwitcherScreen.tsx +113 -116
  993. package/src/ui/screens/AccountVerificationScreen.tsx +18 -20
  994. package/src/ui/screens/AppInfoScreen.tsx +37 -38
  995. package/src/ui/screens/FeedbackScreen.tsx +15 -12
  996. package/src/ui/screens/FileManagementScreen.tsx +764 -2026
  997. package/src/ui/screens/HelpSupportScreen.tsx +30 -23
  998. package/src/ui/screens/HistoryViewScreen.tsx +25 -56
  999. package/src/ui/screens/LanguageSelectorScreen.tsx +84 -170
  1000. package/src/ui/screens/LegalDocumentsScreen.tsx +18 -41
  1001. package/src/ui/screens/OxyAuthScreen.tsx +402 -0
  1002. package/src/ui/screens/PaymentGatewayScreen.tsx +20 -22
  1003. package/src/ui/screens/PremiumSubscriptionScreen.tsx +13 -10
  1004. package/src/ui/screens/PrivacySettingsScreen.tsx +150 -198
  1005. package/src/ui/screens/ProfileScreen.tsx +7 -6
  1006. package/src/ui/screens/SavesCollectionsScreen.tsx +39 -62
  1007. package/src/ui/screens/SearchSettingsScreen.tsx +36 -86
  1008. package/src/ui/screens/SessionManagementScreen.tsx +25 -17
  1009. package/src/ui/screens/UserLinksScreen.tsx +10 -11
  1010. package/src/ui/screens/WelcomeNewUserScreen.tsx +41 -41
  1011. package/src/ui/screens/karma/KarmaAboutScreen.tsx +13 -9
  1012. package/src/ui/screens/karma/KarmaCenterScreen.tsx +70 -70
  1013. package/src/ui/screens/karma/KarmaFAQScreen.tsx +96 -96
  1014. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +15 -10
  1015. package/src/ui/screens/karma/KarmaRewardsScreen.tsx +605 -60
  1016. package/src/ui/screens/karma/KarmaRulesScreen.tsx +14 -9
  1017. package/src/ui/stores/authStore.ts +1 -1
  1018. package/src/ui/styles/authStyles.ts +5 -11
  1019. package/src/ui/styles/spacing.ts +21 -2
  1020. package/src/ui/styles/theme.ts +1 -1
  1021. package/src/ui/types/fileManagement.ts +51 -0
  1022. package/src/ui/types/navigation.ts +61 -0
  1023. package/src/ui/utils/colorUtils.ts +46 -0
  1024. package/src/ui/utils/errorHandlers.ts +136 -0
  1025. package/src/ui/utils/fileManagement.ts +190 -0
  1026. package/src/ui/utils/sessionHelpers.ts +146 -0
  1027. package/src/ui/utils/storageHelpers.ts +134 -0
  1028. package/src/ui/utils/themeUtils.ts +43 -0
  1029. package/src/ui/utils/user-utils.ts +58 -0
  1030. package/src/utils/errorUtils.ts +14 -4
  1031. package/lib/commonjs/ui/components/internal/TextField.js +0 -665
  1032. package/lib/commonjs/ui/components/internal/TextField.js.map +0 -1
  1033. package/lib/commonjs/ui/navigation/OxyRouter.js +0 -213
  1034. package/lib/commonjs/ui/navigation/OxyRouter.js.map +0 -1
  1035. package/lib/commonjs/ui/navigation/types.js +0 -13
  1036. package/lib/commonjs/ui/navigation/types.js.map +0 -1
  1037. package/lib/commonjs/ui/screens/AccountManagementDemo.js +0 -298
  1038. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +0 -1
  1039. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +0 -137
  1040. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +0 -1
  1041. package/lib/commonjs/ui/screens/SignInScreen.js +0 -270
  1042. package/lib/commonjs/ui/screens/SignInScreen.js.map +0 -1
  1043. package/lib/commonjs/ui/screens/SignUpScreen.js +0 -229
  1044. package/lib/commonjs/ui/screens/SignUpScreen.js.map +0 -1
  1045. package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js +0 -135
  1046. package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js.map +0 -1
  1047. package/lib/commonjs/ui/screens/steps/RecoverResetPasswordStep.js +0 -165
  1048. package/lib/commonjs/ui/screens/steps/RecoverResetPasswordStep.js.map +0 -1
  1049. package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js +0 -159
  1050. package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js.map +0 -1
  1051. package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js +0 -144
  1052. package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js.map +0 -1
  1053. package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js +0 -358
  1054. package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js.map +0 -1
  1055. package/lib/commonjs/ui/screens/steps/SignInTotpStep.js +0 -188
  1056. package/lib/commonjs/ui/screens/steps/SignInTotpStep.js.map +0 -1
  1057. package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js +0 -509
  1058. package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js.map +0 -1
  1059. package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js +0 -200
  1060. package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js.map +0 -1
  1061. package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js +0 -185
  1062. package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js.map +0 -1
  1063. package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js +0 -138
  1064. package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js.map +0 -1
  1065. package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js +0 -150
  1066. package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js.map +0 -1
  1067. package/lib/module/package.json +0 -1
  1068. package/lib/module/ui/components/internal/TextField.js +0 -660
  1069. package/lib/module/ui/components/internal/TextField.js.map +0 -1
  1070. package/lib/module/ui/navigation/OxyRouter.js +0 -209
  1071. package/lib/module/ui/navigation/OxyRouter.js.map +0 -1
  1072. package/lib/module/ui/navigation/types.js +0 -22
  1073. package/lib/module/ui/navigation/types.js.map +0 -1
  1074. package/lib/module/ui/screens/AccountManagementDemo.js +0 -295
  1075. package/lib/module/ui/screens/AccountManagementDemo.js.map +0 -1
  1076. package/lib/module/ui/screens/RecoverAccountScreen.js +0 -133
  1077. package/lib/module/ui/screens/RecoverAccountScreen.js.map +0 -1
  1078. package/lib/module/ui/screens/SignInScreen.js +0 -265
  1079. package/lib/module/ui/screens/SignInScreen.js.map +0 -1
  1080. package/lib/module/ui/screens/SignUpScreen.js +0 -224
  1081. package/lib/module/ui/screens/SignUpScreen.js.map +0 -1
  1082. package/lib/module/ui/screens/steps/RecoverRequestStep.js +0 -130
  1083. package/lib/module/ui/screens/steps/RecoverRequestStep.js.map +0 -1
  1084. package/lib/module/ui/screens/steps/RecoverResetPasswordStep.js +0 -160
  1085. package/lib/module/ui/screens/steps/RecoverResetPasswordStep.js.map +0 -1
  1086. package/lib/module/ui/screens/steps/RecoverSuccessStep.js +0 -154
  1087. package/lib/module/ui/screens/steps/RecoverSuccessStep.js.map +0 -1
  1088. package/lib/module/ui/screens/steps/RecoverVerifyStep.js +0 -139
  1089. package/lib/module/ui/screens/steps/RecoverVerifyStep.js.map +0 -1
  1090. package/lib/module/ui/screens/steps/SignInPasswordStep.js +0 -353
  1091. package/lib/module/ui/screens/steps/SignInPasswordStep.js.map +0 -1
  1092. package/lib/module/ui/screens/steps/SignInTotpStep.js +0 -183
  1093. package/lib/module/ui/screens/steps/SignInTotpStep.js.map +0 -1
  1094. package/lib/module/ui/screens/steps/SignInUsernameStep.js +0 -504
  1095. package/lib/module/ui/screens/steps/SignInUsernameStep.js.map +0 -1
  1096. package/lib/module/ui/screens/steps/SignUpIdentityStep.js +0 -195
  1097. package/lib/module/ui/screens/steps/SignUpIdentityStep.js.map +0 -1
  1098. package/lib/module/ui/screens/steps/SignUpSecurityStep.js +0 -180
  1099. package/lib/module/ui/screens/steps/SignUpSecurityStep.js.map +0 -1
  1100. package/lib/module/ui/screens/steps/SignUpSummaryStep.js +0 -133
  1101. package/lib/module/ui/screens/steps/SignUpSummaryStep.js.map +0 -1
  1102. package/lib/module/ui/screens/steps/SignUpWelcomeStep.js +0 -145
  1103. package/lib/module/ui/screens/steps/SignUpWelcomeStep.js.map +0 -1
  1104. package/lib/typescript/ui/components/internal/TextField.d.ts +0 -41
  1105. package/lib/typescript/ui/components/internal/TextField.d.ts.map +0 -1
  1106. package/lib/typescript/ui/navigation/OxyRouter.d.ts +0 -7
  1107. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +0 -1
  1108. package/lib/typescript/ui/navigation/types.d.ts +0 -141
  1109. package/lib/typescript/ui/navigation/types.d.ts.map +0 -1
  1110. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts +0 -8
  1111. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts.map +0 -1
  1112. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +0 -5
  1113. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +0 -1
  1114. package/lib/typescript/ui/screens/SignInScreen.d.ts +0 -5
  1115. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +0 -1
  1116. package/lib/typescript/ui/screens/SignUpScreen.d.ts +0 -5
  1117. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +0 -1
  1118. package/lib/typescript/ui/screens/steps/RecoverRequestStep.d.ts +0 -24
  1119. package/lib/typescript/ui/screens/steps/RecoverRequestStep.d.ts.map +0 -1
  1120. package/lib/typescript/ui/screens/steps/RecoverResetPasswordStep.d.ts +0 -24
  1121. package/lib/typescript/ui/screens/steps/RecoverResetPasswordStep.d.ts.map +0 -1
  1122. package/lib/typescript/ui/screens/steps/RecoverSuccessStep.d.ts +0 -19
  1123. package/lib/typescript/ui/screens/steps/RecoverSuccessStep.d.ts.map +0 -1
  1124. package/lib/typescript/ui/screens/steps/RecoverVerifyStep.d.ts +0 -26
  1125. package/lib/typescript/ui/screens/steps/RecoverVerifyStep.d.ts.map +0 -1
  1126. package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts +0 -30
  1127. package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts.map +0 -1
  1128. package/lib/typescript/ui/screens/steps/SignInTotpStep.d.ts +0 -19
  1129. package/lib/typescript/ui/screens/steps/SignInTotpStep.d.ts.map +0 -1
  1130. package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts +0 -28
  1131. package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts.map +0 -1
  1132. package/lib/typescript/ui/screens/steps/SignUpIdentityStep.d.ts +0 -26
  1133. package/lib/typescript/ui/screens/steps/SignUpIdentityStep.d.ts.map +0 -1
  1134. package/lib/typescript/ui/screens/steps/SignUpSecurityStep.d.ts +0 -27
  1135. package/lib/typescript/ui/screens/steps/SignUpSecurityStep.d.ts.map +0 -1
  1136. package/lib/typescript/ui/screens/steps/SignUpSummaryStep.d.ts +0 -17
  1137. package/lib/typescript/ui/screens/steps/SignUpSummaryStep.d.ts.map +0 -1
  1138. package/lib/typescript/ui/screens/steps/SignUpWelcomeStep.d.ts +0 -14
  1139. package/lib/typescript/ui/screens/steps/SignUpWelcomeStep.d.ts.map +0 -1
  1140. package/src/ui/components/internal/TextField.tsx +0 -808
  1141. package/src/ui/navigation/OxyRouter.tsx +0 -216
  1142. package/src/ui/navigation/types.ts +0 -164
  1143. package/src/ui/screens/AccountManagementDemo.tsx +0 -297
  1144. package/src/ui/screens/RecoverAccountScreen.tsx +0 -141
  1145. package/src/ui/screens/SignInScreen.tsx +0 -297
  1146. package/src/ui/screens/SignUpScreen.tsx +0 -239
  1147. package/src/ui/screens/steps/RecoverRequestStep.tsx +0 -143
  1148. package/src/ui/screens/steps/RecoverResetPasswordStep.tsx +0 -162
  1149. package/src/ui/screens/steps/RecoverSuccessStep.tsx +0 -148
  1150. package/src/ui/screens/steps/RecoverVerifyStep.tsx +0 -154
  1151. package/src/ui/screens/steps/SignInPasswordStep.tsx +0 -343
  1152. package/src/ui/screens/steps/SignInTotpStep.tsx +0 -163
  1153. package/src/ui/screens/steps/SignInUsernameStep.tsx +0 -560
  1154. package/src/ui/screens/steps/SignUpIdentityStep.tsx +0 -217
  1155. package/src/ui/screens/steps/SignUpSecurityStep.tsx +0 -207
  1156. package/src/ui/screens/steps/SignUpSummaryStep.tsx +0 -155
  1157. package/src/ui/screens/steps/SignUpWelcomeStep.tsx +0 -127
@@ -6,74 +6,93 @@ import {
6
6
  StyleSheet,
7
7
  ScrollView,
8
8
  ActivityIndicator,
9
- Platform,
10
9
  RefreshControl,
11
- Dimensions,
12
- Modal,
13
10
  TextInput,
14
- Image, // kept for Image.getSize only
11
+ Image,
15
12
  Animated,
16
13
  Easing,
17
14
  Alert,
18
15
  } from 'react-native';
19
16
  import { Image as ExpoImage } from 'expo-image';
20
- import type { BaseScreenProps } from '../navigation/types';
21
- import { useOxy } from '../context/OxyContext';
22
- import { fontFamilies } from '../styles/fonts';
17
+ import type { FileManagementScreenProps } from '../types/fileManagement';
23
18
  import { toast } from '../../lib/sonner';
24
19
  import { Ionicons } from '@expo/vector-icons';
20
+ // @ts-ignore - MaterialCommunityIcons is available at runtime
21
+ import { MaterialCommunityIcons } from '@expo/vector-icons';
25
22
  import type { FileMetadata } from '../../models/interfaces';
26
23
  import { useFileStore, useFiles, useUploading as useUploadingStore, useUploadAggregateProgress, useDeleting as useDeletingStore } from '../stores/fileStore';
27
24
  import Header from '../components/Header';
28
25
  import JustifiedPhotoGrid from '../components/photogrid/JustifiedPhotoGrid';
29
26
  import { GroupedSection } from '../components';
27
+ import { useThemeStyles } from '../hooks/useThemeStyles';
28
+ import { useColorScheme } from '../hooks/use-color-scheme';
29
+ import { normalizeTheme } from '../utils/themeUtils';
30
+ import { useOxy } from '../context/OxyContext';
31
+ import {
32
+ confirmAction,
33
+ convertDocumentPickerAssetToFile,
34
+ formatFileSize,
35
+ getFileIcon,
36
+ getSafeDownloadUrl,
37
+ uploadFileRaw
38
+ } from '../utils/fileManagement';
39
+ import { FileViewer } from '../components/fileManagement/FileViewer';
40
+ import { FileDetailsModal } from '../components/fileManagement/FileDetailsModal';
41
+ import { UploadPreview } from '../components/fileManagement/UploadPreview';
42
+ import { fileManagementStyles } from '../components/fileManagement/styles';
43
+ import type { OnConfirmFileSelection } from '../types/fileManagement';
44
+
45
+ // Animated button component for smooth transitions
46
+ const AnimatedButton: React.FC<{
47
+ isSelected: boolean;
48
+ onPress: () => void;
49
+ icon: string;
50
+ primaryColor: string;
51
+ textColor: string;
52
+ style: any;
53
+ }> = ({ isSelected, onPress, icon, primaryColor, textColor, style }) => {
54
+ const animatedValue = useRef(new Animated.Value(isSelected ? 1 : 0)).current;
30
55
 
31
- // Exporting props & callback types so external callers (e.g. showBottomSheet config objects) can annotate
32
- export type OnConfirmFileSelection = (files: FileMetadata[]) => void;
33
-
34
- export interface FileManagementScreenProps extends BaseScreenProps {
35
- userId?: string;
36
- // Enable selection mode (acts like a picker). When true, opening a file selects it instead of showing viewer
37
- selectMode?: boolean;
38
- // Allow selecting multiple files; only used if selectMode is true
39
- multiSelect?: boolean;
40
- // Callback when a file is selected (single select mode)
41
- onSelect?: (file: FileMetadata) => void;
42
- // Callback when confirm pressed in multi-select mode
43
- onConfirmSelection?: OnConfirmFileSelection;
44
- // Initial selected file IDs for multi-select
45
- initialSelectedIds?: string[];
46
- maxSelection?: number;
47
- disabledMimeTypes?: string[];
48
- /**
49
- * What to do after a single selection (non-multiSelect) is made.
50
- * 'close' (default) will dismiss the bottom sheet via onClose.
51
- * 'back' will navigate back to the previous screen (e.g., return to AccountSettings without closing sheet).
52
- * 'none' will keep the picker open (caller can manually close or navigate).
53
- */
54
- afterSelect?: 'close' | 'back' | 'none';
55
- allowUploadInSelectMode?: boolean;
56
- /**
57
- * Default visibility for uploaded files in this screen
58
- * Useful for third-party apps that want files to be public (e.g., GIF selector)
59
- */
60
- defaultVisibility?: 'private' | 'public' | 'unlisted';
61
- /**
62
- * Link context for tracking file usage by third-party apps
63
- * When provided, selected files will be linked to this entity
64
- */
65
- linkContext?: {
66
- app: string; // App identifier (e.g., 'chat-app', 'post-composer')
67
- entityType: string; // Type of entity (e.g., 'message', 'post', 'profile')
68
- entityId: string; // Unique ID of the entity using this file
69
- webhookUrl?: string; // Optional webhook URL to receive file events
70
- };
71
- }
56
+ useEffect(() => {
57
+ Animated.timing(animatedValue, {
58
+ toValue: isSelected ? 1 : 0,
59
+ duration: 200,
60
+ easing: Easing.out(Easing.ease),
61
+ useNativeDriver: false,
62
+ }).start();
63
+ }, [isSelected, animatedValue]);
64
+
65
+ const backgroundColor = animatedValue.interpolate({
66
+ inputRange: [0, 1],
67
+ outputRange: ['transparent', primaryColor],
68
+ });
72
69
 
73
- // Add this helper function near the top (after imports):
74
- async function uploadFileRaw(file: File | Blob, userId: string, oxyServices: any, visibility?: 'private' | 'public' | 'unlisted') {
75
- return await oxyServices.uploadRawFile(file, visibility);
76
- }
70
+ const iconColor = animatedValue.interpolate({
71
+ inputRange: [0, 1],
72
+ outputRange: [textColor, '#FFFFFF'],
73
+ });
74
+
75
+ return (
76
+ <TouchableOpacity onPress={onPress} activeOpacity={0.7}>
77
+ <Animated.View
78
+ style={[
79
+ style,
80
+ {
81
+ backgroundColor,
82
+ },
83
+ ]}
84
+ >
85
+ <Animated.View>
86
+ <MaterialCommunityIcons
87
+ name={icon as any}
88
+ size={16}
89
+ color={isSelected ? '#FFFFFF' : textColor}
90
+ />
91
+ </Animated.View>
92
+ </Animated.View>
93
+ </TouchableOpacity>
94
+ );
95
+ };
77
96
 
78
97
  const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
79
98
  onClose,
@@ -94,8 +113,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
94
113
  defaultVisibility = 'private',
95
114
  linkContext,
96
115
  }) => {
116
+ // Use useOxy() hook for OxyContext values
97
117
  const { user, oxyServices } = useOxy();
98
118
  const files = useFiles();
119
+ // Ensure containerWidth is a number (TypeScript guard)
120
+ const safeContainerWidth: number = typeof containerWidth === 'number' ? containerWidth : 400;
99
121
  const uploading = useUploadingStore();
100
122
  const uploadProgress = useUploadAggregateProgress();
101
123
  const deleting = useDeletingStore();
@@ -109,6 +131,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
109
131
  const [fileContent, setFileContent] = useState<string | null>(null);
110
132
  const [loadingFileContent, setLoadingFileContent] = useState(false);
111
133
  const [showFileDetailsInViewer, setShowFileDetailsInViewer] = useState(false);
134
+ const [isPickingDocument, setIsPickingDocument] = useState(false);
112
135
  const [viewMode, setViewMode] = useState<'all' | 'photos' | 'videos' | 'documents' | 'audio'>('all');
113
136
  const [searchQuery, setSearchQuery] = useState('');
114
137
  const [sortBy, setSortBy] = useState<'date' | 'size' | 'name' | 'type'>('date');
@@ -123,7 +146,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
123
146
  } else if (viewMode === 'videos') {
124
147
  filteredByMode = files.filter(file => file.contentType.startsWith('video/'));
125
148
  } else if (viewMode === 'documents') {
126
- filteredByMode = files.filter(file =>
149
+ filteredByMode = files.filter(file =>
127
150
  file.contentType.includes('pdf') ||
128
151
  file.contentType.includes('document') ||
129
152
  file.contentType.includes('text') ||
@@ -136,7 +159,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
136
159
  } else if (viewMode === 'audio') {
137
160
  filteredByMode = files.filter(file => file.contentType.startsWith('audio/'));
138
161
  }
139
-
162
+
140
163
  let filtered = filteredByMode;
141
164
  if (searchQuery.trim()) {
142
165
  const query = searchQuery.toLowerCase();
@@ -146,7 +169,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
146
169
  (file.metadata?.description && file.metadata.description.toLowerCase().includes(query))
147
170
  );
148
171
  }
149
-
172
+
150
173
  // Sort files
151
174
  const sorted = [...filtered].sort((a, b) => {
152
175
  let comparison = 0;
@@ -163,13 +186,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
163
186
  }
164
187
  return sortOrder === 'asc' ? comparison : -comparison;
165
188
  });
166
-
189
+
167
190
  return sorted;
168
191
  }, [files, searchQuery, viewMode, sortBy, sortOrder]);
169
- const [isDragging, setIsDragging] = useState(false);
170
192
  const [photoDimensions, setPhotoDimensions] = useState<{ [key: string]: { width: number, height: number } }>({});
171
193
  const [loadingDimensions, setLoadingDimensions] = useState(false);
172
- const [hoveredPreview, setHoveredPreview] = useState<string | null>(null);
173
194
  const uploadStartRef = useRef<number | null>(null);
174
195
  const MIN_BANNER_MS = 600;
175
196
  // Selection state
@@ -178,7 +199,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
178
199
  const scrollViewRef = useRef<ScrollView>(null);
179
200
  const photoScrollViewRef = useRef<ScrollView>(null);
180
201
  const itemRefs = useRef<Map<string, number>>(new Map()); // Track item positions
181
- const containerRef = useRef<View>(null); // Ref for drag and drop container
202
+ const containerRef = useRef<View>(null);
182
203
  useEffect(() => {
183
204
  if (initialSelectedIds && initialSelectedIds.length) {
184
205
  setSelectedIds(new Set(initialSelectedIds));
@@ -205,7 +226,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
205
226
  // Continue anyway - selection shouldn't fail if visibility update fails
206
227
  }
207
228
  }
208
-
229
+
209
230
  // Track the selected file for scrolling
210
231
  setLastSelectedFileId(file.id);
211
232
 
@@ -303,56 +324,22 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
303
324
  }, []);
304
325
 
305
326
  // Helper to safely request a thumbnail variant only for image mime types.
306
- // Prevents backend warnings: "Variant thumb not supported for mime application/pdf".
307
- const getSafeDownloadUrl = useCallback(
327
+ const getSafeDownloadUrlCallback = useCallback(
308
328
  (file: FileMetadata, variant: string = 'thumb') => {
309
- const isImage = file.contentType.startsWith('image/');
310
- const isVideo = file.contentType.startsWith('video/');
311
-
312
- // Prefer explicit variant key if variants metadata present
313
- if (file.variants && file.variants.length > 0) {
314
- // For videos, try 'poster' regardless of requested variant
315
- if (isVideo) {
316
- const poster = file.variants.find(v => v.type === 'poster');
317
- if (poster) return oxyServices.getFileDownloadUrl(file.id, 'poster');
318
- }
319
- if (isImage) {
320
- const desired = file.variants.find(v => v.type === variant);
321
- if (desired) return oxyServices.getFileDownloadUrl(file.id, variant);
322
- }
323
- }
324
-
325
- if (isImage) {
326
- return oxyServices.getFileDownloadUrl(file.id, variant);
327
- }
328
- if (isVideo) {
329
- // Fallback to poster if backend supports implicit generation
330
- try {
331
- return oxyServices.getFileDownloadUrl(file.id, 'poster');
332
- } catch {
333
- return oxyServices.getFileDownloadUrl(file.id);
334
- }
335
- }
336
- // Other mime types: no variant
337
- return oxyServices.getFileDownloadUrl(file.id);
329
+ return getSafeDownloadUrl(file, variant, (fileId: string, variant?: string) => oxyServices.getFileDownloadUrl(fileId, variant));
338
330
  },
339
331
  [oxyServices]
340
332
  );
341
333
 
342
- // Memoize theme-related calculations to prevent unnecessary recalculations
343
- const themeStyles = useMemo(() => {
344
- const isDarkTheme = theme === 'dark';
345
- return {
346
- isDarkTheme,
347
- textColor: isDarkTheme ? '#FFFFFF' : '#000000',
348
- backgroundColor: isDarkTheme ? '#121212' : '#f2f2f2',
349
- secondaryBackgroundColor: isDarkTheme ? '#222222' : '#FFFFFF',
350
- borderColor: isDarkTheme ? '#444444' : '#E0E0E0',
351
- primaryColor: '#007AFF',
352
- dangerColor: '#FF3B30',
353
- successColor: '#34C759',
354
- };
355
- }, [theme]);
334
+ // Use centralized theme styles hook for consistency
335
+ const colorScheme = useColorScheme();
336
+ const normalizedTheme = normalizeTheme(theme);
337
+ const baseThemeStyles = useThemeStyles(normalizedTheme, colorScheme);
338
+ // FileManagementScreen uses a slightly different light background
339
+ const themeStyles = useMemo(() => ({
340
+ ...baseThemeStyles,
341
+ backgroundColor: baseThemeStyles.isDarkTheme ? baseThemeStyles.backgroundColor : '#f2f2f2',
342
+ }), [baseThemeStyles]);
356
343
 
357
344
  // Extract commonly used theme variables
358
345
  const backgroundColor = themeStyles.backgroundColor;
@@ -425,7 +412,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
425
412
 
426
413
  // (removed effect; filteredFiles is memoized)
427
414
 
428
- // Load photo dimensions for justified grid
415
+ // Load photo dimensions for justified grid - unified approach using Image.getSize
429
416
  const loadPhotoDimensions = useCallback(async (photos: FileMetadata[]) => {
430
417
  if (photos.length === 0) return;
431
418
 
@@ -445,46 +432,25 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
445
432
  await Promise.all(
446
433
  photosToLoad.map(async (photo) => {
447
434
  try {
448
- const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
449
-
450
- if (Platform.OS === 'web') {
451
- const img = new (window as any).Image();
452
- await new Promise<void>((resolve, reject) => {
453
- img.onload = () => {
454
- newDimensions[photo.id] = {
455
- width: img.naturalWidth,
456
- height: img.naturalHeight
457
- };
435
+ const downloadUrl = getSafeDownloadUrlCallback(photo, 'thumb');
436
+
437
+ // Unified approach using Image.getSize (works on all platforms)
438
+ await new Promise<void>((resolve) => {
439
+ Image.getSize(
440
+ downloadUrl,
441
+ (width: number, height: number) => {
442
+ newDimensions[photo.id] = { width, height };
458
443
  hasNewDimensions = true;
459
444
  resolve();
460
- };
461
- img.onerror = () => {
462
- // Fallback dimensions for failed loads
445
+ },
446
+ () => {
447
+ // Fallback dimensions
463
448
  newDimensions[photo.id] = { width: 1, height: 1 };
464
449
  hasNewDimensions = true;
465
450
  resolve();
466
- };
467
- img.src = downloadUrl;
468
- });
469
- } else {
470
- // For mobile, use Image.getSize from react-native
471
- await new Promise<void>((resolve) => {
472
- Image.getSize(
473
- downloadUrl,
474
- (width: number, height: number) => {
475
- newDimensions[photo.id] = { width, height };
476
- hasNewDimensions = true;
477
- resolve();
478
- },
479
- () => {
480
- // Fallback dimensions
481
- newDimensions[photo.id] = { width: 1, height: 1 };
482
- hasNewDimensions = true;
483
- resolve();
484
- }
485
- );
486
- });
487
- }
451
+ }
452
+ );
453
+ });
488
454
  } catch (error) {
489
455
  // Fallback dimensions for any errors
490
456
  newDimensions[photo.id] = { width: 1, height: 1 };
@@ -501,7 +467,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
501
467
  } finally {
502
468
  setLoadingDimensions(false);
503
469
  }
504
- }, [oxyServices, photoDimensions]);
470
+ }, [getSafeDownloadUrlCallback, photoDimensions]);
505
471
 
506
472
  // Create justified rows from photos with responsive algorithm
507
473
  const createJustifiedRows = useCallback((photos: FileMetadata[], containerWidth: number) => {
@@ -518,26 +484,38 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
518
484
  return rows;
519
485
  }, []);
520
486
 
521
- const processFileUploads = async (selectedFiles: File[]) => {
522
- if (selectedFiles.length === 0) return;
523
- if (!targetUserId) return; // Guard clause to ensure userId is defined
487
+ const processFileUploads = async (selectedFiles: File[]): Promise<FileMetadata[]> => {
488
+ if (selectedFiles.length === 0) return [];
489
+ if (!targetUserId) return []; // Guard clause to ensure userId is defined
490
+ const uploadedFiles: FileMetadata[] = [];
524
491
  try {
525
492
  storeSetUploadProgress({ current: 0, total: selectedFiles.length });
526
493
  const maxSize = 50 * 1024 * 1024; // 50MB
527
494
  const oversizedFiles = selectedFiles.filter(file => file.size > maxSize);
528
495
  if (oversizedFiles.length > 0) {
529
- const fileList = oversizedFiles.map(f => f.name).join('\n');
530
- window.alert(`File Size Limit\n\nThe following files are too large (max 50MB):\n${fileList}`);
531
- return;
496
+ const fileList = oversizedFiles.map(f => f.name).join(', ');
497
+ toast.error(`The following files are too large (max 50MB): ${fileList}`);
498
+ return [];
532
499
  }
533
500
  let successCount = 0;
534
501
  let failureCount = 0;
535
502
  const errors: string[] = [];
536
503
  for (let i = 0; i < selectedFiles.length; i++) {
537
504
  storeSetUploadProgress({ current: i + 1, total: selectedFiles.length });
505
+ const raw = selectedFiles[i];
506
+ const fileName = raw.name || `file-${i + 1}`;
507
+ const optimisticId = `temp-${Date.now()}-${i}-${Math.random().toString(36).substr(2, 9)}`; // Unique ID per file
508
+
538
509
  try {
539
- const raw = selectedFiles[i];
540
- const optimisticId = `temp-${Date.now()}-${i}`;
510
+ // Validate file before upload
511
+ if (!raw || !raw.name || raw.size === undefined || raw.size <= 0) {
512
+ const errorMsg = `Invalid file: ${fileName}`;
513
+ console.error('Upload validation failed:', { file: raw, error: errorMsg });
514
+ failureCount++;
515
+ errors.push(`${fileName}: Invalid file (missing name or size)`);
516
+ continue;
517
+ }
518
+
541
519
  const optimisticFile: FileMetadata = {
542
520
  id: optimisticId,
543
521
  filename: raw.name,
@@ -549,7 +527,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
549
527
  variants: [],
550
528
  };
551
529
  useFileStore.getState().addFile(optimisticFile, { prepend: true });
530
+
552
531
  const result = await uploadFileRaw(raw, targetUserId, oxyServices, defaultVisibility);
532
+
553
533
  // Attempt to refresh file list incrementally – fetch single file metadata if API allows
554
534
  if (result?.file || result?.files?.[0]) {
555
535
  const f = result.file || result.files[0];
@@ -566,22 +546,43 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
566
546
  // Remove optimistic then add real
567
547
  useFileStore.getState().removeFile(optimisticId);
568
548
  useFileStore.getState().addFile(merged, { prepend: true });
549
+ uploadedFiles.push(merged);
550
+ successCount++;
569
551
  } else {
570
552
  // Fallback: will reconcile on later list refresh
571
553
  useFileStore.getState().updateFile(optimisticId, { metadata: { uploading: false } as any });
554
+ console.warn('Upload completed but no file data returned:', { fileName, result });
555
+ // Still count as success if upload didn't throw
556
+ successCount++;
572
557
  }
573
- successCount++;
574
558
  } catch (error: any) {
575
559
  failureCount++;
576
- errors.push(`${selectedFiles[i].name}: ${error.message || 'Upload failed'}`);
560
+ const errorMessage = error.message || error.toString() || 'Upload failed';
561
+ const fullError = `${fileName}: ${errorMessage}`;
562
+ errors.push(fullError);
563
+ console.error('File upload failed:', {
564
+ fileName,
565
+ fileSize: raw.size,
566
+ fileType: raw.type,
567
+ error: errorMessage,
568
+ stack: error.stack
569
+ });
570
+
571
+ // Remove optimistic file on error (use the same optimisticId from above)
572
+ useFileStore.getState().removeFile(optimisticId);
577
573
  }
578
574
  }
575
+
576
+ // Show success/error messages
579
577
  if (successCount > 0) {
580
578
  toast.success(`${successCount} file(s) uploaded successfully`);
581
579
  }
582
580
  if (failureCount > 0) {
583
- const errorMessage = `${failureCount} file(s) failed to upload${errors.length > 0 ? ':\n' + errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : '') : ''}`;
584
- toast.error(errorMessage);
581
+ // Show detailed error message with first few errors
582
+ const errorDetails = errors.length > 0
583
+ ? `\n${errors.slice(0, 3).join('\n')}${errors.length > 3 ? `\n...and ${errors.length - 3} more` : ''}`
584
+ : '';
585
+ toast.error(`${failureCount} file(s) failed to upload${errorDetails}`);
585
586
  }
586
587
  // Silent background refresh to ensure metadata/variants updated
587
588
  setTimeout(() => { loadFiles('silent'); }, 1200);
@@ -590,6 +591,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
590
591
  } finally {
591
592
  storeSetUploadProgress(null);
592
593
  }
594
+ return uploadedFiles;
593
595
  };
594
596
 
595
597
  const handleFileSelection = useCallback(async (selectedFiles: File[] | any[]) => {
@@ -597,16 +599,61 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
597
599
  const processedFiles: Array<{ file: File | Blob; preview?: string; size: number; name: string; type: string }> = [];
598
600
 
599
601
  for (const file of selectedFiles) {
602
+ // Validate file has required properties
603
+ if (!file) {
604
+ console.error('Invalid file: file is null or undefined');
605
+ toast.error('Invalid file: file is missing');
606
+ continue;
607
+ }
608
+
609
+ if (!file.name || typeof file.name !== 'string') {
610
+ console.error('Invalid file: missing or invalid name property', file);
611
+ toast.error('Invalid file: missing file name');
612
+ continue;
613
+ }
614
+
615
+ if (file.size === undefined || file.size === null || isNaN(file.size)) {
616
+ console.error('Invalid file: missing or invalid size property', file);
617
+ toast.error(`Invalid file "${file.name || 'unknown'}": missing file size`);
618
+ continue;
619
+ }
620
+
621
+ if (file.size <= 0) {
622
+ console.error('Invalid file: file size is zero or negative', file);
623
+ toast.error(`File "${file.name}" is empty`);
624
+ continue;
625
+ }
626
+
600
627
  // Validate file size
601
628
  if (file.size > MAX_FILE_SIZE) {
602
629
  toast.error(`"${file.name}" is too large. Maximum file size is ${formatFileSize(MAX_FILE_SIZE)}`);
603
630
  continue;
604
631
  }
605
632
 
606
- // Generate preview for images
633
+ // Ensure file has a type property
634
+ const fileType = file.type || 'application/octet-stream';
635
+
636
+ // Generate preview for images - unified approach
607
637
  let preview: string | undefined;
608
- if (file.type.startsWith('image/')) {
609
- preview = URL.createObjectURL(file);
638
+ if (fileType.startsWith('image/')) {
639
+ // Try to use file URI from expo-document-picker if available (works on all platforms)
640
+ const fileUri = (file as any).uri;
641
+ if (fileUri && typeof fileUri === 'string' &&
642
+ (fileUri.startsWith('file://') || fileUri.startsWith('content://') ||
643
+ fileUri.startsWith('http://') || fileUri.startsWith('https://') ||
644
+ fileUri.startsWith('blob:'))) {
645
+ preview = fileUri;
646
+ } else {
647
+ // Fallback: create blob URL if possible (works on web)
648
+ try {
649
+ if (file instanceof File || file instanceof Blob) {
650
+ preview = URL.createObjectURL(file);
651
+ }
652
+ } catch (error: any) {
653
+ console.warn('Failed to create preview URL:', error);
654
+ // Preview is optional, continue without it
655
+ }
656
+ }
610
657
  }
611
658
 
612
659
  processedFiles.push({
@@ -614,11 +661,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
614
661
  preview,
615
662
  size: file.size,
616
663
  name: file.name,
617
- type: file.type
664
+ type: fileType
618
665
  });
619
666
  }
620
667
 
621
- if (processedFiles.length === 0) return;
668
+ if (processedFiles.length === 0) {
669
+ toast.error('No valid files to upload');
670
+ return;
671
+ }
622
672
 
623
673
  // Show preview modal for user to review files before upload
624
674
  setPendingFiles(processedFiles);
@@ -636,8 +686,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
636
686
  try {
637
687
  const filesToUpload = pendingFiles.map(pf => pf.file as File);
638
688
  storeSetUploadProgress({ current: 0, total: filesToUpload.length });
639
- await processFileUploads(filesToUpload);
640
-
689
+ const uploadedFiles = await processFileUploads(filesToUpload);
690
+
641
691
  // Cleanup preview URLs
642
692
  pendingFiles.forEach(pf => {
643
693
  if (pf.preview) {
@@ -645,6 +695,31 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
645
695
  }
646
696
  });
647
697
  setPendingFiles([]);
698
+
699
+ // If in selectMode, automatically select the uploaded file(s)
700
+ if (selectMode && uploadedFiles.length > 0) {
701
+ // Wait a bit for the file store to update and ensure file is available
702
+ setTimeout(() => {
703
+ const fileToSelect = uploadedFiles[0];
704
+ if (!multiSelect && fileToSelect) {
705
+ // Single select mode - directly call onSelect callback
706
+ onSelect?.(fileToSelect);
707
+ if (afterSelect === 'back') {
708
+ goBack?.();
709
+ } else if (afterSelect === 'close') {
710
+ onClose?.();
711
+ }
712
+ } else if (multiSelect) {
713
+ // Multi-select mode - add all uploaded files to selection
714
+ uploadedFiles.forEach(file => {
715
+ if (!selectedIds.has(file.id)) {
716
+ setSelectedIds(prev => new Set(prev).add(file.id));
717
+ }
718
+ });
719
+ }
720
+ }, 500);
721
+ }
722
+
648
723
  endUpload();
649
724
  } catch (error: any) {
650
725
  toast.error(error.message || 'Failed to upload files');
@@ -675,88 +750,124 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
675
750
  }
676
751
  };
677
752
 
753
+ /**
754
+ * Handle file upload - opens document picker and processes selected files
755
+ * Expo 54 compatible - works on web, iOS, and Android
756
+ */
678
757
  const handleFileUpload = async () => {
758
+ // Prevent concurrent document picker calls
759
+ if (isPickingDocument) {
760
+ toast.error('Please wait for the current file selection to complete');
761
+ return;
762
+ }
763
+
679
764
  try {
680
- if (Platform.OS === 'web') {
681
- // Enhanced web file picker
682
- const input = document.createElement('input');
683
- input.type = 'file';
684
- input.multiple = true;
685
- input.accept = '*/*';
686
-
687
- const cancellationTimer = setTimeout(() => {
688
- const state = useFileStore.getState();
689
- if (state.uploading && uploadStartRef.current && !state.uploadProgress) {
690
- endUpload();
691
- }
692
- }, 1500);
693
-
694
- input.onchange = async (e: any) => {
695
- clearTimeout(cancellationTimer);
696
- const selectedFiles = Array.from(e.target.files || []) as File[];
697
- if (selectedFiles.length === 0) {
698
- endUpload();
699
- return;
700
- }
701
- await handleFileSelection(selectedFiles);
702
- };
765
+ setIsPickingDocument(true);
766
+
767
+ // Dynamically import expo-document-picker (Expo 54 supports it on all platforms)
768
+ const DocumentPicker = await import('expo-document-picker').catch(() => null);
703
769
 
704
- input.click();
705
- } else {
706
- // Mobile file picker with expo-document-picker
707
- try {
708
- // Dynamically import to avoid breaking if not installed
709
- const DocumentPicker = await import('expo-document-picker').catch(() => null);
710
-
711
- if (!DocumentPicker) {
712
- toast.error('File picker not available. Please install expo-document-picker');
713
- return;
714
- }
770
+ if (!DocumentPicker || !DocumentPicker.getDocumentAsync) {
771
+ toast.error('File picker not available. Please install expo-document-picker');
772
+ return;
773
+ }
715
774
 
716
- const result = await DocumentPicker.getDocumentAsync({
717
- type: '*/*',
718
- multiple: true,
719
- copyToCacheDirectory: true,
720
- });
775
+ // Use getDocumentAsync directly - it will handle platform availability
776
+ const result = await DocumentPicker.getDocumentAsync({
777
+ type: '*/*',
778
+ multiple: true,
779
+ copyToCacheDirectory: true,
780
+ });
721
781
 
722
- if (result.canceled) {
723
- return;
724
- }
782
+ if (result.canceled) {
783
+ setIsPickingDocument(false);
784
+ return;
785
+ }
786
+
787
+ if (!result.assets || result.assets.length === 0) {
788
+ setIsPickingDocument(false);
789
+ toast.error('No files were selected');
790
+ return;
791
+ }
792
+
793
+ // Convert expo document picker results to File-like objects
794
+ // According to Expo 54 docs, expo-document-picker returns assets with:
795
+ // - uri: file URI (file://, content://, or blob URL)
796
+ // - name: file name
797
+ // - size: file size in bytes
798
+ // - mimeType: MIME type of the file
799
+ // - file: (optional) native File object (usually only on web)
800
+ const files: File[] = [];
801
+ const errors: string[] = [];
725
802
 
726
- // Convert expo document picker results to File-like objects
727
- const files: File[] = [];
728
- for (const doc of result.assets) {
729
- if (doc.file) {
730
- // expo-document-picker provides a File-like object
731
- files.push(doc.file as File);
732
- } else if (doc.uri) {
733
- // Fallback: fetch and create Blob
734
- const response = await fetch(doc.uri);
735
- const blob = await response.blob();
736
- const file = new File([blob], doc.name || 'file', { type: doc.mimeType || 'application/octet-stream' });
737
- files.push(file);
803
+ // Process files in parallel for better performance
804
+ // This allows multiple files to be converted simultaneously
805
+ const conversionPromises = result.assets.map((doc, index) =>
806
+ convertDocumentPickerAssetToFile(doc, index)
807
+ .then((file) => {
808
+ if (file) {
809
+ // Validate file has required properties before adding
810
+ if (!file.name || file.size === undefined) {
811
+ errors.push(`File "${doc.name || 'file'}" is invalid: missing required properties`);
812
+ return null;
813
+ }
814
+ return file;
738
815
  }
739
- }
816
+ return null;
817
+ })
818
+ .catch((error: any) => {
819
+ errors.push(`File "${doc.name || 'file'}": ${error.message || 'Failed to process'}`);
820
+ return null;
821
+ })
822
+ );
740
823
 
741
- if (files.length > 0) {
742
- await handleFileSelection(files);
743
- }
744
- } catch (error: any) {
745
- if (error.message?.includes('expo-document-picker')) {
746
- toast.error('File picker not available. Please install expo-document-picker');
747
- } else {
748
- toast.error(error.message || 'Failed to select files');
749
- }
824
+ const convertedFiles = await Promise.all(conversionPromises);
825
+
826
+ // Filter out null values
827
+ for (const file of convertedFiles) {
828
+ if (file) {
829
+ files.push(file);
750
830
  }
751
831
  }
832
+
833
+ // Show errors if any
834
+ if (errors.length > 0) {
835
+ const errorMessage = errors.slice(0, 3).join('\n') + (errors.length > 3 ? `\n...and ${errors.length - 3} more` : '');
836
+ toast.error(`Failed to load some files:\n${errorMessage}`);
837
+ }
838
+
839
+ // Process successfully converted files
840
+ if (files.length > 0) {
841
+ await handleFileSelection(files);
842
+ } else {
843
+ // Files were selected but none could be converted
844
+ toast.error('No files could be processed. Please try selecting files again.');
845
+ }
752
846
  } catch (error: any) {
753
- toast.error(error.message || 'Failed to open file picker');
847
+ console.error('File upload error:', error);
848
+ if (error.message?.includes('expo-document-picker') || error.message?.includes('Different document picking in progress')) {
849
+ if (error.message?.includes('Different document picking in progress')) {
850
+ toast.error('Please wait for the current file selection to complete');
851
+ } else {
852
+ toast.error('File picker not available. Please install expo-document-picker');
853
+ }
854
+ } else {
855
+ toast.error(error.message || 'Failed to select files');
856
+ }
857
+ } finally {
858
+ // Always reset the picking state, even if there was an error
859
+ setIsPickingDocument(false);
754
860
  }
755
861
  };
756
862
 
757
863
  const handleFileDelete = async (fileId: string, filename: string) => {
758
- // Use web-compatible confirmation dialog
759
- const confirmed = window.confirm(`Are you sure you want to delete "${filename}"? This action cannot be undone.`);
864
+ // Use platform-aware confirmation dialog
865
+ const confirmed = await confirmAction(
866
+ `Are you sure you want to delete "${filename}"? This action cannot be undone.`,
867
+ 'Delete File',
868
+ 'Delete',
869
+ 'Cancel'
870
+ );
760
871
 
761
872
  if (!confirmed) {
762
873
  return;
@@ -792,17 +903,20 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
792
903
 
793
904
  const handleBulkDelete = useCallback(async () => {
794
905
  if (selectedIds.size === 0) return;
795
-
906
+
796
907
  const fileMap: Record<string, FileMetadata> = {};
797
908
  files.forEach(f => { fileMap[f.id] = f; });
798
909
  const selectedFiles = Array.from(selectedIds).map(id => fileMap[id]).filter(Boolean);
799
-
800
- const confirmed = window.confirm(
801
- `Are you sure you want to delete ${selectedFiles.length} file(s)? This action cannot be undone.`
910
+
911
+ const confirmed = await confirmAction(
912
+ `Are you sure you want to delete ${selectedFiles.length} file(s)? This action cannot be undone.`,
913
+ 'Delete Files',
914
+ 'Delete',
915
+ 'Cancel'
802
916
  );
803
-
917
+
804
918
  if (!confirmed) return;
805
-
919
+
806
920
  try {
807
921
  const deletePromises = Array.from(selectedIds).map(async (fileId) => {
808
922
  try {
@@ -813,18 +927,18 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
813
927
  return { success: false, fileId, error };
814
928
  }
815
929
  });
816
-
930
+
817
931
  const results = await Promise.allSettled(deletePromises);
818
932
  const successful = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
819
933
  const failed = results.length - successful;
820
-
934
+
821
935
  if (successful > 0) {
822
936
  toast.success(`${successful} file(s) deleted successfully`);
823
937
  }
824
938
  if (failed > 0) {
825
939
  toast.error(`${failed} file(s) failed to delete`);
826
940
  }
827
-
941
+
828
942
  setSelectedIds(new Set());
829
943
  setTimeout(() => loadFiles('silent'), 800);
830
944
  } catch (error: any) {
@@ -834,7 +948,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
834
948
 
835
949
  const handleBulkVisibilityChange = useCallback(async (visibility: 'private' | 'public' | 'unlisted') => {
836
950
  if (selectedIds.size === 0) return;
837
-
951
+
838
952
  try {
839
953
  const updatePromises = Array.from(selectedIds).map(async (fileId) => {
840
954
  try {
@@ -844,11 +958,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
844
958
  return { success: false, fileId, error };
845
959
  }
846
960
  });
847
-
961
+
848
962
  const results = await Promise.allSettled(updatePromises);
849
963
  const successful = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
850
964
  const failed = results.length - successful;
851
-
965
+
852
966
  if (successful > 0) {
853
967
  toast.success(`${successful} file(s) visibility updated to ${visibility}`);
854
968
  // Update file metadata in store
@@ -861,87 +975,24 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
861
975
  if (failed > 0) {
862
976
  toast.error(`${failed} file(s) failed to update visibility`);
863
977
  }
864
-
978
+
865
979
  setTimeout(() => loadFiles('silent'), 800);
866
980
  } catch (error: any) {
867
981
  toast.error(error.message || 'Failed to update visibility');
868
982
  }
869
983
  }, [selectedIds, oxyServices, files, loadFiles]);
870
984
 
871
- // Global drag listeners (web) - attach to document for reliable drag and drop
872
- useEffect(() => {
873
- if (Platform.OS !== 'web' || user?.id !== targetUserId) return;
874
-
875
- let dragCounter = 0; // Track drag enter/leave to handle nested elements
876
-
877
- const onDragEnter = (e: DragEvent) => {
878
- dragCounter++;
879
- if (e?.dataTransfer?.types?.includes('Files')) {
880
- e.preventDefault();
881
- e.stopPropagation();
882
- setIsDragging(true);
883
- }
884
- };
885
-
886
- const onDragOver = (e: DragEvent) => {
887
- if (e?.dataTransfer?.types?.includes('Files')) {
888
- e.preventDefault();
889
- e.stopPropagation();
890
- // Keep dragging state true while over document
891
- setIsDragging(true);
892
- }
893
- };
894
-
895
- const onDrop = async (e: DragEvent) => {
896
- dragCounter = 0;
897
- setIsDragging(false);
898
-
899
- if (e?.dataTransfer?.files?.length) {
900
- e.preventDefault();
901
- e.stopPropagation();
902
-
903
- try {
904
- const files = Array.from(e.dataTransfer.files) as File[];
905
- if (files.length > 0) {
906
- await handleFileSelection(files);
907
- }
908
- } catch (error: any) {
909
- toast.error(error.message || 'Failed to upload files');
910
- }
911
- }
912
- };
913
-
914
- const onDragLeave = (e: DragEvent) => {
915
- dragCounter--;
916
- // Only hide drag overlay if we're actually leaving the document (drag counter reaches 0)
917
- if (dragCounter === 0) {
918
- setIsDragging(false);
919
- }
920
- };
921
-
922
- // Attach to document for global drag detection
923
- document.addEventListener('dragenter', onDragEnter, false);
924
- document.addEventListener('dragover', onDragOver, false);
925
- document.addEventListener('drop', onDrop, false);
926
- document.addEventListener('dragleave', onDragLeave, false);
927
-
928
- return () => {
929
- document.removeEventListener('dragenter', onDragEnter, false);
930
- document.removeEventListener('dragover', onDragOver, false);
931
- document.removeEventListener('drop', onDrop, false);
932
- document.removeEventListener('dragleave', onDragLeave, false);
933
- };
934
- }, [user?.id, targetUserId, handleFileSelection]);
935
-
936
-
985
+ // Unified download function - works on all platforms
937
986
  const handleFileDownload = async (fileId: string, filename: string) => {
938
987
  try {
939
- if (Platform.OS === 'web') {
940
- // Use the public download URL method
941
- const downloadUrl = oxyServices.getFileDownloadUrl(fileId);
988
+ // Try to use the download URL with a simple approach
989
+ // On web, this creates a download link. On mobile, it opens the URL.
990
+ const downloadUrl = oxyServices.getFileDownloadUrl(fileId);
942
991
 
992
+ // For web platforms, use link download
993
+ if (typeof window !== 'undefined' && window.document) {
943
994
  try {
944
- // Method 1: Try simple link download first
995
+ // Try simple link download first
945
996
  const link = document.createElement('a');
946
997
  link.href = downloadUrl;
947
998
  link.download = filename;
@@ -949,13 +1000,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
949
1000
  document.body.appendChild(link);
950
1001
  link.click();
951
1002
  document.body.removeChild(link);
952
-
953
1003
  toast.success('File download started');
954
1004
  } catch (linkError) {
955
-
956
- // Method 2: Fallback to authenticated download
1005
+ // Fallback to authenticated download
957
1006
  const blob = await oxyServices.getFileContentAsBlob(fileId);
958
- const url = window.URL.createObjectURL(blob);
1007
+ const url = URL.createObjectURL(blob);
959
1008
 
960
1009
  const link = document.createElement('a');
961
1010
  link.href = url;
@@ -965,36 +1014,20 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
965
1014
  document.body.removeChild(link);
966
1015
 
967
1016
  // Clean up the blob URL
968
- window.URL.revokeObjectURL(url);
969
-
1017
+ URL.revokeObjectURL(url);
970
1018
  toast.success('File downloaded successfully');
971
1019
  }
972
1020
  } else {
973
- toast.info('File download not implemented for mobile yet');
1021
+ // For mobile, open the URL (user can save from browser)
1022
+ // Note: This is a simplified approach - for full mobile support,
1023
+ // consider using expo-file-system or react-native-fs
1024
+ toast.info('Please use your browser to download the file');
974
1025
  }
975
1026
  } catch (error: any) {
976
1027
  toast.error(error.message || 'Failed to download file');
977
1028
  }
978
1029
  };
979
1030
 
980
- const formatFileSize = (bytes: number): string => {
981
- if (bytes === 0) return '0 Bytes';
982
- const k = 1024;
983
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
984
- const i = Math.floor(Math.log(bytes) / Math.log(k));
985
- return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
986
- };
987
-
988
- const getFileIcon = (contentType: string): string => {
989
- if (contentType.startsWith('image/')) return 'image';
990
- if (contentType.startsWith('video/')) return 'videocam';
991
- if (contentType.startsWith('audio/')) return 'musical-notes';
992
- if (contentType.includes('pdf')) return 'document-text';
993
- if (contentType.includes('word') || contentType.includes('doc')) return 'document';
994
- if (contentType.includes('excel') || contentType.includes('sheet')) return 'grid';
995
- if (contentType.includes('zip') || contentType.includes('archive')) return 'archive';
996
- return 'document-outline';
997
- };
998
1031
 
999
1032
  const handleFileOpen = async (file: FileMetadata) => {
1000
1033
  if (selectMode) {
@@ -1061,24 +1094,24 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1061
1094
  };
1062
1095
 
1063
1096
  const renderSimplePhotoItem = useCallback((photo: FileMetadata, index: number) => {
1064
- const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
1097
+ const downloadUrl = getSafeDownloadUrlCallback(photo, 'thumb');
1065
1098
 
1066
1099
  // Calculate photo item width based on actual container size from bottom sheet
1067
1100
  let itemsPerRow = 3; // Default for mobile
1068
- if (containerWidth > 768) itemsPerRow = 4; // Desktop/tablet
1069
- else if (containerWidth > 480) itemsPerRow = 3; // Large mobile
1101
+ if (safeContainerWidth > 768) itemsPerRow = 4; // Desktop/tablet
1102
+ else if (safeContainerWidth > 480) itemsPerRow = 3; // Large mobile
1070
1103
 
1071
1104
  // Account for the photoScrollContainer padding (16px on each side = 32px total)
1072
1105
  const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
1073
1106
  const gaps = (itemsPerRow - 1) * 4; // Gap between items (4px)
1074
- const availableWidth = containerWidth - scrollContainerPadding;
1107
+ const availableWidth = safeContainerWidth - scrollContainerPadding;
1075
1108
  const itemWidth = (availableWidth - gaps) / itemsPerRow;
1076
1109
 
1077
1110
  return (
1078
1111
  <TouchableOpacity
1079
1112
  key={photo.id}
1080
1113
  style={[
1081
- styles.simplePhotoItem,
1114
+ fileManagementStyles.simplePhotoItem,
1082
1115
  {
1083
1116
  width: itemWidth,
1084
1117
  height: itemWidth,
@@ -1089,10 +1122,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1089
1122
  onPress={() => handleFileOpen(photo)}
1090
1123
  activeOpacity={0.8}
1091
1124
  >
1092
- <View style={styles.simplePhotoContainer}>
1125
+ <View style={fileManagementStyles.simplePhotoContainer}>
1093
1126
  <ExpoImage
1094
1127
  source={{ uri: downloadUrl }}
1095
- style={styles.simplePhotoImage}
1128
+ style={fileManagementStyles.simplePhotoImage}
1096
1129
  contentFit="cover"
1097
1130
  transition={120}
1098
1131
  cachePolicy="memory-disk"
@@ -1102,23 +1135,23 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1102
1135
  accessibilityLabel={photo.filename}
1103
1136
  />
1104
1137
  {selectMode && (
1105
- <View style={styles.selectionBadge}>
1138
+ <View style={fileManagementStyles.selectionBadge}>
1106
1139
  <Ionicons name={selectedIds.has(photo.id) ? 'checkmark-circle' : 'ellipse-outline'} size={20} color={selectedIds.has(photo.id) ? themeStyles.primaryColor : themeStyles.textColor} />
1107
1140
  </View>
1108
1141
  )}
1109
1142
  </View>
1110
1143
  </TouchableOpacity>
1111
1144
  );
1112
- }, [oxyServices, containerWidth, selectMode, selectedIds, themeStyles.primaryColor, themeStyles.textColor]);
1145
+ }, [oxyServices, safeContainerWidth, selectMode, selectedIds, themeStyles.primaryColor, themeStyles.textColor]);
1113
1146
 
1114
1147
  const renderJustifiedPhotoItem = useCallback((photo: FileMetadata, width: number, height: number, isLast: boolean) => {
1115
- const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
1148
+ const downloadUrl = getSafeDownloadUrlCallback(photo, 'thumb');
1116
1149
 
1117
1150
  return (
1118
1151
  <TouchableOpacity
1119
1152
  key={photo.id}
1120
1153
  style={[
1121
- styles.justifiedPhotoItem,
1154
+ fileManagementStyles.justifiedPhotoItem,
1122
1155
  {
1123
1156
  width,
1124
1157
  height,
@@ -1129,10 +1162,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1129
1162
  onPress={() => handleFileOpen(photo)}
1130
1163
  activeOpacity={0.8}
1131
1164
  >
1132
- <View style={styles.justifiedPhotoContainer}>
1165
+ <View style={fileManagementStyles.justifiedPhotoContainer}>
1133
1166
  <ExpoImage
1134
1167
  source={{ uri: downloadUrl }}
1135
- style={styles.justifiedPhotoImage}
1168
+ style={fileManagementStyles.justifiedPhotoImage}
1136
1169
  contentFit="cover"
1137
1170
  transition={120}
1138
1171
  cachePolicy="memory-disk"
@@ -1142,7 +1175,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1142
1175
  accessibilityLabel={photo.filename}
1143
1176
  />
1144
1177
  {selectMode && (
1145
- <View style={styles.selectionBadge}>
1178
+ <View style={fileManagementStyles.selectionBadge}>
1146
1179
  <Ionicons name={selectedIds.has(photo.id) ? 'checkmark-circle' : 'ellipse-outline'} size={20} color={selectedIds.has(photo.id) ? themeStyles.primaryColor : themeStyles.textColor} />
1147
1180
  </View>
1148
1181
  )}
@@ -1173,26 +1206,20 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1173
1206
  return (
1174
1207
  <View
1175
1208
  key={file.id}
1176
- style={[styles.fileItem, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }, selectMode && selectedIds.has(file.id) && { borderColor: themeStyles.primaryColor, borderWidth: 2 }]}
1209
+ style={[fileManagementStyles.fileItem, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }, selectMode && selectedIds.has(file.id) && { borderColor: themeStyles.primaryColor, borderWidth: 2 }]}
1177
1210
  >
1178
1211
  <TouchableOpacity
1179
- style={styles.fileContent}
1212
+ style={fileManagementStyles.fileContent}
1180
1213
  onPress={() => handleFileOpen(file)}
1181
1214
  >
1182
1215
  {/* Preview Thumbnail */}
1183
- <View style={styles.filePreviewContainer}>
1216
+ <View style={fileManagementStyles.filePreviewContainer}>
1184
1217
  {hasPreview ? (
1185
- <View
1186
- style={styles.filePreview}
1187
- {...(Platform.OS === 'web' && {
1188
- onMouseEnter: () => setHoveredPreview(file.id),
1189
- onMouseLeave: () => setHoveredPreview(null),
1190
- })}
1191
- >
1218
+ <View style={fileManagementStyles.filePreview}>
1192
1219
  {isImage && (
1193
1220
  <ExpoImage
1194
- source={{ uri: getSafeDownloadUrl(file, 'thumb') }}
1195
- style={styles.previewImage}
1221
+ source={{ uri: getSafeDownloadUrlCallback(file, 'thumb') }}
1222
+ style={fileManagementStyles.previewImage}
1196
1223
  contentFit="cover"
1197
1224
  transition={120}
1198
1225
  cachePolicy="memory-disk"
@@ -1203,16 +1230,16 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1203
1230
  />
1204
1231
  )}
1205
1232
  {isPDF && (
1206
- <View style={styles.pdfPreview}>
1233
+ <View style={fileManagementStyles.pdfPreview}>
1207
1234
  <Ionicons name="document" size={32} color={themeStyles.primaryColor} />
1208
- <Text style={[styles.pdfLabel, { color: themeStyles.primaryColor }]}>PDF</Text>
1235
+ <Text style={[fileManagementStyles.pdfLabel, { color: themeStyles.primaryColor }]}>PDF</Text>
1209
1236
  </View>
1210
1237
  )}
1211
1238
  {isVideo && (
1212
- <View style={styles.videoPreviewWrapper}>
1239
+ <View style={fileManagementStyles.videoPreviewWrapper}>
1213
1240
  <ExpoImage
1214
- source={{ uri: getSafeDownloadUrl(file, 'thumb') }}
1215
- style={styles.videoPosterImage}
1241
+ source={{ uri: getSafeDownloadUrlCallback(file, 'thumb') }}
1242
+ style={fileManagementStyles.videoPosterImage}
1216
1243
  contentFit="cover"
1217
1244
  transition={120}
1218
1245
  cachePolicy="memory-disk"
@@ -1221,15 +1248,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1221
1248
  }}
1222
1249
  accessibilityLabel={file.filename + ' video thumbnail'}
1223
1250
  />
1224
- <View style={styles.videoOverlay}>
1251
+ <View style={fileManagementStyles.videoOverlay}>
1225
1252
  <Ionicons name="play" size={24} color="#FFFFFF" />
1226
1253
  </View>
1227
1254
  </View>
1228
1255
  )}
1229
1256
  {/* Fallback icon (hidden by default for images) */}
1230
1257
  <View
1231
- style={[styles.fallbackIcon, { display: isImage ? 'none' : 'flex' }]}
1232
- {...(Platform.OS === 'web' && { 'data-fallback': 'true' })}
1258
+ style={[fileManagementStyles.fallbackIcon, { display: isImage ? 'none' : 'flex' }]}
1233
1259
  >
1234
1260
  <Ionicons
1235
1261
  name={getFileIcon(file.contentType) as any}
@@ -1238,20 +1264,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1238
1264
  />
1239
1265
  </View>
1240
1266
 
1241
- {/* Preview overlay for hover effect */}
1242
- {!selectMode && Platform.OS === 'web' && hoveredPreview === file.id && isImage && (
1243
- <View style={styles.previewOverlay}>
1244
- <Ionicons name="eye" size={24} color="#FFFFFF" />
1245
- </View>
1246
- )}
1247
1267
  {selectMode && (
1248
- <View style={styles.selectionBadge}>
1268
+ <View style={fileManagementStyles.selectionBadge}>
1249
1269
  <Ionicons name={selectedIds.has(file.id) ? 'checkmark-circle' : 'ellipse-outline'} size={22} color={selectedIds.has(file.id) ? themeStyles.primaryColor : themeStyles.textColor} />
1250
1270
  </View>
1251
1271
  )}
1252
1272
  </View>
1253
1273
  ) : (
1254
- <View style={styles.fileIconContainer}>
1274
+ <View style={fileManagementStyles.fileIconContainer}>
1255
1275
  <Ionicons
1256
1276
  name={getFileIcon(file.contentType) as any}
1257
1277
  size={32}
@@ -1261,16 +1281,16 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1261
1281
  )}
1262
1282
  </View>
1263
1283
 
1264
- <View style={styles.fileInfo}>
1265
- <Text style={[styles.fileName, { color: themeStyles.textColor }]} numberOfLines={1}>
1284
+ <View style={fileManagementStyles.fileInfo}>
1285
+ <Text style={[fileManagementStyles.fileName, { color: themeStyles.textColor }]} numberOfLines={1}>
1266
1286
  {file.filename}
1267
1287
  </Text>
1268
- <Text style={[styles.fileDetails, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1288
+ <Text style={[fileManagementStyles.fileDetails, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1269
1289
  {formatFileSize(file.length)} • {new Date(file.uploadDate).toLocaleDateString()}
1270
1290
  </Text>
1271
1291
  {file.metadata?.description && (
1272
1292
  <Text
1273
- style={[styles.fileDescription, { color: themeStyles.isDarkTheme ? '#AAAAAA' : '#888888' }]}
1293
+ style={[fileManagementStyles.fileDescription, { color: themeStyles.isDarkTheme ? '#AAAAAA' : '#888888' }]}
1274
1294
  numberOfLines={2}
1275
1295
  >
1276
1296
  {file.metadata.description}
@@ -1280,11 +1300,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1280
1300
  </TouchableOpacity>
1281
1301
 
1282
1302
  {!selectMode && (
1283
- <View style={styles.fileActions}>
1303
+ <View style={fileManagementStyles.fileActions}>
1284
1304
  {/* Preview button for supported files */}
1285
1305
  {hasPreview && (
1286
1306
  <TouchableOpacity
1287
- style={[styles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1307
+ style={[fileManagementStyles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1288
1308
  onPress={() => handleFileOpen(file)}
1289
1309
  >
1290
1310
  <Ionicons name="eye" size={20} color={themeStyles.primaryColor} />
@@ -1292,7 +1312,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1292
1312
  )}
1293
1313
 
1294
1314
  <TouchableOpacity
1295
- style={[styles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1315
+ style={[fileManagementStyles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1296
1316
  onPress={() => handleFileDownload(file.id, file.filename)}
1297
1317
  >
1298
1318
  <Ionicons name="download" size={20} color={themeStyles.primaryColor} />
@@ -1300,7 +1320,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1300
1320
 
1301
1321
  {/* Always show delete button for debugging */}
1302
1322
  <TouchableOpacity
1303
- style={[styles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#400000' : '#FFEBEE' }]}
1323
+ style={[fileManagementStyles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#400000' : '#FFEBEE' }]}
1304
1324
  onPress={() => {
1305
1325
  handleFileDelete(file.id, file.filename);
1306
1326
  }}
@@ -1322,86 +1342,133 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1322
1342
  const groupedFileItems = useMemo(() => {
1323
1343
  // filteredFiles is already sorted, so just use it directly
1324
1344
  const sortedFiles = filteredFiles;
1325
-
1345
+
1326
1346
  // Store file positions for scrolling
1327
1347
  sortedFiles.forEach((file, index) => {
1328
1348
  itemRefs.current.set(file.id, index);
1329
1349
  });
1330
-
1350
+
1331
1351
  return sortedFiles.map((file) => {
1332
- const isImage = file.contentType.startsWith('image/');
1333
- const isVideo = file.contentType.startsWith('video/');
1334
- const hasPreview = isImage || isVideo;
1335
- const previewUrl = hasPreview ? (isVideo ? getSafeDownloadUrl(file, 'poster') : getSafeDownloadUrl(file, 'thumb')) : undefined;
1336
- const isSelected = selectedIds.has(file.id);
1337
- return {
1338
- id: file.id,
1339
- image: previewUrl,
1340
- imageSize: 44,
1341
- icon: !previewUrl ? getFileIcon(file.contentType) : undefined,
1342
- iconColor: themeStyles.primaryColor,
1343
- title: file.filename,
1344
- subtitle: `${formatFileSize(file.length)} • ${new Date(file.uploadDate).toLocaleDateString()}`,
1345
- theme: theme as 'light' | 'dark',
1346
- onPress: () => {
1347
- // Support selection in regular mode with long press or if already selecting
1348
- if (!selectMode && selectedIds.size > 0) {
1349
- // If already in selection mode (some files selected), toggle selection
1350
- toggleSelect(file);
1351
- } else {
1352
- handleFileOpen(file);
1353
- }
1354
- },
1355
- onLongPress: !selectMode ? () => {
1356
- // Enable selection mode on long press
1357
- if (selectedIds.size === 0) {
1358
- setSelectedIds(new Set([file.id]));
1359
- } else {
1360
- toggleSelect(file);
1361
- }
1362
- } : undefined,
1363
- showChevron: false,
1364
- dense: true,
1365
- multiRow: !!file.metadata?.description,
1366
- selected: (selectMode || selectedIds.size > 0) && isSelected,
1367
- // Hide action buttons when selecting (in selectMode or bulk operations mode)
1368
- customContent: (!selectMode && selectedIds.size === 0) ? (
1369
- <View style={styles.groupedActions}>
1370
- {(isImage || isVideo || file.contentType.includes('pdf')) && (
1371
- <TouchableOpacity
1372
- style={[styles.groupedActionBtn, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1373
- onPress={() => handleFileOpen(file)}
1374
- >
1375
- <Ionicons name="eye" size={18} color={themeStyles.primaryColor} />
1376
- </TouchableOpacity>
1377
- )}
1378
- <TouchableOpacity
1379
- style={[styles.groupedActionBtn, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1380
- onPress={() => handleFileDownload(file.id, file.filename)}
1381
- >
1382
- <Ionicons name="download" size={18} color={themeStyles.primaryColor} />
1383
- </TouchableOpacity>
1352
+ const isImage = file.contentType.startsWith('image/');
1353
+ const isPDF = file.contentType.includes('pdf');
1354
+ const isVideo = file.contentType.startsWith('video/');
1355
+ const hasPreview = isImage || isPDF || isVideo;
1356
+ const isSelected = selectedIds.has(file.id);
1357
+
1358
+ // Create customIcon for preview thumbnails (36x36 to match GroupedItem iconContainer)
1359
+ let customIcon: React.ReactNode | undefined;
1360
+ if (hasPreview) {
1361
+ if (isImage) {
1362
+ customIcon = (
1363
+ <View style={{ width: 36, height: 36, borderRadius: 18, overflow: 'hidden' }}>
1364
+ <ExpoImage
1365
+ source={{ uri: getSafeDownloadUrlCallback(file, 'thumb') }}
1366
+ style={{ width: 36, height: 36 }}
1367
+ contentFit="cover"
1368
+ transition={120}
1369
+ cachePolicy="memory-disk"
1370
+ onError={() => {
1371
+ // Image preview failed to load - will fallback to icon
1372
+ }}
1373
+ accessibilityLabel={file.filename}
1374
+ />
1375
+ </View>
1376
+ );
1377
+ } else if (isVideo) {
1378
+ customIcon = (
1379
+ <View style={{ width: 36, height: 36, borderRadius: 18, overflow: 'hidden', backgroundColor: '#000000', position: 'relative' }}>
1380
+ <ExpoImage
1381
+ source={{ uri: getSafeDownloadUrlCallback(file, 'thumb') }}
1382
+ style={{ width: 36, height: 36 }}
1383
+ contentFit="cover"
1384
+ transition={120}
1385
+ cachePolicy="memory-disk"
1386
+ onError={(_: any) => {
1387
+ // If thumbnail not available, we still show icon overlay
1388
+ }}
1389
+ accessibilityLabel={file.filename + ' video thumbnail'}
1390
+ />
1391
+ <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.25)' }}>
1392
+ <Ionicons name="play" size={16} color="#FFFFFF" />
1393
+ </View>
1394
+ </View>
1395
+ );
1396
+ } else if (isPDF) {
1397
+ customIcon = (
1398
+ <View style={{ width: 36, height: 36, borderRadius: 18, alignItems: 'center', justifyContent: 'center', backgroundColor: '#FF6B6B20' }}>
1399
+ <Ionicons name="document" size={20} color={themeStyles.primaryColor} />
1400
+ </View>
1401
+ );
1402
+ }
1403
+ }
1404
+
1405
+ return {
1406
+ id: file.id,
1407
+ customIcon: customIcon,
1408
+ icon: !hasPreview ? getFileIcon(file.contentType) : undefined,
1409
+ iconColor: themeStyles.primaryColor,
1410
+ title: file.filename,
1411
+ subtitle: `${formatFileSize(file.length)} • ${new Date(file.uploadDate).toLocaleDateString()}`,
1412
+ theme: theme as 'light' | 'dark',
1413
+ onPress: () => {
1414
+ // Support selection in regular mode with long press or if already selecting
1415
+ if (!selectMode && selectedIds.size > 0) {
1416
+ // If already in selection mode (some files selected), toggle selection
1417
+ toggleSelect(file);
1418
+ } else {
1419
+ handleFileOpen(file);
1420
+ }
1421
+ },
1422
+ onLongPress: !selectMode ? () => {
1423
+ // Enable selection mode on long press
1424
+ if (selectedIds.size === 0) {
1425
+ setSelectedIds(new Set([file.id]));
1426
+ } else {
1427
+ toggleSelect(file);
1428
+ }
1429
+ } : undefined,
1430
+ showChevron: false,
1431
+ dense: true,
1432
+ multiRow: !!file.metadata?.description,
1433
+ selected: (selectMode || selectedIds.size > 0) && isSelected,
1434
+ // Hide action buttons when selecting (in selectMode or bulk operations mode)
1435
+ customContent: (!selectMode && selectedIds.size === 0) ? (
1436
+ <View style={fileManagementStyles.groupedActions}>
1437
+ {(isImage || isVideo || file.contentType.includes('pdf')) && (
1384
1438
  <TouchableOpacity
1385
- style={[styles.groupedActionBtn, { backgroundColor: themeStyles.isDarkTheme ? '#400000' : '#FFEBEE' }]}
1386
- onPress={() => handleFileDelete(file.id, file.filename)}
1387
- disabled={deleting === file.id}
1439
+ style={[fileManagementStyles.groupedActionBtn, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1440
+ onPress={() => handleFileOpen(file)}
1388
1441
  >
1389
- {deleting === file.id ? (
1390
- <ActivityIndicator size="small" color={themeStyles.dangerColor} />
1391
- ) : (
1392
- <Ionicons name="trash" size={18} color={themeStyles.dangerColor} />
1393
- )}
1442
+ <Ionicons name="eye" size={18} color={themeStyles.primaryColor} />
1394
1443
  </TouchableOpacity>
1395
- </View>
1396
- ) : undefined,
1397
- customContentBelow: file.metadata?.description ? (
1398
- <Text style={[styles.groupedDescription, { color: themeStyles.isDarkTheme ? '#AAAAAA' : '#666666' }]} numberOfLines={2}>
1399
- {file.metadata.description}
1400
- </Text>
1401
- ) : undefined,
1402
- } as any;
1403
- });
1404
- }, [filteredFiles, theme, themeStyles, deleting, handleFileDownload, handleFileDelete, handleFileOpen, getSafeDownloadUrl, selectMode, selectedIds]);
1444
+ )}
1445
+ <TouchableOpacity
1446
+ style={[fileManagementStyles.groupedActionBtn, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1447
+ onPress={() => handleFileDownload(file.id, file.filename)}
1448
+ >
1449
+ <Ionicons name="download" size={18} color={themeStyles.primaryColor} />
1450
+ </TouchableOpacity>
1451
+ <TouchableOpacity
1452
+ style={[fileManagementStyles.groupedActionBtn, { backgroundColor: themeStyles.isDarkTheme ? '#400000' : '#FFEBEE' }]}
1453
+ onPress={() => handleFileDelete(file.id, file.filename)}
1454
+ disabled={deleting === file.id}
1455
+ >
1456
+ {deleting === file.id ? (
1457
+ <ActivityIndicator size="small" color={themeStyles.dangerColor} />
1458
+ ) : (
1459
+ <Ionicons name="trash" size={18} color={themeStyles.dangerColor} />
1460
+ )}
1461
+ </TouchableOpacity>
1462
+ </View>
1463
+ ) : undefined,
1464
+ customContentBelow: file.metadata?.description ? (
1465
+ <Text style={[fileManagementStyles.groupedDescription, { color: themeStyles.isDarkTheme ? '#AAAAAA' : '#666666' }]} numberOfLines={2}>
1466
+ {file.metadata.description}
1467
+ </Text>
1468
+ ) : undefined,
1469
+ } as any;
1470
+ });
1471
+ }, [filteredFiles, theme, themeStyles, deleting, handleFileDownload, handleFileDelete, handleFileOpen, getSafeDownloadUrlCallback, selectMode, selectedIds]);
1405
1472
 
1406
1473
  // Scroll to selected file after selection
1407
1474
  useEffect(() => {
@@ -1409,7 +1476,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1409
1476
  if (viewMode === 'all' && scrollViewRef.current) {
1410
1477
  // Find the index of the selected file
1411
1478
  const itemIndex = itemRefs.current.get(lastSelectedFileId);
1412
-
1479
+
1413
1480
  if (itemIndex !== undefined && itemIndex >= 0) {
1414
1481
  // Estimate item height (GroupedItem with dense mode is approximately 60-70px)
1415
1482
  // Account for description rows which add extra height
@@ -1417,7 +1484,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1417
1484
  const descriptionHeight = 30; // Approximate height for description
1418
1485
  // Use filteredFiles which is already sorted according to user's selection
1419
1486
  const sortedFiles = filteredFiles;
1420
-
1487
+
1421
1488
  // Calculate total height up to this item
1422
1489
  let scrollPosition = 0;
1423
1490
  for (let i = 0; i <= itemIndex && i < sortedFiles.length; i++) {
@@ -1427,11 +1494,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1427
1494
  scrollPosition += descriptionHeight;
1428
1495
  }
1429
1496
  }
1430
-
1497
+
1431
1498
  // Add header, controls, search, and stats height (approximately 250px)
1432
1499
  const headerHeight = 250;
1433
1500
  const finalScrollPosition = headerHeight + scrollPosition - 150; // Offset to show item near top
1434
-
1501
+
1435
1502
  // Use requestAnimationFrame to ensure DOM is updated before scrolling
1436
1503
  requestAnimationFrame(() => {
1437
1504
  requestAnimationFrame(() => {
@@ -1446,24 +1513,24 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1446
1513
  // For photo grid, find the photo index
1447
1514
  const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
1448
1515
  const photoIndex = photos.findIndex(p => p.id === lastSelectedFileId);
1449
-
1516
+
1450
1517
  if (photoIndex >= 0) {
1451
1518
  // Estimate photo item height based on grid layout
1452
1519
  // Calculate items per row
1453
1520
  let itemsPerRow = 3;
1454
- if (containerWidth > 768) itemsPerRow = 6;
1455
- else if (containerWidth > 480) itemsPerRow = 4;
1456
-
1521
+ if (safeContainerWidth > 768) itemsPerRow = 6;
1522
+ else if (safeContainerWidth > 480) itemsPerRow = 4;
1523
+
1457
1524
  const scrollContainerPadding = 32;
1458
1525
  const gaps = (itemsPerRow - 1) * 4;
1459
- const availableWidth = containerWidth - scrollContainerPadding;
1526
+ const availableWidth = safeContainerWidth - scrollContainerPadding;
1460
1527
  const itemWidth = (availableWidth - gaps) / itemsPerRow;
1461
-
1528
+
1462
1529
  // Calculate row and approximate scroll position
1463
1530
  const row = Math.floor(photoIndex / itemsPerRow);
1464
1531
  const headerHeight = 250;
1465
1532
  const finalScrollPosition = headerHeight + (row * (itemWidth + 4)) - 150;
1466
-
1533
+
1467
1534
  // Use requestAnimationFrame to ensure DOM is updated before scrolling
1468
1535
  requestAnimationFrame(() => {
1469
1536
  requestAnimationFrame(() => {
@@ -1476,7 +1543,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1476
1543
  }
1477
1544
  }
1478
1545
  }
1479
- }, [lastSelectedFileId, selectMode, viewMode, filteredFiles, containerWidth]);
1546
+ }, [lastSelectedFileId, selectMode, viewMode, filteredFiles, safeContainerWidth]);
1480
1547
 
1481
1548
  // Clear selected file ID after scroll animation completes
1482
1549
  useEffect(() => {
@@ -1484,30 +1551,30 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1484
1551
  const timeoutId = setTimeout(() => {
1485
1552
  setLastSelectedFileId(null);
1486
1553
  }, 600); // Allow time for scroll animation to complete
1487
-
1554
+
1488
1555
  return () => clearTimeout(timeoutId);
1489
1556
  }
1490
1557
  }, [lastSelectedFileId]);
1491
1558
 
1492
1559
  const renderPhotoItem = (photo: FileMetadata, index: number) => {
1493
- const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
1560
+ const downloadUrl = getSafeDownloadUrlCallback(photo, 'thumb');
1494
1561
 
1495
1562
  // Calculate photo item width based on actual container size from bottom sheet
1496
1563
  let itemsPerRow = 3; // Default for mobile
1497
- if (containerWidth > 768) itemsPerRow = 6; // Tablet/Desktop
1498
- else if (containerWidth > 480) itemsPerRow = 4; // Large mobile
1564
+ if (safeContainerWidth > 768) itemsPerRow = 6; // Tablet/Desktop
1565
+ else if (safeContainerWidth > 480) itemsPerRow = 4; // Large mobile
1499
1566
 
1500
1567
  // Account for the photoScrollContainer padding (16px on each side = 32px total)
1501
1568
  const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
1502
1569
  const gaps = (itemsPerRow - 1) * 4; // Gap between items
1503
- const availableWidth = containerWidth - scrollContainerPadding;
1570
+ const availableWidth = safeContainerWidth - scrollContainerPadding;
1504
1571
  const itemWidth = (availableWidth - gaps) / itemsPerRow;
1505
1572
 
1506
1573
  return (
1507
1574
  <TouchableOpacity
1508
1575
  key={photo.id}
1509
1576
  style={[
1510
- styles.photoItem,
1577
+ fileManagementStyles.photoItem,
1511
1578
  {
1512
1579
  width: itemWidth,
1513
1580
  height: itemWidth,
@@ -1516,10 +1583,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1516
1583
  onPress={() => handleFileOpen(photo)}
1517
1584
  activeOpacity={0.8}
1518
1585
  >
1519
- <View style={styles.photoContainer}>
1586
+ <View style={fileManagementStyles.photoContainer}>
1520
1587
  <ExpoImage
1521
1588
  source={{ uri: downloadUrl }}
1522
- style={styles.photoImage}
1589
+ style={fileManagementStyles.photoImage}
1523
1590
  contentFit="cover"
1524
1591
  transition={120}
1525
1592
  cachePolicy="memory-disk"
@@ -1538,26 +1605,28 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1538
1605
 
1539
1606
  if (photos.length === 0) {
1540
1607
  return (
1541
- <View style={styles.emptyState}>
1608
+ <View style={fileManagementStyles.emptyState}>
1542
1609
  <Ionicons name="images-outline" size={64} color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'} />
1543
- <Text style={[styles.emptyStateTitle, { color: themeStyles.textColor }]}>No Photos Yet</Text>
1544
- <Text style={[styles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}> {
1610
+ <Text style={[fileManagementStyles.emptyStateTitle, { color: themeStyles.textColor }]}>No Photos Yet</Text>
1611
+ <Text style={[fileManagementStyles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}> {
1545
1612
  user?.id === targetUserId
1546
- ? `Upload photos to get started. You can select multiple photos at once${Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}`
1613
+ ? `Upload photos to get started. You can select multiple photos at once.`
1547
1614
  : "This user hasn't uploaded any photos yet"
1548
1615
  } </Text>
1549
1616
  {user?.id === targetUserId && (
1550
1617
  <TouchableOpacity
1551
- style={[styles.emptyStateButton, { backgroundColor: themeStyles.primaryColor }]}
1618
+ style={[fileManagementStyles.emptyStateButton, { backgroundColor: themeStyles.primaryColor }]}
1552
1619
  onPress={handleFileUpload}
1553
- disabled={uploading}
1620
+ disabled={uploading || isPickingDocument}
1554
1621
  >
1555
1622
  {uploading ? (
1556
1623
  <ActivityIndicator size="small" color="#FFFFFF" />
1624
+ ) : isPickingDocument ? (
1625
+ <ActivityIndicator size="small" color="#FFFFFF" />
1557
1626
  ) : (
1558
1627
  <>
1559
1628
  <Ionicons name="cloud-upload" size={20} color="#FFFFFF" />
1560
- <Text style={styles.emptyStateButtonText}>Upload Photos</Text>
1629
+ <Text style={fileManagementStyles.emptyStateButtonText}>Upload Photos</Text>
1561
1630
  </>
1562
1631
  )}
1563
1632
  </TouchableOpacity>
@@ -1569,8 +1638,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1569
1638
  return (
1570
1639
  <ScrollView
1571
1640
  ref={photoScrollViewRef}
1572
- style={styles.scrollView}
1573
- contentContainerStyle={styles.photoScrollContainer}
1641
+ style={fileManagementStyles.scrollView}
1642
+ contentContainerStyle={fileManagementStyles.photoScrollContainer}
1574
1643
  refreshControl={
1575
1644
  <RefreshControl
1576
1645
  refreshing={refreshing}
@@ -1589,9 +1658,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1589
1658
  scrollEventThrottle={250}
1590
1659
  >
1591
1660
  {loadingDimensions && (
1592
- <View style={styles.dimensionsLoadingIndicator}>
1661
+ <View style={fileManagementStyles.dimensionsLoadingIndicator}>
1593
1662
  <ActivityIndicator size="small" color={themeStyles.primaryColor} />
1594
- <Text style={[styles.dimensionsLoadingText, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>Loading photo layout...</Text>
1663
+ <Text style={[fileManagementStyles.dimensionsLoadingText, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>Loading photo layout...</Text>
1595
1664
  </View>
1596
1665
  )}
1597
1666
 
@@ -1603,7 +1672,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1603
1672
  renderJustifiedPhotoItem={renderJustifiedPhotoItem}
1604
1673
  renderSimplePhotoItem={renderPhotoItem}
1605
1674
  textColor={themeStyles.textColor}
1606
- containerWidth={containerWidth}
1675
+ containerWidth={safeContainerWidth}
1607
1676
  />
1608
1677
  </ScrollView>
1609
1678
  );
@@ -1622,417 +1691,37 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1622
1691
  createJustifiedRows,
1623
1692
  renderJustifiedPhotoItem,
1624
1693
  renderPhotoItem,
1625
- containerWidth
1694
+ safeContainerWidth
1626
1695
  ]);
1627
1696
 
1628
1697
  // Inline justified grid removed (moved to components/photogrid/JustifiedPhotoGrid.tsx)
1629
1698
 
1630
- const renderFileDetailsModal = () => {
1631
- const backgroundColor = themeStyles.backgroundColor;
1632
- const borderColor = themeStyles.borderColor;
1633
-
1634
- return (
1635
- <Modal
1636
- visible={showFileDetails}
1637
- animationType="slide"
1638
- presentationStyle="pageSheet"
1639
- onRequestClose={() => setShowFileDetails(false)}
1640
- >
1641
- <View style={[styles.modalContainer, { backgroundColor }]}>
1642
- <View style={[styles.modalHeader, { borderBottomColor: borderColor }]}>
1643
- <TouchableOpacity
1644
- style={styles.modalCloseButton}
1645
- onPress={() => setShowFileDetails(false)}
1646
- >
1647
- <Ionicons name="close" size={24} color={themeStyles.textColor} />
1648
- </TouchableOpacity>
1649
- <Text style={[styles.modalTitle, { color: themeStyles.textColor }]}>File Details</Text>
1650
- <View style={styles.modalPlaceholder} />
1651
- </View>
1652
-
1653
- {selectedFile && (
1654
- <ScrollView style={styles.modalContent}>
1655
- <View style={[styles.fileDetailCard, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }]}>
1656
- <View style={styles.fileDetailIcon}>
1657
- <Ionicons
1658
- name={getFileIcon(selectedFile.contentType) as any}
1659
- size={64}
1660
- color={themeStyles.primaryColor}
1661
- />
1662
- </View>
1663
-
1664
- <Text style={[styles.fileDetailName, { color: themeStyles.textColor }]}>
1665
- {selectedFile.filename}
1666
- </Text>
1667
-
1668
- <View style={styles.fileDetailInfo}>
1669
- <View style={styles.detailRow}>
1670
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1671
- Size:
1672
- </Text>
1673
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1674
- {formatFileSize(selectedFile.length)}
1675
- </Text>
1676
- </View>
1677
-
1678
- <View style={styles.detailRow}>
1679
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1680
- Type:
1681
- </Text>
1682
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1683
- {selectedFile.contentType}
1684
- </Text>
1685
- </View>
1686
-
1687
- <View style={styles.detailRow}>
1688
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1689
- Uploaded:
1690
- </Text>
1691
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1692
- {new Date(selectedFile.uploadDate).toLocaleString()}
1693
- </Text>
1694
- </View>
1695
-
1696
- {selectedFile.metadata?.description && (
1697
- <View style={styles.detailRow}>
1698
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1699
- Description:
1700
- </Text>
1701
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1702
- {selectedFile.metadata.description}
1703
- </Text>
1704
- </View>
1705
- )}
1706
- </View>
1707
-
1708
- <View style={styles.modalActions}>
1709
- <TouchableOpacity
1710
- style={[styles.modalActionButton, { backgroundColor: themeStyles.primaryColor }]}
1711
- onPress={() => {
1712
- handleFileDownload(selectedFile.id, selectedFile.filename);
1713
- setShowFileDetails(false);
1714
- }}
1715
- >
1716
- <Ionicons name="download" size={20} color="#FFFFFF" />
1717
- <Text style={styles.modalActionText}>Download</Text>
1718
- </TouchableOpacity>
1719
-
1720
- {(user?.id === targetUserId) && (
1721
- <TouchableOpacity
1722
- style={[styles.modalActionButton, { backgroundColor: themeStyles.dangerColor }]}
1723
- onPress={() => {
1724
- setShowFileDetails(false);
1725
- handleFileDelete(selectedFile.id, selectedFile.filename);
1726
- }}
1727
- >
1728
- <Ionicons name="trash" size={20} color="#FFFFFF" />
1729
- <Text style={styles.modalActionText}>Delete</Text>
1730
- </TouchableOpacity>
1731
- )}
1732
- </View>
1733
- </View>
1734
- </ScrollView>
1735
- )}
1736
- </View>
1737
- </Modal>
1738
- );
1739
- };
1740
-
1741
- const renderFileViewer = () => {
1742
- if (!openedFile) return null;
1743
-
1744
- const backgroundColor = themeStyles.backgroundColor;
1745
- const borderColor = themeStyles.borderColor;
1746
-
1747
- const isImage = openedFile.contentType.startsWith('image/');
1748
- const isText = openedFile.contentType.startsWith('text/') ||
1749
- openedFile.contentType.includes('json') ||
1750
- openedFile.contentType.includes('xml') ||
1751
- openedFile.contentType.includes('javascript') ||
1752
- openedFile.contentType.includes('typescript');
1753
- const isPDF = openedFile.contentType.includes('pdf');
1754
- const isVideo = openedFile.contentType.startsWith('video/');
1755
- const isAudio = openedFile.contentType.startsWith('audio/');
1756
-
1757
- return (
1758
- <View style={[styles.fileViewerContainer, { backgroundColor }]}>
1759
- {/* File Viewer Header */}
1760
- <View style={[styles.fileViewerHeader, { borderBottomColor: borderColor }]}>
1761
- <TouchableOpacity
1762
- style={styles.backButton}
1763
- onPress={handleCloseFile}
1764
- >
1765
- <Ionicons name="arrow-back" size={24} color={themeStyles.textColor} />
1766
- </TouchableOpacity>
1767
- <View style={styles.fileViewerTitleContainer}>
1768
- <Text style={[styles.fileViewerTitle, { color: themeStyles.textColor }]} numberOfLines={1}>
1769
- {openedFile.filename}
1770
- </Text>
1771
- <Text style={[styles.fileViewerSubtitle, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1772
- {formatFileSize(openedFile.length)} • {openedFile.contentType}
1773
- </Text>
1774
- </View>
1775
- <View style={styles.fileViewerActions}>
1776
- <TouchableOpacity
1777
- style={[styles.actionButton, { backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0' }]}
1778
- onPress={() => handleFileDownload(openedFile.id, openedFile.filename)}
1779
- >
1780
- <Ionicons name="download" size={20} color={themeStyles.primaryColor} />
1781
- </TouchableOpacity>
1782
- <TouchableOpacity
1783
- style={[
1784
- styles.actionButton,
1785
- {
1786
- backgroundColor: showFileDetailsInViewer
1787
- ? themeStyles.primaryColor
1788
- : (themeStyles.isDarkTheme ? '#333333' : '#F0F0F0')
1789
- }
1790
- ]}
1791
- onPress={() => setShowFileDetailsInViewer(!showFileDetailsInViewer)}
1792
- >
1793
- <Ionicons
1794
- name={showFileDetailsInViewer ? "chevron-up" : "information-circle"}
1795
- size={20}
1796
- color={showFileDetailsInViewer ? "#FFFFFF" : themeStyles.primaryColor}
1797
- />
1798
- </TouchableOpacity>
1799
- </View>
1800
- </View>
1801
-
1802
- {/* File Details Section */}
1803
- {showFileDetailsInViewer && (
1804
- <View style={[styles.fileDetailsSection, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }]}>
1805
- <View style={styles.fileDetailsSectionHeader}>
1806
- <Text style={[styles.fileDetailsSectionTitle, { color: themeStyles.textColor }]}>
1807
- File Details
1808
- </Text>
1809
- <TouchableOpacity
1810
- style={styles.fileDetailsSectionToggle}
1811
- onPress={() => setShowFileDetailsInViewer(false)}
1812
- >
1813
- <Ionicons name="chevron-up" size={20} color={themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'} />
1814
- </TouchableOpacity>
1815
- </View>
1816
-
1817
- <View style={styles.fileDetailInfo}>
1818
- <View style={styles.detailRow}>
1819
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1820
- File Name:
1821
- </Text>
1822
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1823
- {openedFile.filename}
1824
- </Text>
1825
- </View>
1826
-
1827
- <View style={styles.detailRow}>
1828
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1829
- Size:
1830
- </Text>
1831
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1832
- {formatFileSize(openedFile.length)}
1833
- </Text>
1834
- </View>
1835
-
1836
- <View style={styles.detailRow}>
1837
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1838
- Type:
1839
- </Text>
1840
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1841
- {openedFile.contentType}
1842
- </Text>
1843
- </View>
1844
-
1845
- <View style={styles.detailRow}>
1846
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1847
- Uploaded:
1848
- </Text>
1849
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1850
- {new Date(openedFile.uploadDate).toLocaleString()}
1851
- </Text>
1852
- </View>
1853
-
1854
- {openedFile.metadata?.description && (
1855
- <View style={styles.detailRow}>
1856
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1857
- Description:
1858
- </Text>
1859
- <Text style={[styles.detailValue, { color: themeStyles.textColor }]}>
1860
- {openedFile.metadata.description}
1861
- </Text>
1862
- </View>
1863
- )}
1864
-
1865
- <View style={styles.detailRow}>
1866
- <Text style={[styles.detailLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1867
- File ID:
1868
- </Text>
1869
- <Text style={[styles.detailValue, { color: themeStyles.textColor, fontSize: 12, fontFamily: Platform.OS === 'web' ? 'monospace' : 'Courier' }]}>
1870
- {openedFile.id}
1871
- </Text>
1872
- </View>
1873
- </View>
1874
-
1875
- <View style={styles.fileDetailsActions}>
1876
- <TouchableOpacity
1877
- style={[styles.fileDetailsActionButton, { backgroundColor: themeStyles.primaryColor }]}
1878
- onPress={() => handleFileDownload(openedFile.id, openedFile.filename)}
1879
- >
1880
- <Ionicons name="download" size={16} color="#FFFFFF" />
1881
- <Text style={styles.fileDetailsActionText}>Download</Text>
1882
- </TouchableOpacity>
1883
-
1884
- {(user?.id === targetUserId) && (
1885
- <TouchableOpacity
1886
- style={[styles.fileDetailsActionButton, { backgroundColor: themeStyles.dangerColor }]}
1887
- onPress={() => {
1888
- handleCloseFile();
1889
- handleFileDelete(openedFile.id, openedFile.filename);
1890
- }}
1891
- >
1892
- <Ionicons name="trash" size={16} color="#FFFFFF" />
1893
- <Text style={styles.fileDetailsActionText}>Delete</Text>
1894
- </TouchableOpacity>
1895
- )}
1896
- </View>
1897
- </View>
1898
- )}
1899
1699
 
1900
- {/* File Content */}
1901
- <ScrollView
1902
- style={[
1903
- styles.fileViewerContent,
1904
- showFileDetailsInViewer && styles.fileViewerContentWithDetails
1905
- ]}
1906
- contentContainerStyle={styles.fileViewerContentContainer}
1907
- >
1908
- {loadingFileContent ? (
1909
- <View style={styles.fileViewerLoading}>
1910
- <ActivityIndicator size="large" color={themeStyles.primaryColor} />
1911
- <Text style={[styles.fileViewerLoadingText, { color: themeStyles.textColor }]}>
1912
- Loading file content...
1913
- </Text>
1914
- </View>
1915
- ) : isImage && fileContent ? (
1916
- <View style={styles.imageContainer}>
1917
- <ExpoImage
1918
- source={{ uri: fileContent }}
1919
- style={{ width: '100%', height: 400, borderRadius: 8 }}
1920
- contentFit="contain"
1921
- transition={120}
1922
- cachePolicy="memory-disk"
1923
- onError={() => {
1924
- // Image failed to load
1925
- }}
1926
- accessibilityLabel={openedFile.filename}
1927
- />
1928
- </View>
1929
- ) : isText && fileContent ? (
1930
- <View style={[styles.textContainer, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor }]}>
1931
- <ScrollView style={{ flex: 1 }} nestedScrollEnabled>
1932
- <Text style={[styles.textContent, { color: themeStyles.textColor }]}>
1933
- {fileContent}
1934
- </Text>
1935
- </ScrollView>
1936
- </View>
1937
- ) : isPDF && fileContent && Platform.OS === 'web' ? (
1938
- <View style={styles.pdfContainer}>
1939
- <iframe
1940
- src={fileContent}
1941
- width="100%"
1942
- height="600px"
1943
- style={{ border: 'none', borderRadius: 8 }}
1944
- title={openedFile.filename}
1945
- />
1946
- </View>
1947
- ) : isVideo && fileContent ? (
1948
- <View style={styles.mediaContainer}>
1949
- {Platform.OS === 'web' ? (
1950
- <video
1951
- controls
1952
- style={{
1953
- width: '100%',
1954
- maxHeight: '70vh',
1955
- borderRadius: 8,
1956
- }}
1957
- >
1958
- <source src={fileContent} type={openedFile.contentType} />
1959
- Your browser does not support the video tag.
1960
- </video>
1961
- ) : (
1962
- <Text style={[styles.unsupportedText, { color: themeStyles.textColor }]}>
1963
- Video playback not supported on mobile
1964
- </Text>
1965
- )}
1966
- </View>
1967
- ) : isAudio && fileContent ? (
1968
- <View style={styles.mediaContainer}>
1969
- {Platform.OS === 'web' ? (
1970
- <audio
1971
- controls
1972
- style={{
1973
- width: '100%',
1974
- borderRadius: 8,
1975
- }}
1976
- >
1977
- <source src={fileContent} type={openedFile.contentType} />
1978
- Your browser does not support the audio tag.
1979
- </audio>
1980
- ) : (
1981
- <Text style={[styles.unsupportedText, { color: themeStyles.textColor }]}>
1982
- Audio playback not supported on mobile
1983
- </Text>
1984
- )}
1985
- </View>
1986
- ) : (
1987
- <View style={styles.unsupportedFileContainer}>
1988
- <Ionicons
1989
- name={getFileIcon(openedFile.contentType) as any}
1990
- size={64}
1991
- color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'}
1992
- />
1993
- <Text style={[styles.unsupportedFileTitle, { color: themeStyles.textColor }]}>
1994
- Preview Not Available
1995
- </Text>
1996
- <Text style={[styles.unsupportedFileDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1997
- This file type cannot be previewed in the browser.{'\n'}
1998
- Download the file to view its contents.
1999
- </Text>
2000
- <TouchableOpacity
2001
- style={[styles.downloadButtonLarge, { backgroundColor: themeStyles.primaryColor }]}
2002
- onPress={() => handleFileDownload(openedFile.id, openedFile.filename)}
2003
- >
2004
- <Ionicons name="download" size={20} color="#FFFFFF" />
2005
- <Text style={styles.downloadButtonText}>Download File</Text>
2006
- </TouchableOpacity>
2007
- </View>
2008
- )}
2009
- </ScrollView>
2010
- </View>
2011
- );
2012
- };
2013
1700
 
2014
1701
  const renderEmptyState = () => (
2015
- <View style={styles.emptyState}>
1702
+ <View style={fileManagementStyles.emptyState}>
2016
1703
  <Ionicons name="folder-open-outline" size={64} color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'} />
2017
- <Text style={[styles.emptyStateTitle, { color: themeStyles.textColor }]}>No Files Yet</Text>
2018
- <Text style={[styles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
1704
+ <Text style={[fileManagementStyles.emptyStateTitle, { color: themeStyles.textColor }]}>No Files Yet</Text>
1705
+ <Text style={[fileManagementStyles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2019
1706
  {user?.id === targetUserId
2020
- ? `Upload files to get started. You can select multiple files at once${Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}`
1707
+ ? `Upload files to get started. You can select multiple files at once.`
2021
1708
  : "This user hasn't uploaded any files yet"
2022
1709
  }
2023
1710
  </Text>
2024
1711
  {user?.id === targetUserId && (
2025
1712
  <TouchableOpacity
2026
- style={[styles.emptyStateButton, { backgroundColor: themeStyles.primaryColor }]}
1713
+ style={[fileManagementStyles.emptyStateButton, { backgroundColor: themeStyles.primaryColor }]}
2027
1714
  onPress={handleFileUpload}
2028
- disabled={uploading}
1715
+ disabled={uploading || isPickingDocument}
2029
1716
  >
2030
1717
  {uploading ? (
2031
1718
  <ActivityIndicator size="small" color="#FFFFFF" />
1719
+ ) : isPickingDocument ? (
1720
+ <ActivityIndicator size="small" color="#FFFFFF" />
2032
1721
  ) : (
2033
1722
  <>
2034
1723
  <Ionicons name="cloud-upload" size={20} color="#FFFFFF" />
2035
- <Text style={styles.emptyStateButtonText}>Upload Files</Text>
1724
+ <Text style={fileManagementStyles.emptyStateButtonText}>Upload Files</Text>
2036
1725
  </>
2037
1726
  )}
2038
1727
  </TouchableOpacity>
@@ -2043,7 +1732,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2043
1732
  // Professional Skeleton Loading Component with Advanced Shimmer Effect
2044
1733
  const SkeletonLoader = React.memo(() => {
2045
1734
  const shimmerAnim = useRef(new Animated.Value(0)).current;
2046
- const skeletonContainerWidth = containerWidth || 400;
1735
+ const skeletonContainerWidth = safeContainerWidth;
2047
1736
 
2048
1737
  useEffect(() => {
2049
1738
  const shimmer = Animated.loop(
@@ -2070,7 +1759,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2070
1759
  outputRange: [-skeletonContainerWidth * 2 + delay, skeletonContainerWidth * 2 + delay],
2071
1760
  });
2072
1761
 
2073
- return (
1762
+ return (
2074
1763
  <View
2075
1764
  style={[
2076
1765
  {
@@ -2110,8 +1799,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2110
1799
  style={{
2111
1800
  width: skeletonContainerWidth,
2112
1801
  height: '100%',
2113
- backgroundColor: themeStyles.isDarkTheme
2114
- ? 'rgba(255, 255, 255, 0.08)'
1802
+ backgroundColor: themeStyles.isDarkTheme
1803
+ ? 'rgba(255, 255, 255, 0.08)'
2115
1804
  : 'rgba(255, 255, 255, 0.8)',
2116
1805
  shadowColor: themeStyles.isDarkTheme ? '#000' : '#FFF',
2117
1806
  shadowOffset: { width: 0, height: 0 },
@@ -2120,8 +1809,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2120
1809
  }}
2121
1810
  />
2122
1811
  </Animated.View>
2123
- </View>
2124
- );
1812
+ </View>
1813
+ );
2125
1814
  };
2126
1815
 
2127
1816
  // Skeleton file item matching GroupedSection structure
@@ -2141,18 +1830,18 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2141
1830
  >
2142
1831
  {/* Icon/Image skeleton */}
2143
1832
  <SkeletonBox width={44} height={44} borderRadius={8} delay={index * 50} />
2144
-
1833
+
2145
1834
  {/* Content skeleton */}
2146
1835
  <View style={{ flex: 1, marginLeft: 12, justifyContent: 'center' }}>
2147
- <SkeletonBox
2148
- width={index % 3 === 0 ? '85%' : index % 3 === 1 ? '70%' : '90%'}
2149
- height={16}
2150
- style={{ marginBottom: 8 }}
1836
+ <SkeletonBox
1837
+ width={index % 3 === 0 ? '85%' : index % 3 === 1 ? '70%' : '90%'}
1838
+ height={16}
1839
+ style={{ marginBottom: 8 }}
2151
1840
  delay={index * 50 + 20}
2152
1841
  />
2153
- <SkeletonBox
2154
- width={index % 2 === 0 ? '50%' : '60%'}
2155
- height={12}
1842
+ <SkeletonBox
1843
+ width={index % 2 === 0 ? '50%' : '60%'}
1844
+ height={12}
2156
1845
  delay={index * 50 + 40}
2157
1846
  />
2158
1847
  </View>
@@ -2160,11 +1849,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2160
1849
  );
2161
1850
 
2162
1851
  return (
2163
- <View style={[styles.container, { backgroundColor }]}>
1852
+ <View style={[fileManagementStyles.container, { backgroundColor }]}>
2164
1853
  {/* Header Skeleton */}
2165
- <View style={[styles.header, { borderBottomColor: themeStyles.borderColor, borderBottomWidth: StyleSheet.hairlineWidth }]}>
1854
+ <View style={[fileManagementStyles.header, { borderBottomColor: themeStyles.borderColor, borderBottomWidth: StyleSheet.hairlineWidth }]}>
2166
1855
  <SkeletonBox width={44} height={44} borderRadius={12} />
2167
- <View style={[styles.headerTitleContainer, { flex: 1 }]}>
1856
+ <View style={[fileManagementStyles.headerTitleContainer, { flex: 1 }]}>
2168
1857
  <SkeletonBox width={140} height={20} style={{ marginBottom: 6 }} />
2169
1858
  <SkeletonBox width={100} height={14} />
2170
1859
  </View>
@@ -2172,28 +1861,24 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2172
1861
  </View>
2173
1862
 
2174
1863
  {/* Controls Bar Skeleton */}
2175
- <View style={styles.controlsBar}>
1864
+ <View style={fileManagementStyles.controlsBar}>
2176
1865
  <SkeletonBox width={100} height={36} borderRadius={18} />
2177
1866
  <SkeletonBox width={44} height={44} borderRadius={22} />
2178
1867
  </View>
2179
1868
 
2180
1869
  {/* Search Bar Skeleton */}
2181
- <View style={[styles.searchContainer, {
2182
- backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
2183
- borderColor: themeStyles.borderColor,
2184
- borderWidth: StyleSheet.hairlineWidth,
1870
+ <View style={[fileManagementStyles.searchContainer, {
1871
+ backgroundColor: themeStyles.colors.card,
2185
1872
  }]}>
2186
1873
  <SkeletonBox width="100%" height={44} borderRadius={12} />
2187
1874
  </View>
2188
1875
 
2189
1876
  {/* Stats Container Skeleton */}
2190
- <View style={[styles.statsContainer, {
2191
- backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
2192
- borderColor: themeStyles.borderColor,
2193
- borderWidth: StyleSheet.hairlineWidth,
1877
+ <View style={[fileManagementStyles.statsContainer, {
1878
+ backgroundColor: themeStyles.colors.card,
2194
1879
  }]}>
2195
1880
  {[1, 2, 3].map((i) => (
2196
- <View key={i} style={styles.statItem}>
1881
+ <View key={i} style={fileManagementStyles.statItem}>
2197
1882
  <SkeletonBox width={50} height={20} style={{ marginBottom: 4 }} delay={i * 30} />
2198
1883
  <SkeletonBox width={40} height={14} delay={i * 30 + 15} />
2199
1884
  </View>
@@ -2201,16 +1886,15 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2201
1886
  </View>
2202
1887
 
2203
1888
  {/* File List Skeleton - Matching GroupedSection */}
2204
- <ScrollView
2205
- style={styles.scrollView}
2206
- contentContainerStyle={styles.scrollContainer}
1889
+ <ScrollView
1890
+ style={fileManagementStyles.scrollView}
1891
+ contentContainerStyle={fileManagementStyles.scrollContainer}
2207
1892
  showsVerticalScrollIndicator={false}
2208
1893
  >
2209
1894
  <View style={{
2210
- backgroundColor: themeStyles.isDarkTheme ? '#121212' : '#FFFFFF',
2211
- borderRadius: 12,
1895
+ backgroundColor: themeStyles.colors.card,
1896
+ borderRadius: 18,
2212
1897
  overflow: 'hidden',
2213
- marginHorizontal: 16,
2214
1898
  marginTop: 8,
2215
1899
  }}>
2216
1900
  {[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
@@ -2230,20 +1914,59 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2230
1914
  if (!selectMode && openedFile) {
2231
1915
  return (
2232
1916
  <>
2233
- {renderFileViewer()}
2234
- {renderFileDetailsModal()}
1917
+ <FileViewer
1918
+ file={openedFile}
1919
+ fileContent={fileContent}
1920
+ loadingFileContent={loadingFileContent}
1921
+ showFileDetailsInViewer={showFileDetailsInViewer}
1922
+ onToggleDetails={() => setShowFileDetailsInViewer(!showFileDetailsInViewer)}
1923
+ onClose={handleCloseFile}
1924
+ onDownload={handleFileDownload}
1925
+ onDelete={handleFileDelete}
1926
+ themeStyles={themeStyles}
1927
+ isOwner={user?.id === targetUserId}
1928
+ />
1929
+ <FileDetailsModal
1930
+ visible={showFileDetails}
1931
+ file={selectedFile}
1932
+ onClose={() => setShowFileDetails(false)}
1933
+ onDownload={handleFileDownload}
1934
+ onDelete={handleFileDelete}
1935
+ themeStyles={themeStyles}
1936
+ isOwner={user?.id === targetUserId}
1937
+ />
2235
1938
  </>
2236
1939
  );
2237
1940
  }
2238
1941
 
1942
+ // If upload preview is showing, render it inline instead of the file list
1943
+ if (showUploadPreview) {
1944
+ return (
1945
+ <View style={fileManagementStyles.container}>
1946
+ <Header
1947
+ title="Review Files"
1948
+ subtitle={`${pendingFiles.length} file${pendingFiles.length !== 1 ? 's' : ''} ready to upload`}
1949
+ onBack={handleCancelUpload}
1950
+ showBackButton
1951
+ variant="minimal"
1952
+ elevation="none"
1953
+ titleAlignment="left"
1954
+ />
1955
+ <UploadPreview
1956
+ visible={true}
1957
+ pendingFiles={pendingFiles}
1958
+ onConfirm={handleConfirmUpload}
1959
+ onCancel={handleCancelUpload}
1960
+ onRemoveFile={removePendingFile}
1961
+ themeStyles={themeStyles}
1962
+ inline={true}
1963
+ />
1964
+ </View>
1965
+ );
1966
+ }
1967
+
2239
1968
  return (
2240
- <View
2241
- ref={containerRef}
2242
- style={[
2243
- styles.container,
2244
- isDragging && Platform.OS === 'web' && styles.dragOverlay
2245
- ]}
2246
- >
1969
+ <View style={fileManagementStyles.container}>
2247
1970
  <Header
2248
1971
  title={selectMode ? (multiSelect ? `${selectedIds.size}${maxSelection ? '/' + maxSelection : ''} Selected` : 'Select a File') : (viewMode === 'photos' ? 'Photos' : 'File Management')}
2249
1972
  subtitle={selectMode ? (multiSelect ? `${filteredFiles.length} available` : 'Tap to select') : `${filteredFiles.length} ${filteredFiles.length === 1 ? 'item' : 'items'}`}
@@ -2292,98 +2015,70 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2292
2015
  }
2293
2016
  ] : undefined}
2294
2017
  onBack={onClose || goBack}
2295
- theme={theme}
2018
+
2296
2019
  showBackButton
2297
2020
  variant="minimal"
2298
2021
  elevation="none"
2299
2022
  titleAlignment="left"
2300
2023
  />
2301
2024
 
2302
- <View style={styles.controlsBar}>
2303
- <ScrollView
2304
- horizontal
2025
+ <View style={fileManagementStyles.controlsBar}>
2026
+ <ScrollView
2027
+ horizontal
2305
2028
  showsHorizontalScrollIndicator={false}
2306
- style={styles.viewModeScroll}
2029
+ style={fileManagementStyles.viewModeScroll}
2307
2030
  >
2308
2031
  <View style={[
2309
- styles.viewModeToggle,
2032
+ fileManagementStyles.viewModeToggle,
2310
2033
  {
2311
- backgroundColor: themeStyles.isDarkTheme ? '#181818' : '#FFFFFF',
2312
- borderWidth: 1,
2313
- borderColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#E8E9EA',
2034
+ backgroundColor: themeStyles.colors.card,
2314
2035
  }
2315
2036
  ]}>
2316
- <TouchableOpacity
2317
- style={[
2318
- styles.viewModeButton,
2319
- viewMode === 'all' && { backgroundColor: themeStyles.primaryColor }
2320
- ]}
2037
+ <AnimatedButton
2038
+ isSelected={viewMode === 'all'}
2321
2039
  onPress={() => setViewMode('all')}
2322
- >
2323
- <Ionicons
2324
- name="folder"
2325
- size={18}
2326
- color={viewMode === 'all' ? '#FFFFFF' : themeStyles.textColor}
2327
- />
2328
- </TouchableOpacity>
2329
- <TouchableOpacity
2330
- style={[
2331
- styles.viewModeButton,
2332
- viewMode === 'photos' && { backgroundColor: themeStyles.primaryColor }
2333
- ]}
2040
+ icon={viewMode === 'all' ? 'folder' : 'folder-outline'}
2041
+ primaryColor={themeStyles.primaryColor}
2042
+ textColor={themeStyles.textColor}
2043
+ style={fileManagementStyles.viewModeButton}
2044
+ />
2045
+ <AnimatedButton
2046
+ isSelected={viewMode === 'photos'}
2334
2047
  onPress={() => setViewMode('photos')}
2335
- >
2336
- <Ionicons
2337
- name="images"
2338
- size={18}
2339
- color={viewMode === 'photos' ? '#FFFFFF' : themeStyles.textColor}
2340
- />
2341
- </TouchableOpacity>
2342
- <TouchableOpacity
2343
- style={[
2344
- styles.viewModeButton,
2345
- viewMode === 'videos' && { backgroundColor: themeStyles.primaryColor }
2346
- ]}
2048
+ icon={viewMode === 'photos' ? 'image-multiple' : 'image-multiple-outline'}
2049
+ primaryColor={themeStyles.primaryColor}
2050
+ textColor={themeStyles.textColor}
2051
+ style={fileManagementStyles.viewModeButton}
2052
+ />
2053
+ <AnimatedButton
2054
+ isSelected={viewMode === 'videos'}
2347
2055
  onPress={() => setViewMode('videos')}
2348
- >
2349
- <Ionicons
2350
- name="videocam"
2351
- size={18}
2352
- color={viewMode === 'videos' ? '#FFFFFF' : themeStyles.textColor}
2353
- />
2354
- </TouchableOpacity>
2355
- <TouchableOpacity
2356
- style={[
2357
- styles.viewModeButton,
2358
- viewMode === 'documents' && { backgroundColor: themeStyles.primaryColor }
2359
- ]}
2056
+ icon={viewMode === 'videos' ? 'video' : 'video-outline'}
2057
+ primaryColor={themeStyles.primaryColor}
2058
+ textColor={themeStyles.textColor}
2059
+ style={fileManagementStyles.viewModeButton}
2060
+ />
2061
+ <AnimatedButton
2062
+ isSelected={viewMode === 'documents'}
2360
2063
  onPress={() => setViewMode('documents')}
2361
- >
2362
- <Ionicons
2363
- name="document-text"
2364
- size={18}
2365
- color={viewMode === 'documents' ? '#FFFFFF' : themeStyles.textColor}
2366
- />
2367
- </TouchableOpacity>
2368
- <TouchableOpacity
2369
- style={[
2370
- styles.viewModeButton,
2371
- viewMode === 'audio' && { backgroundColor: themeStyles.primaryColor }
2372
- ]}
2064
+ icon={viewMode === 'documents' ? 'file-document' : 'file-document-outline'}
2065
+ primaryColor={themeStyles.primaryColor}
2066
+ textColor={themeStyles.textColor}
2067
+ style={fileManagementStyles.viewModeButton}
2068
+ />
2069
+ <AnimatedButton
2070
+ isSelected={viewMode === 'audio'}
2373
2071
  onPress={() => setViewMode('audio')}
2374
- >
2375
- <Ionicons
2376
- name="musical-notes"
2377
- size={18}
2378
- color={viewMode === 'audio' ? '#FFFFFF' : themeStyles.textColor}
2379
- />
2380
- </TouchableOpacity>
2072
+ icon={viewMode === 'audio' ? 'music-note' : 'music-note-outline'}
2073
+ primaryColor={themeStyles.primaryColor}
2074
+ textColor={themeStyles.textColor}
2075
+ style={fileManagementStyles.viewModeButton}
2076
+ />
2381
2077
  </View>
2382
2078
  </ScrollView>
2383
2079
  <TouchableOpacity
2384
- style={[styles.sortButton, {
2385
- backgroundColor: themeStyles.isDarkTheme ? '#181818' : '#FFFFFF',
2386
- borderColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#E8E9EA',
2080
+ style={[fileManagementStyles.sortButton, {
2081
+ backgroundColor: themeStyles.colors.card,
2387
2082
  }]}
2388
2083
  onPress={() => {
2389
2084
  // Cycle through sort options: date -> size -> name -> type -> date
@@ -2397,37 +2092,38 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2397
2092
  }
2398
2093
  }}
2399
2094
  >
2400
- <Ionicons
2401
- name={sortOrder === 'asc' ? 'arrow-up' : 'arrow-down'}
2402
- size={18}
2403
- color={themeStyles.textColor}
2404
- />
2405
- <Ionicons
2095
+ <MaterialCommunityIcons
2406
2096
  name={
2407
2097
  sortBy === 'date' ? 'calendar' :
2408
- sortBy === 'size' ? 'resize' :
2409
- sortBy === 'name' ? 'text' : 'document'
2098
+ sortBy === 'size' ? 'sort-numeric-variant' :
2099
+ sortBy === 'name' ? 'sort-alphabetical-variant' : 'file-document-outline'
2410
2100
  }
2411
2101
  size={16}
2412
2102
  color={themeStyles.textColor}
2413
- style={{ marginLeft: 4 }}
2103
+ />
2104
+ <MaterialCommunityIcons
2105
+ name={sortOrder === 'asc' ? 'arrow-up' : 'arrow-down'}
2106
+ size={14}
2107
+ color={themeStyles.colors.secondaryText}
2414
2108
  />
2415
2109
  </TouchableOpacity>
2416
2110
  {user?.id === targetUserId && (!selectMode || (selectMode && allowUploadInSelectMode)) && (
2417
2111
  <TouchableOpacity
2418
- style={[styles.uploadButton, { backgroundColor: themeStyles.primaryColor }]}
2112
+ style={[fileManagementStyles.uploadButton, { backgroundColor: themeStyles.primaryColor }]}
2419
2113
  onPress={handleFileUpload}
2420
- disabled={uploading}
2114
+ disabled={uploading || isPickingDocument}
2421
2115
  >
2422
2116
  {uploading ? (
2423
- <View style={styles.uploadProgress}>
2117
+ <View style={fileManagementStyles.uploadProgress}>
2424
2118
  <ActivityIndicator size="small" color="#FFFFFF" />
2425
2119
  {uploadProgress && (
2426
- <Text style={styles.uploadProgressText}>
2120
+ <Text style={fileManagementStyles.uploadProgressText}>
2427
2121
  {uploadProgress.current}/{uploadProgress.total}
2428
2122
  </Text>
2429
2123
  )}
2430
2124
  </View>
2125
+ ) : isPickingDocument ? (
2126
+ <ActivityIndicator size="small" color="#FFFFFF" />
2431
2127
  ) : (
2432
2128
  <Ionicons name="add" size={22} color="#FFFFFF" />
2433
2129
  )}
@@ -2438,26 +2134,25 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2438
2134
  {/* Search Bar */}
2439
2135
  {files.length > 0 && (viewMode === 'all' || files.some(f => f.contentType.startsWith('image/'))) && (
2440
2136
  <View style={[
2441
- styles.searchContainer,
2137
+ fileManagementStyles.searchContainer,
2442
2138
  {
2443
- backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
2444
- borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
2139
+ backgroundColor: themeStyles.colors.card,
2445
2140
  }
2446
2141
  ]}>
2447
- <Ionicons name="search" size={22} color={themeStyles.isDarkTheme ? '#888888' : '#666666'} />
2142
+ <Ionicons name="search" size={22} color={themeStyles.colors.icon} />
2448
2143
  <TextInput
2449
- style={[styles.searchInput, { color: themeStyles.textColor }]}
2144
+ style={[fileManagementStyles.searchInput, { color: themeStyles.textColor }]}
2450
2145
  placeholder={viewMode === 'photos' ? 'Search photos...' : 'Search files...'}
2451
- placeholderTextColor={themeStyles.isDarkTheme ? '#888888' : '#999999'}
2146
+ placeholderTextColor={themeStyles.colors.secondaryText}
2452
2147
  value={searchQuery}
2453
2148
  onChangeText={setSearchQuery}
2454
2149
  />
2455
2150
  {searchQuery.length > 0 && (
2456
2151
  <TouchableOpacity
2457
2152
  onPress={() => setSearchQuery('')}
2458
- style={styles.searchClearButton}
2153
+ style={fileManagementStyles.searchClearButton}
2459
2154
  >
2460
- <Ionicons name="close-circle" size={22} color={themeStyles.isDarkTheme ? '#888888' : '#666666'} />
2155
+ <Ionicons name="close-circle" size={22} color={themeStyles.colors.icon} />
2461
2156
  </TouchableOpacity>
2462
2157
  )}
2463
2158
  </View>
@@ -2466,30 +2161,29 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2466
2161
  {/* File Stats */}
2467
2162
  {files.length > 0 && (
2468
2163
  <View style={[
2469
- styles.statsContainer,
2164
+ fileManagementStyles.statsContainer,
2470
2165
  {
2471
- backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
2472
- borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
2166
+ backgroundColor: themeStyles.colors.card,
2473
2167
  }
2474
2168
  ]}>
2475
- <View style={styles.statItem}>
2476
- <Text style={[styles.statValue, { color: themeStyles.textColor }]}>{filteredFiles.length}</Text>
2477
- <Text style={[styles.statLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2169
+ <View style={fileManagementStyles.statItem}>
2170
+ <Text style={[fileManagementStyles.statValue, { color: themeStyles.textColor }]}>{filteredFiles.length}</Text>
2171
+ <Text style={[fileManagementStyles.statLabel, { color: themeStyles.colors.secondaryText }]}>
2478
2172
  {searchQuery.length > 0 ? 'Found' : (filteredFiles.length === 1 ? (viewMode === 'photos' ? 'Photo' : 'File') : (viewMode === 'photos' ? 'Photos' : 'Files'))}
2479
2173
  </Text>
2480
2174
  </View>
2481
- <View style={styles.statItem}>
2482
- <Text style={[styles.statValue, { color: themeStyles.textColor }]}>
2175
+ <View style={fileManagementStyles.statItem}>
2176
+ <Text style={[fileManagementStyles.statValue, { color: themeStyles.textColor }]}>
2483
2177
  {formatFileSize(filteredFiles.reduce((total, file) => total + file.length, 0))}
2484
2178
  </Text>
2485
- <Text style={[styles.statLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2179
+ <Text style={[fileManagementStyles.statLabel, { color: themeStyles.colors.secondaryText }]}>
2486
2180
  {searchQuery.length > 0 ? 'Size' : 'Total Size'}
2487
2181
  </Text>
2488
2182
  </View>
2489
2183
  {searchQuery.length > 0 && (
2490
- <View style={styles.statItem}>
2491
- <Text style={[styles.statValue, { color: themeStyles.textColor }]}>{files.length}</Text>
2492
- <Text style={[styles.statLabel, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2184
+ <View style={fileManagementStyles.statItem}>
2185
+ <Text style={[fileManagementStyles.statValue, { color: themeStyles.textColor }]}>{files.length}</Text>
2186
+ <Text style={[fileManagementStyles.statLabel, { color: themeStyles.colors.secondaryText }]}>
2493
2187
  Total
2494
2188
  </Text>
2495
2189
  </View>
@@ -2503,8 +2197,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2503
2197
  ) : (
2504
2198
  <ScrollView
2505
2199
  ref={scrollViewRef}
2506
- style={styles.scrollView}
2507
- contentContainerStyle={styles.scrollContainer}
2200
+ style={fileManagementStyles.scrollView}
2201
+ contentContainerStyle={fileManagementStyles.scrollContainer}
2508
2202
  refreshControl={
2509
2203
  <RefreshControl
2510
2204
  refreshing={refreshing}
@@ -2522,27 +2216,27 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2522
2216
  scrollEventThrottle={250}
2523
2217
  >
2524
2218
  {filteredFiles.length === 0 && searchQuery.length > 0 ? (
2525
- <View style={styles.emptyState}>
2219
+ <View style={fileManagementStyles.emptyState}>
2526
2220
  <Ionicons name="search" size={64} color={themeStyles.isDarkTheme ? '#666666' : '#CCCCCC'} />
2527
- <Text style={[styles.emptyStateTitle, { color: themeStyles.textColor }]}>No Results Found</Text>
2528
- <Text style={[styles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2221
+ <Text style={[fileManagementStyles.emptyStateTitle, { color: themeStyles.textColor }]}>No Results Found</Text>
2222
+ <Text style={[fileManagementStyles.emptyStateDescription, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2529
2223
  No files match your search for "{searchQuery}"
2530
2224
  </Text>
2531
2225
  <TouchableOpacity
2532
- style={[styles.emptyStateButton, { backgroundColor: themeStyles.primaryColor }]}
2226
+ style={[fileManagementStyles.emptyStateButton, { backgroundColor: themeStyles.primaryColor }]}
2533
2227
  onPress={() => setSearchQuery('')}
2534
2228
  >
2535
2229
  <Ionicons name="refresh" size={20} color="#FFFFFF" />
2536
- <Text style={styles.emptyStateButtonText}>Clear Search</Text>
2230
+ <Text style={fileManagementStyles.emptyStateButtonText}>Clear Search</Text>
2537
2231
  </TouchableOpacity>
2538
2232
  </View>
2539
2233
  ) : filteredFiles.length === 0 ? renderEmptyState() : (
2540
2234
  <>
2541
- <GroupedSection items={groupedFileItems} theme={theme as 'light' | 'dark'} />
2235
+ <GroupedSection items={groupedFileItems} />
2542
2236
  {paging.loadingMore && (
2543
- <View style={styles.loadingMoreBar}>
2237
+ <View style={fileManagementStyles.loadingMoreBar}>
2544
2238
  <ActivityIndicator size="small" color={themeStyles.primaryColor} />
2545
- <Text style={[styles.loadingMoreText, { color: themeStyles.textColor }]}>Loading more...</Text>
2239
+ <Text style={[fileManagementStyles.loadingMoreText, { color: themeStyles.textColor }]}>Loading more...</Text>
2546
2240
  </View>
2547
2241
  )}
2548
2242
  </>
@@ -2550,1008 +2244,52 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
2550
2244
  </ScrollView>
2551
2245
  )}
2552
2246
 
2553
- {!selectMode && renderFileDetailsModal()}
2247
+ {!selectMode && (
2248
+ <FileDetailsModal
2249
+ visible={showFileDetails}
2250
+ file={selectedFile}
2251
+ onClose={() => setShowFileDetails(false)}
2252
+ onDownload={handleFileDownload}
2253
+ onDelete={handleFileDelete}
2254
+ themeStyles={themeStyles}
2255
+ isOwner={user?.id === targetUserId}
2256
+ />
2257
+ )}
2554
2258
 
2555
- {/* Uploading banner overlay */}
2259
+ {/* Uploading banner overlay with progress */}
2556
2260
  {!selectMode && uploading && (
2557
- <View style={[styles.uploadBannerContainer, { pointerEvents: 'none' }]}>
2558
- <View style={[styles.uploadBanner, { backgroundColor: themeStyles.isDarkTheme ? '#222831EE' : '#FFFFFFEE', borderColor: themeStyles.borderColor }]}>
2261
+ <View style={[fileManagementStyles.uploadBannerContainer, { pointerEvents: 'none' }]}>
2262
+ <View style={[fileManagementStyles.uploadBanner, { backgroundColor: themeStyles.isDarkTheme ? '#222831EE' : '#FFFFFFEE', borderColor: themeStyles.borderColor }]}>
2559
2263
  <Ionicons name="cloud-upload" size={18} color={themeStyles.primaryColor} />
2560
- <Text style={[styles.uploadBannerText, { color: themeStyles.textColor }]}>Uploading{uploadProgress ? ` ${uploadProgress.current}/${uploadProgress.total}` : '...'}</Text>
2561
- <View style={styles.uploadBannerDots}>
2562
- {[0, 1, 2].map(i => (
2563
- <View key={i} style={[styles.dot, { opacity: ((Date.now() / 400 + i) % 3) < 1 ? 1 : 0.25 }]} />
2564
- ))}
2264
+ <View style={fileManagementStyles.uploadBannerContent}>
2265
+ <Text style={[fileManagementStyles.uploadBannerText, { color: themeStyles.textColor }]}>
2266
+ Uploading{uploadProgress ? ` ${uploadProgress.current}/${uploadProgress.total}` : '...'}
2267
+ </Text>
2268
+ {uploadProgress && uploadProgress.total > 0 && (
2269
+ <View style={[fileManagementStyles.uploadProgressBarContainer, { backgroundColor: themeStyles.isDarkTheme ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)' }]}>
2270
+ <View
2271
+ style={[
2272
+ fileManagementStyles.uploadProgressBar,
2273
+ {
2274
+ width: `${(uploadProgress.current / uploadProgress.total) * 100}%`,
2275
+ backgroundColor: themeStyles.primaryColor
2276
+ }
2277
+ ]}
2278
+ />
2279
+ </View>
2280
+ )}
2565
2281
  </View>
2282
+ <ActivityIndicator size="small" color={themeStyles.primaryColor} />
2566
2283
  </View>
2567
2284
  </View>
2568
2285
  )}
2569
2286
 
2570
2287
  {/* Selection bar removed; actions are now in header */}
2571
2288
  {/* Global loadingMore bar removed; now inline in scroll areas */}
2572
-
2573
- {/* Drag and Drop Overlay - Enhanced */}
2574
- {isDragging && Platform.OS === 'web' && (
2575
- <View style={styles.dragDropOverlay}>
2576
- <View style={[styles.dragDropContent, {
2577
- backgroundColor: themeStyles.isDarkTheme ? 'rgba(30, 30, 30, 0.98)' : 'rgba(255, 255, 255, 0.98)',
2578
- borderColor: themeStyles.primaryColor,
2579
- }]}>
2580
- <View style={[styles.dragDropIconContainer, { backgroundColor: `${themeStyles.primaryColor}15` }]}>
2581
- <Ionicons name="cloud-upload" size={64} color={themeStyles.primaryColor} />
2582
- </View>
2583
- <Text style={[styles.dragDropTitle, { color: themeStyles.primaryColor }]}>
2584
- Drop files to upload
2585
- </Text>
2586
- <Text style={[styles.dragDropSubtitle, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }]}>
2587
- Release to upload multiple files
2588
- </Text>
2589
- </View>
2590
- </View>
2591
- )}
2592
2289
  </View>
2593
2290
  );
2594
2291
  };
2595
2292
 
2596
- const styles = StyleSheet.create({
2597
- container: {
2598
- flex: 1,
2599
- },
2600
- selectionBadge: {
2601
- position: 'absolute',
2602
- top: 4,
2603
- right: 4,
2604
- backgroundColor: 'rgba(0,0,0,0.4)',
2605
- borderRadius: 12,
2606
- padding: 2,
2607
- },
2608
- selectionBar: {
2609
- position: 'absolute',
2610
- left: 0,
2611
- right: 0,
2612
- bottom: 0,
2613
- flexDirection: 'row',
2614
- justifyContent: 'space-between',
2615
- padding: 12,
2616
- backgroundColor: 'rgba(0,0,0,0.55)',
2617
- gap: 12,
2618
- },
2619
- selectionBarButton: {
2620
- flex: 1,
2621
- paddingVertical: 14,
2622
- borderRadius: 10,
2623
- alignItems: 'center',
2624
- justifyContent: 'center',
2625
- },
2626
- selectionBarButtonText: {
2627
- color: '#FFFFFF',
2628
- fontSize: 15,
2629
- fontWeight: '600',
2630
- },
2631
- loadingMoreBar: {
2632
- flexDirection: 'row',
2633
- alignItems: 'center',
2634
- justifyContent: 'center',
2635
- paddingVertical: 12,
2636
- gap: 8,
2637
- },
2638
- loadingMoreText: {
2639
- fontSize: 13,
2640
- fontWeight: '500',
2641
- },
2642
- dragOverlay: {
2643
- backgroundColor: 'rgba(0, 122, 255, 0.06)',
2644
- borderWidth: 1,
2645
- borderColor: '#66AFFF',
2646
- borderStyle: 'dashed',
2647
- },
2648
- centerContent: {
2649
- justifyContent: 'center',
2650
- alignItems: 'center',
2651
- },
2652
- header: {
2653
- flexDirection: 'row',
2654
- alignItems: 'center',
2655
- justifyContent: 'space-between',
2656
- paddingHorizontal: 16,
2657
- paddingVertical: 12,
2658
- borderBottomWidth: 1,
2659
- position: 'relative',
2660
- },
2661
- backButton: {
2662
- padding: 12,
2663
- borderRadius: 12,
2664
- alignItems: 'center',
2665
- justifyContent: 'center',
2666
- minWidth: 44,
2667
- minHeight: 44,
2668
- },
2669
- headerTitleContainer: {
2670
- flex: 1,
2671
- alignItems: 'center',
2672
- justifyContent: 'center',
2673
- marginHorizontal: 16,
2674
- },
2675
- headerTitle: {
2676
- fontSize: 22,
2677
- fontWeight: '700',
2678
- fontFamily: fontFamilies.phuduBold,
2679
- letterSpacing: -0.5,
2680
- lineHeight: 28,
2681
- },
2682
- headerSubtitle: {
2683
- fontSize: 13,
2684
- fontWeight: '500',
2685
- fontFamily: fontFamilies.phuduMedium,
2686
- marginTop: 2,
2687
- letterSpacing: 0.2,
2688
- },
2689
- uploadButton: {
2690
- width: 44,
2691
- height: 44,
2692
- borderRadius: 22,
2693
- alignItems: 'center',
2694
- justifyContent: 'center',
2695
- },
2696
- uploadProgress: {
2697
- alignItems: 'center',
2698
- justifyContent: 'center',
2699
- },
2700
- uploadProgressText: {
2701
- color: '#FFFFFF',
2702
- fontSize: 10,
2703
- fontWeight: '600',
2704
- marginTop: 2,
2705
- },
2706
- uploadBannerContainer: {
2707
- position: 'absolute',
2708
- top: 72, // below header
2709
- left: 0,
2710
- right: 0,
2711
- alignItems: 'center',
2712
- zIndex: 50,
2713
- },
2714
- uploadBanner: {
2715
- flexDirection: 'row',
2716
- alignItems: 'center',
2717
- paddingHorizontal: 14,
2718
- paddingVertical: 8,
2719
- borderRadius: 24,
2720
- gap: 8,
2721
- borderWidth: 1,
2722
- shadowColor: '#000',
2723
- shadowOpacity: 0.1,
2724
- shadowRadius: 6,
2725
- shadowOffset: { width: 0, height: 2 },
2726
- elevation: 2,
2727
- },
2728
- uploadBannerText: {
2729
- fontSize: 13,
2730
- fontWeight: '500',
2731
- },
2732
- uploadBannerDots: {
2733
- flexDirection: 'row',
2734
- gap: 4,
2735
- marginLeft: 2,
2736
- },
2737
- dot: {
2738
- width: 6,
2739
- height: 6,
2740
- borderRadius: 3,
2741
- backgroundColor: '#007AFF',
2742
- },
2743
- searchContainer: {
2744
- flexDirection: 'row',
2745
- alignItems: 'center',
2746
- paddingHorizontal: 14,
2747
- paddingVertical: 10,
2748
- marginHorizontal: 16,
2749
- marginTop: 12,
2750
- borderRadius: 10,
2751
- borderWidth: 1,
2752
- gap: 10,
2753
- },
2754
- searchInput: {
2755
- flex: 1,
2756
- fontSize: 16,
2757
- fontFamily: fontFamilies.phudu,
2758
- lineHeight: 20,
2759
- },
2760
- searchClearButton: {
2761
- padding: 4,
2762
- borderRadius: 12,
2763
- alignItems: 'center',
2764
- justifyContent: 'center',
2765
- },
2766
- searchIcon: {
2767
- marginRight: 8,
2768
- },
2769
- statsContainer: {
2770
- flexDirection: 'row',
2771
- paddingHorizontal: 14,
2772
- paddingVertical: 10,
2773
- marginHorizontal: 16,
2774
- marginTop: 12,
2775
- borderRadius: 10,
2776
- borderWidth: 1,
2777
- },
2778
- statItem: {
2779
- flex: 1,
2780
- alignItems: 'center',
2781
- paddingVertical: 4,
2782
- },
2783
- statValue: {
2784
- fontSize: 20,
2785
- fontWeight: '800',
2786
- fontFamily: fontFamilies.phuduBold,
2787
- letterSpacing: -0.5,
2788
- lineHeight: 24,
2789
- },
2790
- statLabel: {
2791
- fontSize: 12,
2792
- fontWeight: '500',
2793
- fontFamily: fontFamilies.phuduMedium,
2794
- marginTop: 2,
2795
- letterSpacing: 0.2,
2796
- },
2797
- scrollView: {
2798
- flex: 1,
2799
- backgroundColor: '#e5f1ff',
2800
- },
2801
- scrollContainer: {
2802
- padding: 12,
2803
- },
2804
- fileItem: {
2805
- flexDirection: 'row',
2806
- alignItems: 'center',
2807
- padding: 10,
2808
- marginBottom: 8,
2809
- borderRadius: 10,
2810
- borderWidth: 1,
2811
- },
2812
- fileContent: {
2813
- flexDirection: 'row',
2814
- alignItems: 'center',
2815
- flex: 1,
2816
- },
2817
- fileIconContainer: {
2818
- width: 50,
2819
- height: 50,
2820
- alignItems: 'center',
2821
- justifyContent: 'center',
2822
- marginRight: 12,
2823
- },
2824
- filePreviewContainer: {
2825
- width: 52,
2826
- height: 52,
2827
- marginRight: 10,
2828
- },
2829
- filePreview: {
2830
- width: '100%',
2831
- height: '100%',
2832
- borderRadius: 8,
2833
- backgroundColor: 'transparent',
2834
- alignItems: 'center',
2835
- justifyContent: 'center',
2836
- overflow: 'hidden',
2837
- position: 'relative',
2838
- },
2839
- previewImage: {
2840
- width: '100%',
2841
- height: '100%',
2842
- borderRadius: 8,
2843
- },
2844
- pdfPreview: {
2845
- alignItems: 'center',
2846
- justifyContent: 'center',
2847
- width: '100%',
2848
- height: '100%',
2849
- backgroundColor: '#FF6B6B20',
2850
- },
2851
- pdfLabel: {
2852
- fontSize: 8,
2853
- fontWeight: 'bold',
2854
- marginTop: 2,
2855
- },
2856
- videoPreview: {
2857
- alignItems: 'center',
2858
- justifyContent: 'center',
2859
- width: '100%',
2860
- height: '100%',
2861
- backgroundColor: '#4ECDC420',
2862
- },
2863
- videoLabel: {
2864
- fontSize: 8,
2865
- fontWeight: 'bold',
2866
- marginTop: 2,
2867
- },
2868
- fallbackIcon: {
2869
- position: 'absolute',
2870
- top: 0,
2871
- left: 0,
2872
- right: 0,
2873
- bottom: 0,
2874
- alignItems: 'center',
2875
- justifyContent: 'center',
2876
- backgroundColor: 'transparent',
2877
- borderRadius: 8,
2878
- },
2879
- previewOverlay: {
2880
- position: 'absolute',
2881
- top: 0,
2882
- left: 0,
2883
- right: 0,
2884
- bottom: 0,
2885
- backgroundColor: 'rgba(0, 0, 0, 0.25)',
2886
- alignItems: 'center',
2887
- justifyContent: 'center',
2888
- borderRadius: 8,
2889
- },
2890
- groupedActions: {
2891
- flexDirection: 'row',
2892
- alignItems: 'center',
2893
- gap: 6,
2894
- marginLeft: 12,
2895
- },
2896
- groupedActionBtn: {
2897
- width: 34,
2898
- height: 34,
2899
- borderRadius: 8,
2900
- alignItems: 'center',
2901
- justifyContent: 'center',
2902
- },
2903
- groupedDescription: {
2904
- fontSize: 12,
2905
- lineHeight: 16,
2906
- marginTop: 6,
2907
- },
2908
- videoPreviewWrapper: {
2909
- width: '100%',
2910
- height: '100%',
2911
- borderRadius: 8,
2912
- overflow: 'hidden',
2913
- backgroundColor: '#000000',
2914
- alignItems: 'center',
2915
- justifyContent: 'center',
2916
- },
2917
- videoPosterImage: {
2918
- position: 'absolute',
2919
- top: 0,
2920
- left: 0,
2921
- right: 0,
2922
- bottom: 0,
2923
- width: '100%',
2924
- height: '100%',
2925
- },
2926
- videoOverlay: {
2927
- position: 'absolute',
2928
- top: 0,
2929
- left: 0,
2930
- right: 0,
2931
- bottom: 0,
2932
- alignItems: 'center',
2933
- justifyContent: 'center',
2934
- backgroundColor: 'rgba(0,0,0,0.25)',
2935
- },
2936
- fileInfo: {
2937
- flex: 1,
2938
- },
2939
- fileName: {
2940
- fontSize: 16,
2941
- fontWeight: '600',
2942
- marginBottom: 4,
2943
- },
2944
- fileDetails: {
2945
- fontSize: 14,
2946
- marginBottom: 2,
2947
- },
2948
- fileDescription: {
2949
- fontSize: 12,
2950
- fontStyle: 'italic',
2951
- },
2952
- fileActions: {
2953
- flexDirection: 'row',
2954
- gap: 8,
2955
- },
2956
- actionButton: {
2957
- width: 36,
2958
- height: 36,
2959
- borderRadius: 18,
2960
- alignItems: 'center',
2961
- justifyContent: 'center',
2962
- borderWidth: 1,
2963
- borderColor: '#E0E0E0',
2964
- backgroundColor: 'transparent',
2965
- },
2966
- emptyState: {
2967
- alignItems: 'center',
2968
- paddingVertical: 40,
2969
- paddingHorizontal: 24,
2970
- },
2971
- emptyStateTitle: {
2972
- fontSize: 24,
2973
- fontWeight: 'bold',
2974
- fontFamily: fontFamilies.phuduBold,
2975
- marginTop: 16,
2976
- marginBottom: 8,
2977
- },
2978
- emptyStateDescription: {
2979
- fontSize: 16,
2980
- textAlign: 'center',
2981
- lineHeight: 24,
2982
- marginBottom: 32,
2983
- },
2984
- emptyStateButton: {
2985
- flexDirection: 'row',
2986
- alignItems: 'center',
2987
- paddingHorizontal: 24,
2988
- paddingVertical: 12,
2989
- borderRadius: 24,
2990
- gap: 8,
2991
- },
2992
- emptyStateButtonText: {
2993
- color: '#FFFFFF',
2994
- fontSize: 16,
2995
- fontWeight: '600',
2996
- },
2997
- loadingText: {
2998
- fontSize: 16,
2999
- marginTop: 16,
3000
- },
3001
-
3002
- // Modal styles
3003
- modalContainer: {
3004
- flex: 1,
3005
- },
3006
- modalHeader: {
3007
- flexDirection: 'row',
3008
- alignItems: 'center',
3009
- justifyContent: 'space-between',
3010
- paddingHorizontal: 16,
3011
- paddingVertical: 12,
3012
- borderBottomWidth: 1,
3013
- },
3014
- modalCloseButton: {
3015
- padding: 8,
3016
- },
3017
- modalTitle: {
3018
- fontSize: 18,
3019
- fontWeight: '600',
3020
- fontFamily: fontFamilies.phuduSemiBold,
3021
- },
3022
- modalPlaceholder: {
3023
- width: 40,
3024
- },
3025
- modalContent: {
3026
- flex: 1,
3027
- padding: 16,
3028
- },
3029
- fileDetailCard: {
3030
- padding: 18,
3031
- borderRadius: 14,
3032
- borderWidth: 1,
3033
- alignItems: 'center',
3034
- },
3035
- fileDetailIcon: {
3036
- marginBottom: 16,
3037
- },
3038
- fileDetailName: {
3039
- fontSize: 20,
3040
- fontWeight: 'bold',
3041
- fontFamily: fontFamilies.phuduBold,
3042
- textAlign: 'center',
3043
- marginBottom: 24,
3044
- },
3045
- fileDetailInfo: {
3046
- width: '100%',
3047
- marginBottom: 32,
3048
- },
3049
- detailRow: {
3050
- flexDirection: 'row',
3051
- justifyContent: 'space-between',
3052
- alignItems: 'flex-start',
3053
- marginBottom: 12,
3054
- flexWrap: 'wrap',
3055
- },
3056
- detailLabel: {
3057
- fontSize: 16,
3058
- fontWeight: '500',
3059
- flex: 1,
3060
- minWidth: 100,
3061
- },
3062
- detailValue: {
3063
- fontSize: 16,
3064
- flex: 2,
3065
- textAlign: 'right',
3066
- },
3067
- modalActions: {
3068
- flexDirection: 'row',
3069
- gap: 12,
3070
- width: '100%',
3071
- },
3072
- modalActionButton: {
3073
- flex: 1,
3074
- flexDirection: 'row',
3075
- alignItems: 'center',
3076
- justifyContent: 'center',
3077
- paddingVertical: 16,
3078
- borderRadius: 12,
3079
- gap: 8,
3080
- },
3081
- modalActionText: {
3082
- color: '#FFFFFF',
3083
- fontSize: 16,
3084
- fontWeight: '600',
3085
- },
3086
-
3087
- // Drag and Drop styles - Enhanced
3088
- dragDropOverlay: {
3089
- position: 'absolute',
3090
- top: 0,
3091
- left: 0,
3092
- right: 0,
3093
- bottom: 0,
3094
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
3095
- justifyContent: 'center',
3096
- alignItems: 'center',
3097
- zIndex: 1000,
3098
- },
3099
- dragDropContent: {
3100
- alignItems: 'center',
3101
- padding: 32,
3102
- borderRadius: 20,
3103
- borderWidth: 3,
3104
- borderStyle: 'dashed',
3105
- minWidth: 280,
3106
- shadowColor: '#000',
3107
- shadowOpacity: 0.3,
3108
- shadowRadius: 20,
3109
- shadowOffset: { width: 0, height: 10 },
3110
- elevation: 10,
3111
- },
3112
- dragDropIconContainer: {
3113
- padding: 24,
3114
- borderRadius: 50,
3115
- marginBottom: 16,
3116
- },
3117
- dragDropTitle: {
3118
- fontSize: 24,
3119
- fontWeight: '700',
3120
- fontFamily: fontFamilies.phuduBold,
3121
- marginBottom: 8,
3122
- },
3123
- dragDropSubtitle: {
3124
- fontSize: 16,
3125
- fontWeight: '500',
3126
- fontFamily: fontFamilies.phuduMedium,
3127
- },
3128
- // Upload Preview Modal styles
3129
- uploadPreviewContainer: {
3130
- flex: 1,
3131
- },
3132
- uploadPreviewHeader: {
3133
- flexDirection: 'row',
3134
- alignItems: 'center',
3135
- justifyContent: 'space-between',
3136
- paddingHorizontal: 16,
3137
- paddingVertical: 16,
3138
- borderBottomWidth: 1,
3139
- },
3140
- uploadPreviewTitle: {
3141
- fontSize: 20,
3142
- fontWeight: '700',
3143
- fontFamily: fontFamilies.phuduBold,
3144
- },
3145
- uploadPreviewList: {
3146
- flex: 1,
3147
- padding: 16,
3148
- },
3149
- uploadPreviewItem: {
3150
- flexDirection: 'row',
3151
- alignItems: 'center',
3152
- padding: 12,
3153
- borderRadius: 12,
3154
- borderWidth: 1,
3155
- marginBottom: 12,
3156
- gap: 12,
3157
- },
3158
- uploadPreviewThumbnail: {
3159
- width: 60,
3160
- height: 60,
3161
- borderRadius: 8,
3162
- },
3163
- uploadPreviewIconContainer: {
3164
- width: 60,
3165
- height: 60,
3166
- borderRadius: 8,
3167
- alignItems: 'center',
3168
- justifyContent: 'center',
3169
- },
3170
- uploadPreviewInfo: {
3171
- flex: 1,
3172
- minWidth: 0,
3173
- },
3174
- uploadPreviewName: {
3175
- fontSize: 16,
3176
- fontWeight: '600',
3177
- fontFamily: fontFamilies.phuduSemiBold,
3178
- marginBottom: 4,
3179
- },
3180
- uploadPreviewMeta: {
3181
- fontSize: 13,
3182
- fontFamily: fontFamilies.phudu,
3183
- },
3184
- uploadPreviewRemove: {
3185
- padding: 4,
3186
- },
3187
- uploadPreviewFooter: {
3188
- padding: 16,
3189
- borderTopWidth: 1,
3190
- },
3191
- uploadPreviewStats: {
3192
- flexDirection: 'row',
3193
- justifyContent: 'space-between',
3194
- marginBottom: 16,
3195
- },
3196
- uploadPreviewStatsText: {
3197
- fontSize: 15,
3198
- fontWeight: '600',
3199
- fontFamily: fontFamilies.phuduSemiBold,
3200
- },
3201
- uploadPreviewActions: {
3202
- flexDirection: 'row',
3203
- gap: 12,
3204
- },
3205
- uploadPreviewCancelButton: {
3206
- flex: 1,
3207
- paddingVertical: 14,
3208
- borderRadius: 12,
3209
- borderWidth: 1,
3210
- alignItems: 'center',
3211
- justifyContent: 'center',
3212
- },
3213
- uploadPreviewCancelText: {
3214
- fontSize: 16,
3215
- fontWeight: '600',
3216
- fontFamily: fontFamilies.phuduSemiBold,
3217
- },
3218
- uploadPreviewConfirmButton: {
3219
- flex: 2,
3220
- flexDirection: 'row',
3221
- alignItems: 'center',
3222
- justifyContent: 'center',
3223
- paddingVertical: 14,
3224
- borderRadius: 12,
3225
- gap: 8,
3226
- },
3227
- uploadPreviewConfirmText: {
3228
- color: '#FFFFFF',
3229
- fontSize: 16,
3230
- fontWeight: '600',
3231
- fontFamily: fontFamilies.phuduSemiBold,
3232
- },
3233
-
3234
- // File Viewer styles
3235
- fileViewerContainer: {
3236
- flex: 1,
3237
- },
3238
- fileViewerHeader: {
3239
- flexDirection: 'row',
3240
- alignItems: 'center',
3241
- paddingHorizontal: 16,
3242
- paddingVertical: 12,
3243
- borderBottomWidth: 1,
3244
- },
3245
- fileViewerTitleContainer: {
3246
- flex: 1,
3247
- marginHorizontal: 16,
3248
- },
3249
- fileViewerTitle: {
3250
- fontSize: 18,
3251
- fontWeight: '600',
3252
- fontFamily: fontFamilies.phuduSemiBold,
3253
- marginBottom: 2,
3254
- },
3255
- fileViewerSubtitle: {
3256
- fontSize: 14,
3257
- },
3258
- fileViewerActions: {
3259
- flexDirection: 'row',
3260
- gap: 8,
3261
- },
3262
- fileViewerContent: {
3263
- flex: 1,
3264
- },
3265
- fileViewerContentWithDetails: {
3266
- paddingBottom: 20,
3267
- },
3268
- fileViewerContentContainer: {
3269
- flexGrow: 1,
3270
- padding: 14,
3271
- },
3272
- fileViewerLoading: {
3273
- flex: 1,
3274
- justifyContent: 'center',
3275
- alignItems: 'center',
3276
- },
3277
- fileViewerLoadingText: {
3278
- fontSize: 16,
3279
- marginTop: 16,
3280
- },
3281
- imageContainer: {
3282
- alignItems: 'center',
3283
- justifyContent: 'center',
3284
- flex: 1,
3285
- },
3286
- textContainer: {
3287
- flex: 1,
3288
- borderRadius: 10,
3289
- borderWidth: 1,
3290
- padding: 12,
3291
- minHeight: 180,
3292
- maxHeight: '80%',
3293
- },
3294
- textContent: {
3295
- fontSize: 14,
3296
- fontFamily: Platform.OS === 'web' ? 'monospace' : 'Courier',
3297
- lineHeight: 20,
3298
- },
3299
- unsupportedFileContainer: {
3300
- flex: 1,
3301
- justifyContent: 'center',
3302
- alignItems: 'center',
3303
- paddingVertical: 40,
3304
- paddingHorizontal: 24,
3305
- },
3306
- unsupportedFileTitle: {
3307
- fontSize: 24,
3308
- fontWeight: 'bold',
3309
- fontFamily: fontFamilies.phuduBold,
3310
- marginTop: 16,
3311
- marginBottom: 8,
3312
- textAlign: 'center',
3313
- },
3314
- unsupportedFileDescription: {
3315
- fontSize: 16,
3316
- textAlign: 'center',
3317
- lineHeight: 24,
3318
- marginBottom: 32,
3319
- },
3320
- downloadButtonLarge: {
3321
- flexDirection: 'row',
3322
- alignItems: 'center',
3323
- paddingHorizontal: 18,
3324
- paddingVertical: 12,
3325
- borderRadius: 20,
3326
- gap: 8,
3327
- },
3328
- downloadButtonText: {
3329
- color: '#FFFFFF',
3330
- fontSize: 16,
3331
- fontWeight: '600',
3332
- },
3333
- pdfContainer: {
3334
- flex: 1,
3335
- alignItems: 'center',
3336
- justifyContent: 'center',
3337
- },
3338
- mediaContainer: {
3339
- flex: 1,
3340
- alignItems: 'center',
3341
- justifyContent: 'center',
3342
- padding: 20,
3343
- },
3344
- unsupportedText: {
3345
- fontSize: 16,
3346
- textAlign: 'center',
3347
- fontStyle: 'italic',
3348
- },
3349
-
3350
- // File Details in Viewer styles
3351
- fileDetailsSection: {
3352
- margin: 12,
3353
- marginTop: 0,
3354
- padding: 14,
3355
- borderRadius: 10,
3356
- borderWidth: 1,
3357
- },
3358
- fileDetailsSectionTitle: {
3359
- fontSize: 18,
3360
- fontWeight: '600',
3361
- fontFamily: fontFamilies.phuduSemiBold,
3362
- flex: 1,
3363
- },
3364
- fileDetailsSectionHeader: {
3365
- flexDirection: 'row',
3366
- alignItems: 'center',
3367
- justifyContent: 'space-between',
3368
- marginBottom: 16,
3369
- },
3370
- fileDetailsSectionToggle: {
3371
- padding: 4,
3372
- },
3373
- fileDetailsActions: {
3374
- flexDirection: 'row',
3375
- gap: 12,
3376
- marginTop: 16,
3377
- },
3378
- fileDetailsActionButton: {
3379
- flex: 1,
3380
- flexDirection: 'row',
3381
- alignItems: 'center',
3382
- justifyContent: 'center',
3383
- paddingVertical: 12,
3384
- borderRadius: 8,
3385
- gap: 6,
3386
- },
3387
- fileDetailsActionText: {
3388
- color: '#FFFFFF',
3389
- fontSize: 14,
3390
- fontWeight: '600',
3391
- },
3392
-
3393
- // Header styles
3394
- headerActions: {
3395
- flexDirection: 'row',
3396
- alignItems: 'center',
3397
- gap: 16,
3398
- },
3399
- controlsBar: {
3400
- flexDirection: 'row',
3401
- alignItems: 'center',
3402
- justifyContent: 'space-between',
3403
- paddingHorizontal: 16,
3404
- paddingTop: 8,
3405
- paddingBottom: 4,
3406
- gap: 12,
3407
- },
3408
- viewModeScroll: {
3409
- flex: 1,
3410
- maxWidth: '80%',
3411
- },
3412
- viewModeToggle: {
3413
- flexDirection: 'row',
3414
- borderRadius: 24,
3415
- padding: 3,
3416
- overflow: 'hidden',
3417
- },
3418
- viewModeButton: {
3419
- paddingHorizontal: 14,
3420
- paddingVertical: 10,
3421
- borderRadius: 20,
3422
- minWidth: 44,
3423
- alignItems: 'center',
3424
- justifyContent: 'center',
3425
- marginHorizontal: 1,
3426
- },
3427
- sortButton: {
3428
- flexDirection: 'row',
3429
- alignItems: 'center',
3430
- justifyContent: 'center',
3431
- paddingHorizontal: 12,
3432
- paddingVertical: 10,
3433
- borderRadius: 20,
3434
- borderWidth: 1,
3435
- minWidth: 44,
3436
- },
3437
-
3438
- // Photo Grid styles
3439
- photoScrollContainer: {
3440
- padding: 10,
3441
- },
3442
- photoDateSection: {
3443
- marginBottom: 16,
3444
- },
3445
- photoDateHeader: {
3446
- fontSize: 16,
3447
- fontWeight: '600',
3448
- fontFamily: fontFamilies.phuduSemiBold,
3449
- marginBottom: 8,
3450
- paddingHorizontal: 2,
3451
- },
3452
- photoGrid: {
3453
- flexDirection: 'row',
3454
- flexWrap: 'wrap',
3455
- gap: 4,
3456
- justifyContent: 'flex-start',
3457
- },
3458
- photoItem: {
3459
- borderRadius: 8,
3460
- overflow: 'hidden',
3461
- },
3462
- photoContainer: {
3463
- width: '100%',
3464
- height: '100%',
3465
- position: 'relative',
3466
- borderRadius: 8,
3467
- overflow: 'hidden',
3468
- },
3469
- photoImage: {
3470
- width: '100%',
3471
- height: '100%',
3472
- },
3473
-
3474
- // Justified Grid styles
3475
- dimensionsLoadingIndicator: {
3476
- flexDirection: 'row',
3477
- alignItems: 'center',
3478
- justifyContent: 'center',
3479
- paddingVertical: 16,
3480
- gap: 8,
3481
- },
3482
- dimensionsLoadingText: {
3483
- fontSize: 14,
3484
- fontStyle: 'italic',
3485
- },
3486
- justifiedPhotoGrid: {
3487
- gap: 4,
3488
- },
3489
- justifiedPhotoRow: {
3490
- flexDirection: 'row',
3491
- },
3492
- justifiedPhotoItem: {
3493
- borderRadius: 6,
3494
- overflow: 'hidden',
3495
- position: 'relative',
3496
- },
3497
- justifiedPhotoContainer: {
3498
- width: '100%',
3499
- height: '100%',
3500
- position: 'relative',
3501
- borderRadius: 6,
3502
- overflow: 'hidden',
3503
- backgroundColor: 'transparent',
3504
- },
3505
- justifiedPhotoImage: {
3506
- width: '100%',
3507
- height: '100%',
3508
- borderRadius: 6,
3509
- },
3510
-
3511
- // Simple Photo Grid styles
3512
- simplePhotoItem: {
3513
- borderRadius: 8,
3514
- overflow: 'hidden',
3515
- backgroundColor: 'transparent',
3516
- },
3517
- simplePhotoContainer: {
3518
- width: '100%',
3519
- height: '100%',
3520
- position: 'relative',
3521
- borderRadius: 8,
3522
- overflow: 'hidden',
3523
- },
3524
- simplePhotoImage: {
3525
- width: '100%',
3526
- height: '100%',
3527
- borderRadius: 8,
3528
- },
3529
-
3530
- // Loading skeleton styles
3531
- photoSkeletonGrid: {
3532
- flexDirection: 'row',
3533
- flexWrap: 'wrap',
3534
- gap: 4,
3535
- marginTop: 20,
3536
- },
3537
- photoSkeletonItem: {
3538
- width: '32%',
3539
- aspectRatio: 1,
3540
- borderRadius: 8,
3541
- marginBottom: 4,
3542
- },
3543
- skeletonFileItem: {
3544
- flexDirection: 'row',
3545
- alignItems: 'center',
3546
- paddingHorizontal: 16,
3547
- paddingVertical: 12,
3548
- borderBottomWidth: 1,
3549
- gap: 12,
3550
- },
3551
- skeletonFileInfo: {
3552
- flex: 1,
3553
- justifyContent: 'center',
3554
- },
3555
- });
2293
+ // Styles have been moved to components/fileManagement/styles.ts
3556
2294
 
3557
2295
  export default FileManagementScreen;