@oxyhq/services 5.10.16 → 5.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (460) hide show
  1. package/README.md +78 -14
  2. package/lib/commonjs/assets/assets/icons/OxyServices.tsx +2 -2
  3. package/lib/commonjs/assets/assets/illustrations/HighFive.tsx +1 -1
  4. package/lib/commonjs/assets/icons/OxyServices.js +0 -2
  5. package/lib/commonjs/assets/icons/OxyServices.js.map +1 -1
  6. package/lib/commonjs/assets/illustrations/HighFive.js +0 -2
  7. package/lib/commonjs/assets/illustrations/HighFive.js.map +1 -1
  8. package/lib/commonjs/core/OxyServices.js +238 -25
  9. package/lib/commonjs/core/OxyServices.js.map +1 -1
  10. package/lib/commonjs/index.js +86 -0
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/commonjs/lib/sonner-safe.js +2 -0
  13. package/lib/commonjs/lib/sonner-safe.js.map +1 -1
  14. package/lib/commonjs/lib/sonner.js +2 -0
  15. package/lib/commonjs/lib/sonner.js.map +1 -1
  16. package/lib/commonjs/ui/components/Avatar.js +0 -2
  17. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  18. package/lib/commonjs/ui/components/FollowButton.js +4 -4
  19. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  20. package/lib/commonjs/ui/components/FontLoader.js +3 -2
  21. package/lib/commonjs/ui/components/FontLoader.js.map +1 -1
  22. package/lib/commonjs/ui/components/GroupedItem.js +7 -4
  23. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  24. package/lib/commonjs/ui/components/GroupedSection.js +1 -1
  25. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
  26. package/lib/commonjs/ui/components/Header.js +0 -1
  27. package/lib/commonjs/ui/components/Header.js.map +1 -1
  28. package/lib/commonjs/ui/components/OxyLogo.js +0 -2
  29. package/lib/commonjs/ui/components/OxyLogo.js.map +1 -1
  30. package/lib/commonjs/ui/components/OxyPayButton.js +4 -5
  31. package/lib/commonjs/ui/components/OxyPayButton.js.map +1 -1
  32. package/lib/commonjs/ui/components/OxyProvider.js +4 -3
  33. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  34. package/lib/commonjs/ui/components/OxySignInButton.js +0 -1
  35. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  36. package/lib/commonjs/ui/components/ProfileCard.js +0 -1
  37. package/lib/commonjs/ui/components/ProfileCard.js.map +1 -1
  38. package/lib/commonjs/ui/components/QuickActions.js +0 -2
  39. package/lib/commonjs/ui/components/QuickActions.js.map +1 -1
  40. package/lib/commonjs/ui/components/Section.js +0 -1
  41. package/lib/commonjs/ui/components/Section.js.map +1 -1
  42. package/lib/commonjs/ui/components/SectionTitle.js +0 -2
  43. package/lib/commonjs/ui/components/SectionTitle.js.map +1 -1
  44. package/lib/commonjs/ui/components/icon/FAIRWalletIcon.js +0 -2
  45. package/lib/commonjs/ui/components/icon/FAIRWalletIcon.js.map +1 -1
  46. package/lib/commonjs/ui/components/icon/OxyIcon.js +0 -2
  47. package/lib/commonjs/ui/components/icon/OxyIcon.js.map +1 -1
  48. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +0 -2
  49. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
  50. package/lib/commonjs/ui/components/internal/PinInput.js +4 -3
  51. package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
  52. package/lib/commonjs/ui/components/internal/TextField.js +3 -3
  53. package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
  54. package/lib/commonjs/ui/context/OxyContext.js +3 -1
  55. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  56. package/lib/commonjs/ui/hooks/useAssets.js +263 -0
  57. package/lib/commonjs/ui/hooks/useAssets.js.map +1 -0
  58. package/lib/commonjs/ui/navigation/OxyRouter.js +1 -2
  59. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  60. package/lib/commonjs/ui/screens/AccountCenterScreen.js +0 -2
  61. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  62. package/lib/commonjs/ui/screens/AccountManagementDemo.js +0 -1
  63. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
  64. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +2 -2
  65. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  66. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +5 -6
  67. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  68. package/lib/commonjs/ui/screens/AppInfoScreen.js +1 -2
  69. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  70. package/lib/commonjs/ui/screens/FileManagementScreen.js +631 -430
  71. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  72. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +2 -2
  73. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  74. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +1 -2
  75. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  76. package/lib/commonjs/ui/screens/ProfileScreen.js +1 -2
  77. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  78. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +1 -2
  79. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
  80. package/lib/commonjs/ui/screens/SessionManagementScreen.js +1 -2
  81. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  82. package/lib/commonjs/ui/screens/SignInScreen.js +1 -2
  83. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  84. package/lib/commonjs/ui/screens/UserLinksScreen.js +0 -2
  85. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -1
  86. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +1 -2
  87. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  88. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +1 -2
  89. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  90. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +1 -2
  91. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  92. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +1 -2
  93. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  94. package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js +0 -1
  95. package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js.map +1 -1
  96. package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js +0 -1
  97. package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js.map +1 -1
  98. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js +0 -2
  99. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  100. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +1 -2
  101. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  102. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +1 -2
  103. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  104. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +0 -2
  105. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  106. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -2
  107. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  108. package/lib/commonjs/ui/stores/assetStore.js +225 -0
  109. package/lib/commonjs/ui/stores/assetStore.js.map +1 -0
  110. package/lib/commonjs/ui/stores/authStore.js +1 -1
  111. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  112. package/lib/commonjs/ui/stores/fileStore.js +153 -0
  113. package/lib/commonjs/ui/stores/fileStore.js.map +1 -0
  114. package/lib/commonjs/ui/stores/followStore.js +2 -2
  115. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  116. package/lib/commonjs/utils/asyncUtils.js +1 -1
  117. package/lib/commonjs/utils/asyncUtils.js.map +1 -1
  118. package/lib/commonjs/utils/errorUtils.js +19 -11
  119. package/lib/commonjs/utils/errorUtils.js.map +1 -1
  120. package/lib/commonjs/utils/hookUtils.js +1 -1
  121. package/lib/commonjs/utils/hookUtils.js.map +1 -1
  122. package/lib/commonjs/utils/loggerUtils.js.map +1 -1
  123. package/lib/commonjs/utils/validationUtils.js +2 -2
  124. package/lib/commonjs/utils/validationUtils.js.map +1 -1
  125. package/lib/module/assets/assets/icons/OxyServices.tsx +2 -2
  126. package/lib/module/assets/assets/illustrations/HighFive.tsx +1 -1
  127. package/lib/module/assets/icons/OxyServices.js +0 -1
  128. package/lib/module/assets/icons/OxyServices.js.map +1 -1
  129. package/lib/module/assets/illustrations/HighFive.js +0 -1
  130. package/lib/module/assets/illustrations/HighFive.js.map +1 -1
  131. package/lib/module/core/OxyServices.js +238 -25
  132. package/lib/module/core/OxyServices.js.map +1 -1
  133. package/lib/module/index.js +2 -0
  134. package/lib/module/index.js.map +1 -1
  135. package/lib/module/lib/sonner-safe.js +2 -0
  136. package/lib/module/lib/sonner-safe.js.map +1 -1
  137. package/lib/module/lib/sonner.js +3 -0
  138. package/lib/module/lib/sonner.js.map +1 -1
  139. package/lib/module/ui/components/Avatar.js +0 -1
  140. package/lib/module/ui/components/Avatar.js.map +1 -1
  141. package/lib/module/ui/components/FollowButton.js +4 -3
  142. package/lib/module/ui/components/FollowButton.js.map +1 -1
  143. package/lib/module/ui/components/FontLoader.js +3 -2
  144. package/lib/module/ui/components/FontLoader.js.map +1 -1
  145. package/lib/module/ui/components/GroupedItem.js +7 -3
  146. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  147. package/lib/module/ui/components/GroupedSection.js +1 -1
  148. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  149. package/lib/module/ui/components/Header.js +0 -1
  150. package/lib/module/ui/components/Header.js.map +1 -1
  151. package/lib/module/ui/components/OxyLogo.js +0 -1
  152. package/lib/module/ui/components/OxyLogo.js.map +1 -1
  153. package/lib/module/ui/components/OxyPayButton.js +4 -4
  154. package/lib/module/ui/components/OxyPayButton.js.map +1 -1
  155. package/lib/module/ui/components/OxyProvider.js +4 -2
  156. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  157. package/lib/module/ui/components/OxySignInButton.js +0 -1
  158. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  159. package/lib/module/ui/components/ProfileCard.js +0 -1
  160. package/lib/module/ui/components/ProfileCard.js.map +1 -1
  161. package/lib/module/ui/components/QuickActions.js +0 -1
  162. package/lib/module/ui/components/QuickActions.js.map +1 -1
  163. package/lib/module/ui/components/Section.js +0 -1
  164. package/lib/module/ui/components/Section.js.map +1 -1
  165. package/lib/module/ui/components/SectionTitle.js +0 -1
  166. package/lib/module/ui/components/SectionTitle.js.map +1 -1
  167. package/lib/module/ui/components/icon/FAIRWalletIcon.js +0 -1
  168. package/lib/module/ui/components/icon/FAIRWalletIcon.js.map +1 -1
  169. package/lib/module/ui/components/icon/OxyIcon.js +0 -1
  170. package/lib/module/ui/components/icon/OxyIcon.js.map +1 -1
  171. package/lib/module/ui/components/internal/GroupedPillButtons.js +0 -1
  172. package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
  173. package/lib/module/ui/components/internal/PinInput.js +4 -2
  174. package/lib/module/ui/components/internal/PinInput.js.map +1 -1
  175. package/lib/module/ui/components/internal/TextField.js +3 -3
  176. package/lib/module/ui/components/internal/TextField.js.map +1 -1
  177. package/lib/module/ui/context/OxyContext.js +3 -1
  178. package/lib/module/ui/context/OxyContext.js.map +1 -1
  179. package/lib/module/ui/hooks/useAssets.js +257 -0
  180. package/lib/module/ui/hooks/useAssets.js.map +1 -0
  181. package/lib/module/ui/navigation/OxyRouter.js +1 -1
  182. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  183. package/lib/module/ui/screens/AccountCenterScreen.js +0 -1
  184. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  185. package/lib/module/ui/screens/AccountManagementDemo.js +0 -1
  186. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
  187. package/lib/module/ui/screens/AccountSettingsScreen.js +2 -2
  188. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  189. package/lib/module/ui/screens/AccountSwitcherScreen.js +5 -5
  190. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  191. package/lib/module/ui/screens/AppInfoScreen.js +1 -1
  192. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  193. package/lib/module/ui/screens/FileManagementScreen.js +633 -432
  194. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  195. package/lib/module/ui/screens/PaymentGatewayScreen.js +1 -1
  196. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  197. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +1 -1
  198. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  199. package/lib/module/ui/screens/ProfileScreen.js +1 -1
  200. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  201. package/lib/module/ui/screens/RecoverAccountScreen.js +1 -1
  202. package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
  203. package/lib/module/ui/screens/SessionManagementScreen.js +1 -1
  204. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  205. package/lib/module/ui/screens/SignInScreen.js +1 -1
  206. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  207. package/lib/module/ui/screens/UserLinksScreen.js +0 -1
  208. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -1
  209. package/lib/module/ui/screens/internal/SignInPasswordStep.js +1 -1
  210. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  211. package/lib/module/ui/screens/internal/SignInUsernameStep.js +1 -1
  212. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  213. package/lib/module/ui/screens/internal/SignUpIdentityStep.js +1 -1
  214. package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  215. package/lib/module/ui/screens/internal/SignUpSecurityStep.js +1 -1
  216. package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  217. package/lib/module/ui/screens/internal/SignUpSummaryStep.js +0 -1
  218. package/lib/module/ui/screens/internal/SignUpSummaryStep.js.map +1 -1
  219. package/lib/module/ui/screens/internal/SignUpWelcomeStep.js +0 -1
  220. package/lib/module/ui/screens/internal/SignUpWelcomeStep.js.map +1 -1
  221. package/lib/module/ui/screens/karma/KarmaAboutScreen.js +0 -1
  222. package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  223. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +1 -1
  224. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  225. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  226. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  227. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +0 -1
  228. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  229. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
  230. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  231. package/lib/module/ui/stores/assetStore.js +212 -0
  232. package/lib/module/ui/stores/assetStore.js.map +1 -0
  233. package/lib/module/ui/stores/authStore.js +1 -1
  234. package/lib/module/ui/stores/authStore.js.map +1 -1
  235. package/lib/module/ui/stores/fileStore.js +145 -0
  236. package/lib/module/ui/stores/fileStore.js.map +1 -0
  237. package/lib/module/ui/stores/followStore.js +2 -2
  238. package/lib/module/ui/stores/followStore.js.map +1 -1
  239. package/lib/module/ui/styles/fonts.js.map +1 -1
  240. package/lib/module/ui/styles/theme.js.map +1 -1
  241. package/lib/module/utils/asyncUtils.js +1 -1
  242. package/lib/module/utils/asyncUtils.js.map +1 -1
  243. package/lib/module/utils/errorUtils.js +19 -11
  244. package/lib/module/utils/errorUtils.js.map +1 -1
  245. package/lib/module/utils/hookUtils.js +1 -1
  246. package/lib/module/utils/hookUtils.js.map +1 -1
  247. package/lib/module/utils/loggerUtils.js.map +1 -1
  248. package/lib/module/utils/validationUtils.js +2 -2
  249. package/lib/module/utils/validationUtils.js.map +1 -1
  250. package/lib/typescript/assets/icons/OxyServices.d.ts +2 -2
  251. package/lib/typescript/assets/icons/OxyServices.d.ts.map +1 -1
  252. package/lib/typescript/assets/illustrations/HighFive.d.ts +1 -1
  253. package/lib/typescript/assets/illustrations/HighFive.d.ts.map +1 -1
  254. package/lib/typescript/core/OxyServices.d.ts +68 -12
  255. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  256. package/lib/typescript/index.d.ts +3 -1
  257. package/lib/typescript/index.d.ts.map +1 -1
  258. package/lib/typescript/lib/sonner-safe.d.ts +2 -1
  259. package/lib/typescript/lib/sonner-safe.d.ts.map +1 -1
  260. package/lib/typescript/lib/sonner.d.ts +11 -3
  261. package/lib/typescript/lib/sonner.d.ts.map +1 -1
  262. package/lib/typescript/models/interfaces.d.ts +105 -6
  263. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  264. package/lib/typescript/types/expo-vector-icons.d.ts +8 -1
  265. package/lib/typescript/types/express.d.ts +22 -3
  266. package/lib/typescript/ui/components/Avatar.d.ts +2 -2
  267. package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
  268. package/lib/typescript/ui/components/FollowButton.d.ts +2 -2
  269. package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
  270. package/lib/typescript/ui/components/FontLoader.d.ts +1 -1
  271. package/lib/typescript/ui/components/FontLoader.d.ts.map +1 -1
  272. package/lib/typescript/ui/components/GroupedItem.d.ts +2 -1
  273. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  274. package/lib/typescript/ui/components/GroupedSection.d.ts +2 -1
  275. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -1
  276. package/lib/typescript/ui/components/Header.d.ts +1 -1
  277. package/lib/typescript/ui/components/Header.d.ts.map +1 -1
  278. package/lib/typescript/ui/components/OxyLogo.d.ts +2 -2
  279. package/lib/typescript/ui/components/OxyLogo.d.ts.map +1 -1
  280. package/lib/typescript/ui/components/OxyPayButton.d.ts +2 -2
  281. package/lib/typescript/ui/components/OxyPayButton.d.ts.map +1 -1
  282. package/lib/typescript/ui/components/OxyProvider.d.ts +2 -2
  283. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  284. package/lib/typescript/ui/components/OxySignInButton.d.ts +2 -2
  285. package/lib/typescript/ui/components/OxySignInButton.d.ts.map +1 -1
  286. package/lib/typescript/ui/components/ProfileCard.d.ts +1 -1
  287. package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -1
  288. package/lib/typescript/ui/components/QuickActions.d.ts +1 -1
  289. package/lib/typescript/ui/components/QuickActions.d.ts.map +1 -1
  290. package/lib/typescript/ui/components/Section.d.ts +1 -1
  291. package/lib/typescript/ui/components/Section.d.ts.map +1 -1
  292. package/lib/typescript/ui/components/SectionTitle.d.ts +1 -1
  293. package/lib/typescript/ui/components/SectionTitle.d.ts.map +1 -1
  294. package/lib/typescript/ui/components/icon/FAIRWalletIcon.d.ts +1 -1
  295. package/lib/typescript/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -1
  296. package/lib/typescript/ui/components/icon/OxyIcon.d.ts +1 -1
  297. package/lib/typescript/ui/components/icon/OxyIcon.d.ts.map +1 -1
  298. package/lib/typescript/ui/components/internal/GroupedPillButtons.d.ts +1 -1
  299. package/lib/typescript/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
  300. package/lib/typescript/ui/components/internal/PinInput.d.ts +1 -1
  301. package/lib/typescript/ui/components/internal/PinInput.d.ts.map +1 -1
  302. package/lib/typescript/ui/components/internal/TextField.d.ts +1 -1
  303. package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
  304. package/lib/typescript/ui/context/OxyContext.d.ts +8 -3
  305. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  306. package/lib/typescript/ui/hooks/useAssets.d.ts +35 -0
  307. package/lib/typescript/ui/hooks/useAssets.d.ts.map +1 -0
  308. package/lib/typescript/ui/navigation/OxyRouter.d.ts +2 -2
  309. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
  310. package/lib/typescript/ui/navigation/types.d.ts +7 -7
  311. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  312. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts +2 -2
  313. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  314. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts +1 -1
  315. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts.map +1 -1
  316. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts +1 -1
  317. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  318. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +1 -1
  319. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  320. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts +2 -2
  321. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  322. package/lib/typescript/ui/screens/AppInfoScreen.d.ts +2 -2
  323. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  324. package/lib/typescript/ui/screens/FeedbackScreen.d.ts +1 -1
  325. package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
  326. package/lib/typescript/ui/screens/FileManagementScreen.d.ts +1 -1
  327. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
  328. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts +2 -2
  329. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  330. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts +2 -2
  331. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  332. package/lib/typescript/ui/screens/ProfileScreen.d.ts +2 -2
  333. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  334. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +1 -1
  335. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
  336. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts +2 -2
  337. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  338. package/lib/typescript/ui/screens/SignInScreen.d.ts +2 -2
  339. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  340. package/lib/typescript/ui/screens/SignUpScreen.d.ts +1 -1
  341. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
  342. package/lib/typescript/ui/screens/UserLinksScreen.d.ts +2 -2
  343. package/lib/typescript/ui/screens/UserLinksScreen.d.ts.map +1 -1
  344. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +1 -1
  345. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
  346. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +1 -1
  347. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -1
  348. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts +1 -1
  349. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +1 -1
  350. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts +1 -1
  351. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +1 -1
  352. package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts +1 -1
  353. package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts.map +1 -1
  354. package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts +1 -1
  355. package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts.map +1 -1
  356. package/lib/typescript/ui/screens/karma/KarmaAboutScreen.d.ts +2 -2
  357. package/lib/typescript/ui/screens/karma/KarmaAboutScreen.d.ts.map +1 -1
  358. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts +2 -2
  359. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
  360. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts +1 -1
  361. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts.map +1 -1
  362. package/lib/typescript/ui/screens/karma/KarmaLeaderboardScreen.d.ts +2 -2
  363. package/lib/typescript/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +1 -1
  364. package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts +2 -2
  365. package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
  366. package/lib/typescript/ui/screens/karma/KarmaRulesScreen.d.ts +2 -2
  367. package/lib/typescript/ui/screens/karma/KarmaRulesScreen.d.ts.map +1 -1
  368. package/lib/typescript/ui/stores/assetStore.d.ts +54 -0
  369. package/lib/typescript/ui/stores/assetStore.d.ts.map +1 -0
  370. package/lib/typescript/ui/stores/fileStore.d.ts +31 -0
  371. package/lib/typescript/ui/stores/fileStore.d.ts.map +1 -0
  372. package/lib/typescript/ui/stores/followStore.d.ts +1 -1
  373. package/lib/typescript/ui/stores/followStore.d.ts.map +1 -1
  374. package/lib/typescript/ui/styles/fonts.d.ts +1 -1
  375. package/lib/typescript/ui/styles/fonts.d.ts.map +1 -1
  376. package/lib/typescript/ui/styles/theme.d.ts +1 -1
  377. package/lib/typescript/ui/styles/theme.d.ts.map +1 -1
  378. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
  379. package/lib/typescript/utils/errorUtils.d.ts +5 -5
  380. package/lib/typescript/utils/errorUtils.d.ts.map +1 -1
  381. package/lib/typescript/utils/hookUtils.d.ts +4 -4
  382. package/lib/typescript/utils/hookUtils.d.ts.map +1 -1
  383. package/lib/typescript/utils/loggerUtils.d.ts +18 -18
  384. package/lib/typescript/utils/loggerUtils.d.ts.map +1 -1
  385. package/lib/typescript/utils/validationUtils.d.ts +6 -6
  386. package/lib/typescript/utils/validationUtils.d.ts.map +1 -1
  387. package/package.json +149 -143
  388. package/src/assets/icons/OxyServices.tsx +2 -2
  389. package/src/assets/illustrations/HighFive.tsx +1 -1
  390. package/src/core/OxyServices.ts +264 -40
  391. package/src/core/__tests__/OxyServices.test.ts +180 -0
  392. package/src/index.ts +15 -1
  393. package/src/lib/sonner-safe.ts +4 -1
  394. package/src/lib/sonner.ts +19 -2
  395. package/src/models/interfaces.ts +117 -6
  396. package/src/types/expo-vector-icons.d.ts +8 -1
  397. package/src/types/express.d.ts +22 -3
  398. package/src/ui/components/Avatar.tsx +2 -2
  399. package/src/ui/components/FollowButton.tsx +10 -8
  400. package/src/ui/components/FontLoader.tsx +5 -3
  401. package/src/ui/components/GroupedItem.tsx +12 -2
  402. package/src/ui/components/GroupedSection.tsx +3 -1
  403. package/src/ui/components/Header.tsx +1 -1
  404. package/src/ui/components/OxyLogo.tsx +2 -2
  405. package/src/ui/components/OxyPayButton.tsx +6 -5
  406. package/src/ui/components/OxyProvider.tsx +7 -4
  407. package/src/ui/components/OxySignInButton.tsx +2 -2
  408. package/src/ui/components/ProfileCard.tsx +1 -1
  409. package/src/ui/components/QuickActions.tsx +1 -1
  410. package/src/ui/components/Section.tsx +1 -1
  411. package/src/ui/components/SectionTitle.tsx +1 -1
  412. package/src/ui/components/icon/FAIRWalletIcon.tsx +1 -1
  413. package/src/ui/components/icon/OxyIcon.tsx +1 -1
  414. package/src/ui/components/internal/GroupedPillButtons.tsx +1 -1
  415. package/src/ui/components/internal/PinInput.tsx +3 -2
  416. package/src/ui/components/internal/TextField.tsx +9 -11
  417. package/src/ui/context/OxyContext.tsx +11 -3
  418. package/src/ui/hooks/useAssets.ts +306 -0
  419. package/src/ui/navigation/OxyRouter.tsx +3 -2
  420. package/src/ui/navigation/types.ts +8 -8
  421. package/src/ui/screens/AccountCenterScreen.tsx +2 -2
  422. package/src/ui/screens/AccountManagementDemo.tsx +1 -1
  423. package/src/ui/screens/AccountOverviewScreen.tsx +1 -1
  424. package/src/ui/screens/AccountSettingsScreen.tsx +3 -3
  425. package/src/ui/screens/AccountSwitcherScreen.tsx +9 -8
  426. package/src/ui/screens/AppInfoScreen.tsx +3 -2
  427. package/src/ui/screens/FeedbackScreen.tsx +1 -1
  428. package/src/ui/screens/FileManagementScreen.tsx +619 -494
  429. package/src/ui/screens/PaymentGatewayScreen.tsx +3 -2
  430. package/src/ui/screens/PremiumSubscriptionScreen.tsx +3 -2
  431. package/src/ui/screens/ProfileScreen.tsx +3 -2
  432. package/src/ui/screens/RecoverAccountScreen.tsx +3 -2
  433. package/src/ui/screens/SessionManagementScreen.tsx +4 -3
  434. package/src/ui/screens/SignInScreen.tsx +3 -2
  435. package/src/ui/screens/SignUpScreen.tsx +1 -1
  436. package/src/ui/screens/UserLinksScreen.tsx +2 -2
  437. package/src/ui/screens/internal/SignInPasswordStep.tsx +3 -2
  438. package/src/ui/screens/internal/SignInUsernameStep.tsx +3 -2
  439. package/src/ui/screens/internal/SignUpIdentityStep.tsx +3 -2
  440. package/src/ui/screens/internal/SignUpSecurityStep.tsx +3 -2
  441. package/src/ui/screens/internal/SignUpSummaryStep.tsx +1 -1
  442. package/src/ui/screens/internal/SignUpWelcomeStep.tsx +1 -1
  443. package/src/ui/screens/karma/KarmaAboutScreen.tsx +2 -2
  444. package/src/ui/screens/karma/KarmaCenterScreen.tsx +3 -2
  445. package/src/ui/screens/karma/KarmaFAQScreen.tsx +1 -1
  446. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +3 -2
  447. package/src/ui/screens/karma/KarmaRewardsScreen.tsx +2 -2
  448. package/src/ui/screens/karma/KarmaRulesScreen.tsx +3 -2
  449. package/src/ui/stores/assetStore.ts +281 -0
  450. package/src/ui/stores/authStore.ts +1 -1
  451. package/src/ui/stores/fileStore.ts +118 -0
  452. package/src/ui/stores/followStore.ts +4 -4
  453. package/src/ui/styles/fonts.ts +1 -1
  454. package/src/ui/styles/theme.ts +1 -1
  455. package/src/utils/__tests__/validationUtils.test.ts +236 -0
  456. package/src/utils/asyncUtils.ts +4 -4
  457. package/src/utils/errorUtils.ts +35 -23
  458. package/src/utils/hookUtils.ts +7 -7
  459. package/src/utils/loggerUtils.ts +18 -18
  460. package/src/utils/validationUtils.ts +8 -8
@@ -1,30 +1,20 @@
1
1
  "use strict";
2
2
 
3
- import React, { useState, useEffect, useCallback, useMemo } from 'react';
4
- import { View, Text, TouchableOpacity, StyleSheet, ScrollView, ActivityIndicator, Platform, RefreshControl, Modal, TextInput, Image } from 'react-native';
3
+ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
4
+ import { View, Text, TouchableOpacity, StyleSheet, ScrollView, ActivityIndicator, Platform, RefreshControl, Modal, TextInput, Image // kept for Image.getSize only
5
+ } from 'react-native';
6
+ import { Image as ExpoImage } from 'expo-image';
5
7
  import { useOxy } from '../context/OxyContext';
6
8
  import { fontFamilies } from '../styles/fonts';
7
9
  import { toast } from '../../lib/sonner';
8
10
  import { Ionicons } from '@expo/vector-icons';
11
+ import { useFileStore, useFiles, useUploading as useUploadingStore, useUploadAggregateProgress, useDeleting as useDeletingStore } from '../stores/fileStore';
12
+ import Header from '../components/Header';
13
+ import { GroupedSection } from '../components';
9
14
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
10
15
  // Add this helper function near the top (after imports):
11
- async function uploadFileRaw(file, userId) {
12
- const fileName = file.name || 'upload.bin';
13
- const mimeType = file.type || 'application/octet-stream';
14
- const res = await fetch('/api/files/upload-raw', {
15
- method: 'POST',
16
- headers: {
17
- 'Content-Type': mimeType,
18
- 'X-File-Name': encodeURIComponent(fileName),
19
- 'X-User-Id': userId
20
- },
21
- body: file,
22
- credentials: 'include' // if you use cookies/session
23
- });
24
- if (!res.ok) {
25
- throw new Error(await res.text());
26
- }
27
- return await res.json();
16
+ async function uploadFileRaw(file, userId, oxyServices) {
17
+ return await oxyServices.uploadRawFile(file);
28
18
  }
29
19
  const FileManagementScreen = ({
30
20
  onClose,
@@ -50,12 +40,12 @@ const FileManagementScreen = ({
50
40
  console.log('[FileManagementScreen] Available content width:', availableContentWidth);
51
41
  console.log('[FileManagementScreen] Spacing fix applied: 4px uniform gap both horizontal and vertical');
52
42
  }, [containerWidth]);
53
- const [files, setFiles] = useState([]);
43
+ const files = useFiles();
44
+ const uploading = useUploadingStore();
45
+ const uploadProgress = useUploadAggregateProgress();
46
+ const deleting = useDeletingStore();
54
47
  const [loading, setLoading] = useState(true);
55
48
  const [refreshing, setRefreshing] = useState(false);
56
- const [uploading, setUploading] = useState(false);
57
- const [uploadProgress, setUploadProgress] = useState(null);
58
- const [deleting, setDeleting] = useState(null);
59
49
  const [selectedFile, setSelectedFile] = useState(null);
60
50
  const [showFileDetails, setShowFileDetails] = useState(false);
61
51
  const [openedFile, setOpenedFile] = useState(null);
@@ -64,11 +54,66 @@ const FileManagementScreen = ({
64
54
  const [showFileDetailsInViewer, setShowFileDetailsInViewer] = useState(false);
65
55
  const [viewMode, setViewMode] = useState('all');
66
56
  const [searchQuery, setSearchQuery] = useState('');
67
- const [filteredFiles, setFilteredFiles] = useState([]);
57
+ // Derived filtered files (avoid setState loops)
58
+ const filteredFiles = useMemo(() => {
59
+ let filteredByMode = files;
60
+ if (viewMode === 'photos') {
61
+ filteredByMode = files.filter(file => file.contentType.startsWith('image/'));
62
+ }
63
+ if (!searchQuery.trim()) {
64
+ return filteredByMode;
65
+ }
66
+ const query = searchQuery.toLowerCase();
67
+ return filteredByMode.filter(file => file.filename.toLowerCase().includes(query) || file.contentType.toLowerCase().includes(query) || file.metadata?.description && file.metadata.description.toLowerCase().includes(query));
68
+ }, [files, searchQuery, viewMode]);
68
69
  const [isDragging, setIsDragging] = useState(false);
69
70
  const [photoDimensions, setPhotoDimensions] = useState({});
70
71
  const [loadingDimensions, setLoadingDimensions] = useState(false);
71
72
  const [hoveredPreview, setHoveredPreview] = useState(null);
73
+ const uploadStartRef = useRef(null);
74
+ const MIN_BANNER_MS = 600;
75
+ const endUpload = useCallback(() => {
76
+ const started = uploadStartRef.current;
77
+ const elapsed = started ? Date.now() - started : MIN_BANNER_MS;
78
+ const remaining = elapsed < MIN_BANNER_MS ? MIN_BANNER_MS - elapsed : 0;
79
+ setTimeout(() => {
80
+ useFileStore.getState().setUploading(false);
81
+ uploadStartRef.current = null;
82
+ }, remaining);
83
+ }, []);
84
+
85
+ // Helper to safely request a thumbnail variant only for image mime types.
86
+ // Prevents backend warnings: "Variant thumb not supported for mime application/pdf".
87
+ const getSafeDownloadUrl = useCallback((file, variant = 'thumb') => {
88
+ const isImage = file.contentType.startsWith('image/');
89
+ const isVideo = file.contentType.startsWith('video/');
90
+
91
+ // Prefer explicit variant key if variants metadata present
92
+ if (file.variants && file.variants.length > 0) {
93
+ // For videos, try 'poster' regardless of requested variant
94
+ if (isVideo) {
95
+ const poster = file.variants.find(v => v.type === 'poster');
96
+ if (poster) return oxyServices.getFileDownloadUrl(file.id, 'poster');
97
+ }
98
+ if (isImage) {
99
+ const desired = file.variants.find(v => v.type === variant);
100
+ if (desired) return oxyServices.getFileDownloadUrl(file.id, variant);
101
+ }
102
+ }
103
+ if (isImage) {
104
+ return oxyServices.getFileDownloadUrl(file.id, variant);
105
+ }
106
+ if (isVideo) {
107
+ // Fallback to poster if backend supports implicit generation
108
+ try {
109
+ return oxyServices.getFileDownloadUrl(file.id, 'poster');
110
+ } catch {
111
+ return oxyServices.getFileDownloadUrl(file.id);
112
+ }
113
+ }
114
+ // Other mime types: no variant
115
+ return oxyServices.getFileDownloadUrl(file.id);
116
+ }, [oxyServices]);
72
117
 
73
118
  // Memoize theme-related calculations to prevent unnecessary recalculations
74
119
  const themeStyles = useMemo(() => {
@@ -89,16 +134,32 @@ const FileManagementScreen = ({
89
134
  const backgroundColor = themeStyles.backgroundColor;
90
135
  const borderColor = themeStyles.borderColor;
91
136
  const targetUserId = userId || user?.id;
92
- const loadFiles = useCallback(async (isRefresh = false) => {
137
+ const storeSetUploading = useFileStore(s => s.setUploading);
138
+ const storeSetUploadProgress = useFileStore(s => s.setUploadProgress);
139
+ const storeSetDeleting = useFileStore(s => s.setDeleting);
140
+ const loadFiles = useCallback(async (mode = 'initial') => {
93
141
  if (!targetUserId) return;
94
142
  try {
95
- if (isRefresh) {
143
+ if (mode === 'refresh') {
96
144
  setRefreshing(true);
97
- } else {
145
+ } else if (mode === 'initial') {
98
146
  setLoading(true);
99
147
  }
100
- const response = await oxyServices.listUserFiles(targetUserId);
101
- setFiles(response.files || []);
148
+ const response = await oxyServices.listUserFiles();
149
+ const assets = (response.files || []).map(f => ({
150
+ id: f.id,
151
+ filename: f.originalName || f.sha256,
152
+ contentType: f.mime,
153
+ length: f.size,
154
+ chunkSize: 0,
155
+ uploadDate: f.createdAt,
156
+ metadata: f.metadata || {},
157
+ variants: f.variants || []
158
+ }));
159
+ // Merge to preserve existing order & allow incremental updates
160
+ useFileStore.getState().setFiles(assets, {
161
+ merge: true
162
+ });
102
163
  } catch (error) {
103
164
  console.error('Failed to load files:', error);
104
165
  toast.error(error.message || 'Failed to load files');
@@ -108,24 +169,7 @@ const FileManagementScreen = ({
108
169
  }
109
170
  }, [targetUserId, oxyServices]);
110
171
 
111
- // Filter files based on search query and view mode
112
- useEffect(() => {
113
- let filteredByMode = files;
114
-
115
- // Filter by view mode first
116
- if (viewMode === 'photos') {
117
- filteredByMode = files.filter(file => file.contentType.startsWith('image/'));
118
- }
119
-
120
- // Then filter by search query
121
- if (!searchQuery.trim()) {
122
- setFilteredFiles(filteredByMode);
123
- } else {
124
- const query = searchQuery.toLowerCase();
125
- const filtered = filteredByMode.filter(file => file.filename.toLowerCase().includes(query) || file.contentType.toLowerCase().includes(query) || file.metadata?.description && file.metadata.description.toLowerCase().includes(query));
126
- setFilteredFiles(filtered);
127
- }
128
- }, [files, searchQuery, viewMode]);
172
+ // (removed effect; filteredFiles is memoized)
129
173
 
130
174
  // Load photo dimensions for justified grid
131
175
  const loadPhotoDimensions = useCallback(async photos => {
@@ -145,7 +189,7 @@ const FileManagementScreen = ({
145
189
  try {
146
190
  await Promise.all(photosToLoad.map(async photo => {
147
191
  try {
148
- const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
192
+ const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
149
193
  if (Platform.OS === 'web') {
150
194
  const img = new window.Image();
151
195
  await new Promise((resolve, reject) => {
@@ -224,7 +268,7 @@ const FileManagementScreen = ({
224
268
  if (selectedFiles.length === 0) return;
225
269
  if (!targetUserId) return; // Guard clause to ensure userId is defined
226
270
  try {
227
- setUploadProgress({
271
+ storeSetUploadProgress({
228
272
  current: 0,
229
273
  total: selectedFiles.length
230
274
  });
@@ -239,12 +283,55 @@ const FileManagementScreen = ({
239
283
  let failureCount = 0;
240
284
  const errors = [];
241
285
  for (let i = 0; i < selectedFiles.length; i++) {
242
- setUploadProgress({
286
+ storeSetUploadProgress({
243
287
  current: i + 1,
244
288
  total: selectedFiles.length
245
289
  });
246
290
  try {
247
- await uploadFileRaw(selectedFiles[i], targetUserId);
291
+ const raw = selectedFiles[i];
292
+ const optimisticId = `temp-${Date.now()}-${i}`;
293
+ const optimisticFile = {
294
+ id: optimisticId,
295
+ filename: raw.name,
296
+ contentType: raw.type || 'application/octet-stream',
297
+ length: raw.size,
298
+ chunkSize: 0,
299
+ uploadDate: new Date().toISOString(),
300
+ metadata: {
301
+ uploading: true
302
+ },
303
+ variants: []
304
+ };
305
+ useFileStore.getState().addFile(optimisticFile, {
306
+ prepend: true
307
+ });
308
+ const result = await uploadFileRaw(raw, targetUserId, oxyServices);
309
+ // Attempt to refresh file list incrementally – fetch single file metadata if API allows
310
+ if (result?.file || result?.files?.[0]) {
311
+ const f = result.file || result.files[0];
312
+ const merged = {
313
+ id: f.id,
314
+ filename: f.originalName || f.sha256 || raw.name,
315
+ contentType: f.mime || raw.type || 'application/octet-stream',
316
+ length: f.size || raw.size,
317
+ chunkSize: 0,
318
+ uploadDate: f.createdAt || new Date().toISOString(),
319
+ metadata: f.metadata || {},
320
+ variants: f.variants || []
321
+ };
322
+ // Remove optimistic then add real
323
+ useFileStore.getState().removeFile(optimisticId);
324
+ useFileStore.getState().addFile(merged, {
325
+ prepend: true
326
+ });
327
+ } else {
328
+ // Fallback: will reconcile on later list refresh
329
+ useFileStore.getState().updateFile(optimisticId, {
330
+ metadata: {
331
+ uploading: false
332
+ }
333
+ });
334
+ }
248
335
  successCount++;
249
336
  } catch (error) {
250
337
  failureCount++;
@@ -258,29 +345,51 @@ const FileManagementScreen = ({
258
345
  const errorMessage = `${failureCount} file(s) failed to upload${errors.length > 0 ? ':\n' + errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : '') : ''}`;
259
346
  toast.error(errorMessage);
260
347
  }
261
- setTimeout(async () => {
262
- await loadFiles();
263
- }, 500);
348
+ // Silent background refresh to ensure metadata/variants updated
349
+ setTimeout(() => {
350
+ loadFiles('silent');
351
+ }, 1200);
264
352
  } catch (error) {
265
353
  console.error('Upload error:', error);
266
354
  toast.error(error.message || 'Failed to upload files');
267
355
  } finally {
268
- setUploadProgress(null);
356
+ storeSetUploadProgress(null);
269
357
  }
270
358
  };
271
359
  const handleFileUpload = async () => {
272
360
  try {
273
- setUploading(true);
274
- setUploadProgress(null);
361
+ uploadStartRef.current = Date.now();
362
+ storeSetUploading(true);
363
+ storeSetUploadProgress(null);
275
364
  if (Platform.OS === 'web') {
276
365
  // Web file picker implementation
277
366
  const input = document.createElement('input');
278
367
  input.type = 'file';
279
368
  input.multiple = true;
280
369
  input.accept = '*/*';
370
+ // Fallback: if the user cancels the dialog (no onchange fires or 0 files), hide banner
371
+ const cancellationTimer = setTimeout(() => {
372
+ const state = useFileStore.getState();
373
+ if (state.uploading && uploadStartRef.current && !state.uploadProgress) {
374
+ // No selection happened; treat as cancel
375
+ endUpload();
376
+ }
377
+ }, 1500); // allow enough time for user to pick
378
+
281
379
  input.onchange = async e => {
282
- const selectedFiles = Array.from(e.target.files);
380
+ clearTimeout(cancellationTimer);
381
+ const selectedFiles = Array.from(e.target.files || []);
382
+ if (selectedFiles.length === 0) {
383
+ // User explicitly canceled (some browsers still fire onchange with empty list)
384
+ endUpload();
385
+ return;
386
+ }
387
+ storeSetUploadProgress({
388
+ current: 0,
389
+ total: selectedFiles.length
390
+ });
283
391
  await processFileUploads(selectedFiles);
392
+ endUpload();
284
393
  };
285
394
  input.click();
286
395
  } else {
@@ -296,8 +405,11 @@ const FileManagementScreen = ({
296
405
  } catch (error) {
297
406
  toast.error(error.message || 'Failed to upload file');
298
407
  } finally {
299
- setUploading(false);
300
- setUploadProgress(null);
408
+ // IMPORTANT: Do NOT call endUpload here.
409
+ // We only want to hide the banner after the actual upload(s) complete.
410
+ // The input.onchange handler invokes processFileUploads then calls endUpload().
411
+ // Calling endUpload here caused the banner to disappear while files were still uploading.
412
+ storeSetUploadProgress(null); // keep clearing any stale progress
301
413
  }
302
414
  };
303
415
  const handleFileDelete = async (fileId, filename) => {
@@ -314,15 +426,16 @@ const FileManagementScreen = ({
314
426
  });
315
427
  console.log('Target user ID:', targetUserId);
316
428
  console.log('Current user ID:', user?.id);
317
- setDeleting(fileId);
429
+ storeSetDeleting(fileId);
318
430
  const result = await oxyServices.deleteFile(fileId);
319
431
  console.log('Delete result:', result);
320
432
  toast.success('File deleted successfully');
321
433
 
322
434
  // Reload files after successful deletion
323
- setTimeout(async () => {
324
- await loadFiles();
325
- }, 500);
435
+ // Optimistic remove
436
+ useFileStore.getState().removeFile(fileId);
437
+ // Silent background reconcile
438
+ setTimeout(() => loadFiles('silent'), 800);
326
439
  } catch (error) {
327
440
  console.error('Delete error:', error);
328
441
  console.error('Error details:', error.response?.data || error.message);
@@ -331,16 +444,14 @@ const FileManagementScreen = ({
331
444
  if (error.message?.includes('File not found') || error.message?.includes('404')) {
332
445
  toast.error('File not found. It may have already been deleted.');
333
446
  // Still reload files to refresh the list
334
- setTimeout(async () => {
335
- await loadFiles();
336
- }, 500);
447
+ setTimeout(() => loadFiles('silent'), 800);
337
448
  } else if (error.message?.includes('permission') || error.message?.includes('403')) {
338
449
  toast.error('You do not have permission to delete this file.');
339
450
  } else {
340
451
  toast.error(error.message || 'Failed to delete file');
341
452
  }
342
453
  } finally {
343
- setDeleting(null);
454
+ storeSetDeleting(null);
344
455
  }
345
456
  };
346
457
 
@@ -351,24 +462,68 @@ const FileManagementScreen = ({
351
462
  setIsDragging(true);
352
463
  }
353
464
  };
465
+ const handleDragEnter = e => {
466
+ if (Platform.OS === 'web' && user?.id === targetUserId) {
467
+ e.preventDefault();
468
+ setIsDragging(true);
469
+ }
470
+ };
354
471
  const handleDragLeave = e => {
355
472
  if (Platform.OS === 'web') {
356
473
  e.preventDefault();
357
474
  setIsDragging(false);
358
475
  }
359
476
  };
477
+
478
+ // Global drag listeners (web) to catch drags outside component bounds
479
+ useEffect(() => {
480
+ if (Platform.OS !== 'web' || user?.id !== targetUserId) return;
481
+ const onDocDragEnter = e => {
482
+ if (e?.dataTransfer?.types?.includes('Files')) setIsDragging(true);
483
+ };
484
+ const onDocDragOver = e => {
485
+ if (e?.dataTransfer?.types?.includes('Files')) {
486
+ e.preventDefault();
487
+ setIsDragging(true);
488
+ }
489
+ };
490
+ const onDocDrop = e => {
491
+ if (e?.dataTransfer?.files?.length) {
492
+ e.preventDefault();
493
+ setIsDragging(false);
494
+ }
495
+ };
496
+ const onDocDragLeave = e => {
497
+ if (!e.relatedTarget && e.screenX === 0 && e.screenY === 0) setIsDragging(false);
498
+ };
499
+ document.addEventListener('dragenter', onDocDragEnter);
500
+ document.addEventListener('dragover', onDocDragOver);
501
+ document.addEventListener('drop', onDocDrop);
502
+ document.addEventListener('dragleave', onDocDragLeave);
503
+ return () => {
504
+ document.removeEventListener('dragenter', onDocDragEnter);
505
+ document.removeEventListener('dragover', onDocDragOver);
506
+ document.removeEventListener('drop', onDocDrop);
507
+ document.removeEventListener('dragleave', onDocDragLeave);
508
+ };
509
+ }, [user?.id, targetUserId]);
360
510
  const handleDrop = async e => {
361
511
  if (Platform.OS === 'web' && user?.id === targetUserId) {
362
512
  e.preventDefault();
363
513
  setIsDragging(false);
364
- setUploading(true);
514
+ uploadStartRef.current = Date.now();
515
+ storeSetUploading(true);
365
516
  try {
366
517
  const files = Array.from(e.dataTransfer.files);
518
+ if (files.length > 0) storeSetUploadProgress({
519
+ current: 0,
520
+ total: files.length
521
+ });
367
522
  await processFileUploads(files);
368
523
  } catch (error) {
369
524
  toast.error(error.message || 'Failed to upload files');
370
525
  } finally {
371
- setUploading(false);
526
+ endUpload();
372
527
  }
373
528
  }
374
529
  };
@@ -423,7 +578,7 @@ const FileManagementScreen = ({
423
578
  const k = 1024;
424
579
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
425
580
  const i = Math.floor(Math.log(bytes) / Math.log(k));
426
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
581
+ return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
427
582
  };
428
583
  const getFileIcon = contentType => {
429
584
  if (contentType.startsWith('image/')) return 'image';
@@ -483,7 +638,7 @@ const FileManagementScreen = ({
483
638
  setShowFileDetails(true);
484
639
  };
485
640
  const renderSimplePhotoItem = useCallback((photo, index) => {
486
- const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
641
+ const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
487
642
 
488
643
  // Calculate photo item width based on actual container size from bottom sheet
489
644
  let itemsPerRow = 3; // Default for mobile
@@ -505,41 +660,24 @@ const FileManagementScreen = ({
505
660
  activeOpacity: 0.8,
506
661
  children: /*#__PURE__*/_jsx(View, {
507
662
  style: styles.simplePhotoContainer,
508
- children: Platform.OS === 'web' ? /*#__PURE__*/_jsx("img", {
509
- src: downloadUrl,
510
- alt: photo.filename,
511
- style: {
512
- width: '100%',
513
- height: '100%',
514
- objectFit: 'cover',
515
- borderRadius: 8,
516
- transition: 'transform 0.2s ease'
517
- },
518
- loading: "lazy",
519
- onError: e => {
520
- console.error('Photo failed to load:', e);
521
- },
522
- onMouseEnter: e => {
523
- e.currentTarget.style.transform = 'scale(1.05)';
524
- },
525
- onMouseLeave: e => {
526
- e.currentTarget.style.transform = 'scale(1)';
527
- }
528
- }) : /*#__PURE__*/_jsx(Image, {
663
+ children: /*#__PURE__*/_jsx(ExpoImage, {
529
664
  source: {
530
665
  uri: downloadUrl
531
666
  },
532
667
  style: styles.simplePhotoImage,
533
- resizeMode: "cover",
668
+ contentFit: "cover",
669
+ transition: 120,
670
+ cachePolicy: "memory-disk",
534
671
  onError: e => {
535
- console.error('Photo failed to load:', e);
536
- }
672
+ console.error('Photo failed to load:', e?.nativeEvent ?? e);
673
+ },
674
+ accessibilityLabel: photo.filename
537
675
  })
538
676
  })
539
677
  }, photo.id);
540
678
  }, [oxyServices, containerWidth]);
541
679
  const renderJustifiedPhotoItem = useCallback((photo, width, height, isLast) => {
542
- const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
680
+ const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
543
681
  return /*#__PURE__*/_jsx(TouchableOpacity, {
544
682
  style: [styles.justifiedPhotoItem, {
545
683
  width,
@@ -549,46 +687,33 @@ const FileManagementScreen = ({
549
687
  activeOpacity: 0.8,
550
688
  children: /*#__PURE__*/_jsx(View, {
551
689
  style: styles.justifiedPhotoContainer,
552
- children: Platform.OS === 'web' ? /*#__PURE__*/_jsx("img", {
553
- src: downloadUrl,
554
- alt: photo.filename,
555
- style: {
556
- width: '100%',
557
- height: '100%',
558
- objectFit: 'cover',
559
- borderRadius: 6,
560
- transition: 'transform 0.2s ease, box-shadow 0.2s ease'
561
- },
562
- loading: "lazy",
563
- onError: e => {
564
- console.error('Photo failed to load:', e);
565
- },
566
- onMouseEnter: e => {
567
- e.currentTarget.style.transform = 'scale(1.02)';
568
- e.currentTarget.style.boxShadow = '0 8px 25px rgba(0,0,0,0.15)';
569
- e.currentTarget.style.zIndex = '10';
570
- },
571
- onMouseLeave: e => {
572
- e.currentTarget.style.transform = 'scale(1)';
573
- e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
574
- e.currentTarget.style.zIndex = '1';
575
- }
576
- }) : /*#__PURE__*/_jsx(Image, {
690
+ children: /*#__PURE__*/_jsx(ExpoImage, {
577
691
  source: {
578
692
  uri: downloadUrl
579
693
  },
580
694
  style: styles.justifiedPhotoImage,
581
- resizeMode: "cover",
695
+ contentFit: "cover",
696
+ transition: 120,
697
+ cachePolicy: "memory-disk",
582
698
  onError: e => {
583
- console.error('Photo failed to load:', e);
584
- }
699
+ console.error('Photo failed to load:', e?.nativeEvent ?? e);
700
+ },
701
+ accessibilityLabel: photo.filename
585
702
  })
586
703
  })
587
704
  }, photo.id);
588
705
  }, [oxyServices]);
706
+
707
+ // Run initial load once per targetUserId change to avoid accidental loops
708
+ const lastLoadedFor = useRef(undefined);
589
709
  useEffect(() => {
590
- loadFiles();
591
- }, [loadFiles]);
710
+ const key = targetUserId || 'anonymous';
711
+ if (lastLoadedFor.current !== key) {
712
+ lastLoadedFor.current = key;
713
+ loadFiles('initial');
714
+ }
715
+ // eslint-disable-next-line react-hooks/exhaustive-deps
716
+ }, [targetUserId]);
592
717
  const renderFileItem = file => {
593
718
  const isImage = file.contentType.startsWith('image/');
594
719
  const isPDF = file.contentType.includes('pdf');
@@ -612,35 +737,19 @@ const FileManagementScreen = ({
612
737
  onMouseEnter: () => setHoveredPreview(file.id),
613
738
  onMouseLeave: () => setHoveredPreview(null)
614
739
  }),
615
- children: [isImage && (Platform.OS === 'web' ? /*#__PURE__*/_jsx("img", {
616
- src: oxyServices.getFileDownloadUrl(file.id),
617
- style: {
618
- width: '100%',
619
- height: '100%',
620
- objectFit: 'cover',
621
- borderRadius: 8,
622
- transition: 'transform 0.2s ease',
623
- transform: hoveredPreview === file.id ? 'scale(1.05)' : 'scale(1)'
624
- },
625
- onError: e => {
626
- // Show fallback icon if image fails to load
627
- e.currentTarget.style.display = 'none';
628
- const fallbackElement = e.currentTarget.parentElement?.querySelector('[data-fallback="true"]');
629
- if (fallbackElement) {
630
- fallbackElement.style.display = 'flex';
631
- }
632
- }
633
- }) : /*#__PURE__*/_jsx(Image, {
740
+ children: [isImage && /*#__PURE__*/_jsx(ExpoImage, {
634
741
  source: {
635
- uri: oxyServices.getFileDownloadUrl(file.id)
742
+ uri: getSafeDownloadUrl(file, 'thumb')
636
743
  },
637
744
  style: styles.previewImage,
638
- resizeMode: "cover",
639
- onError: () => {
640
- // For React Native, you might want to set an error state
641
- console.warn('Failed to load image preview for file:', file.id);
642
- }
643
- })), isPDF && /*#__PURE__*/_jsxs(View, {
745
+ contentFit: "cover",
746
+ transition: 120,
747
+ cachePolicy: "memory-disk",
748
+ onError: _ => {
749
+ console.warn('Failed to load image preview.');
750
+ },
751
+ accessibilityLabel: file.filename
752
+ }), isPDF && /*#__PURE__*/_jsxs(View, {
644
753
  style: styles.pdfPreview,
645
754
  children: [/*#__PURE__*/_jsx(Ionicons, {
646
755
  name: "document",
@@ -653,16 +762,26 @@ const FileManagementScreen = ({
653
762
  children: "PDF"
654
763
  })]
655
764
  }), isVideo && /*#__PURE__*/_jsxs(View, {
656
- style: styles.videoPreview,
657
- children: [/*#__PURE__*/_jsx(Ionicons, {
658
- name: "play-circle",
659
- size: 32,
660
- color: themeStyles.primaryColor
661
- }), /*#__PURE__*/_jsx(Text, {
662
- style: [styles.videoLabel, {
663
- color: themeStyles.primaryColor
664
- }],
665
- children: "VIDEO"
765
+ style: styles.videoPreviewWrapper,
766
+ children: [/*#__PURE__*/_jsx(ExpoImage, {
767
+ source: {
768
+ uri: getSafeDownloadUrl(file, 'thumb')
769
+ },
770
+ style: styles.videoPosterImage,
771
+ contentFit: "cover",
772
+ transition: 120,
773
+ cachePolicy: "memory-disk",
774
+ onError: _ => {
775
+ // If thumbnail not available, we still show icon overlay
776
+ },
777
+ accessibilityLabel: file.filename + ' video thumbnail'
778
+ }), /*#__PURE__*/_jsx(View, {
779
+ style: styles.videoOverlay,
780
+ children: /*#__PURE__*/_jsx(Ionicons, {
781
+ name: "play",
782
+ size: 24,
783
+ color: "#FFFFFF"
784
+ })
666
785
  })]
667
786
  }), /*#__PURE__*/_jsx(View, {
668
787
  style: [styles.fallbackIcon, {
@@ -755,8 +874,78 @@ const FileManagementScreen = ({
755
874
  })]
756
875
  }, file.id);
757
876
  };
877
+
878
+ // GroupedSection-based file items (for 'all' view) replacing legacy flat list look
879
+ const groupedFileItems = useMemo(() => {
880
+ return filteredFiles.filter(f => true) // placeholder for future filtering
881
+ .sort((a, b) => new Date(b.uploadDate).getTime() - new Date(a.uploadDate).getTime()).map(file => {
882
+ const isImage = file.contentType.startsWith('image/');
883
+ const isVideo = file.contentType.startsWith('video/');
884
+ const hasPreview = isImage || isVideo;
885
+ const previewUrl = hasPreview ? isVideo ? getSafeDownloadUrl(file, 'poster') : getSafeDownloadUrl(file, 'thumb') : undefined;
886
+ return {
887
+ id: file.id,
888
+ image: previewUrl,
889
+ imageSize: 44,
890
+ icon: !previewUrl ? getFileIcon(file.contentType) : undefined,
891
+ iconColor: themeStyles.primaryColor,
892
+ title: file.filename,
893
+ subtitle: `${formatFileSize(file.length)} • ${new Date(file.uploadDate).toLocaleDateString()}`,
894
+ theme: theme,
895
+ onPress: () => handleFileOpen(file),
896
+ showChevron: false,
897
+ dense: true,
898
+ multiRow: !!file.metadata?.description,
899
+ customContent: /*#__PURE__*/_jsxs(View, {
900
+ style: styles.groupedActions,
901
+ children: [(isImage || isVideo || file.contentType.includes('pdf')) && /*#__PURE__*/_jsx(TouchableOpacity, {
902
+ style: [styles.groupedActionBtn, {
903
+ backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0'
904
+ }],
905
+ onPress: () => handleFileOpen(file),
906
+ children: /*#__PURE__*/_jsx(Ionicons, {
907
+ name: "eye",
908
+ size: 18,
909
+ color: themeStyles.primaryColor
910
+ })
911
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
912
+ style: [styles.groupedActionBtn, {
913
+ backgroundColor: themeStyles.isDarkTheme ? '#333333' : '#F0F0F0'
914
+ }],
915
+ onPress: () => handleFileDownload(file.id, file.filename),
916
+ children: /*#__PURE__*/_jsx(Ionicons, {
917
+ name: "download",
918
+ size: 18,
919
+ color: themeStyles.primaryColor
920
+ })
921
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
922
+ style: [styles.groupedActionBtn, {
923
+ backgroundColor: themeStyles.isDarkTheme ? '#400000' : '#FFEBEE'
924
+ }],
925
+ onPress: () => handleFileDelete(file.id, file.filename),
926
+ disabled: deleting === file.id,
927
+ children: deleting === file.id ? /*#__PURE__*/_jsx(ActivityIndicator, {
928
+ size: "small",
929
+ color: themeStyles.dangerColor
930
+ }) : /*#__PURE__*/_jsx(Ionicons, {
931
+ name: "trash",
932
+ size: 18,
933
+ color: themeStyles.dangerColor
934
+ })
935
+ })]
936
+ }),
937
+ customContentBelow: file.metadata?.description ? /*#__PURE__*/_jsx(Text, {
938
+ style: [styles.groupedDescription, {
939
+ color: themeStyles.isDarkTheme ? '#AAAAAA' : '#666666'
940
+ }],
941
+ numberOfLines: 2,
942
+ children: file.metadata.description
943
+ }) : undefined
944
+ }; // GroupedSectionItem shape
945
+ });
946
+ }, [filteredFiles, theme, themeStyles, deleting, handleFileDownload, handleFileDelete, handleFileOpen, getSafeDownloadUrl]);
758
947
  const renderPhotoItem = (photo, index) => {
759
- const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
948
+ const downloadUrl = getSafeDownloadUrl(photo, 'thumb');
760
949
 
761
950
  // Calculate photo item width based on actual container size from bottom sheet
762
951
  let itemsPerRow = 3; // Default for mobile
@@ -777,36 +966,18 @@ const FileManagementScreen = ({
777
966
  activeOpacity: 0.8,
778
967
  children: /*#__PURE__*/_jsx(View, {
779
968
  style: styles.photoContainer,
780
- children: Platform.OS === 'web' ? /*#__PURE__*/_jsx("img", {
781
- src: downloadUrl,
782
- alt: photo.filename,
783
- style: {
784
- width: '100%',
785
- height: '100%',
786
- objectFit: 'cover',
787
- borderRadius: 8,
788
- transition: 'transform 0.2s ease'
789
- },
790
- loading: "lazy",
791
- onError: e => {
792
- console.error('Photo failed to load:', e);
793
- // Could replace with placeholder image
794
- },
795
- onMouseEnter: e => {
796
- e.currentTarget.style.transform = 'scale(1.02)';
797
- },
798
- onMouseLeave: e => {
799
- e.currentTarget.style.transform = 'scale(1)';
800
- }
801
- }) : /*#__PURE__*/_jsx(Image, {
969
+ children: /*#__PURE__*/_jsx(ExpoImage, {
802
970
  source: {
803
971
  uri: downloadUrl
804
972
  },
805
973
  style: styles.photoImage,
806
- resizeMode: "cover",
807
- onError: e => {
808
- console.error('Photo failed to load:', e);
809
- }
974
+ contentFit: "cover",
975
+ transition: 120,
976
+ cachePolicy: "memory-disk",
977
+ onError: _ => {
978
+ console.warn('Failed to load image preview for photo:', photo.id);
979
+ },
980
+ accessibilityLabel: photo.filename
810
981
  })
811
982
  })
812
983
  }, photo.id);
@@ -825,11 +996,11 @@ const FileManagementScreen = ({
825
996
  color: themeStyles.textColor
826
997
  }],
827
998
  children: "No Photos Yet"
828
- }), /*#__PURE__*/_jsx(Text, {
999
+ }), /*#__PURE__*/_jsxs(Text, {
829
1000
  style: [styles.emptyStateDescription, {
830
1001
  color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'
831
1002
  }],
832
- children: user?.id === targetUserId ? `Upload photos to get started. You can select multiple photos at once${Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}` : "This user hasn't uploaded any photos yet"
1003
+ children: [" ", user?.id === targetUserId ? `Upload photos to get started. You can select multiple photos at once${Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}` : "This user hasn't uploaded any photos yet", " "]
833
1004
  }), user?.id === targetUserId && /*#__PURE__*/_jsx(TouchableOpacity, {
834
1005
  style: [styles.emptyStateButton, {
835
1006
  backgroundColor: themeStyles.primaryColor
@@ -857,7 +1028,7 @@ const FileManagementScreen = ({
857
1028
  contentContainerStyle: styles.photoScrollContainer,
858
1029
  refreshControl: /*#__PURE__*/_jsx(RefreshControl, {
859
1030
  refreshing: refreshing,
860
- onRefresh: () => loadFiles(true),
1031
+ onRefresh: () => loadFiles('refresh'),
861
1032
  tintColor: themeStyles.primaryColor
862
1033
  }),
863
1034
  showsVerticalScrollIndicator: false,
@@ -899,7 +1070,9 @@ const FileManagementScreen = ({
899
1070
  // Load dimensions for new photos
900
1071
  React.useEffect(() => {
901
1072
  loadPhotoDimensions(photos);
902
- }, [photos.map(p => p.id).join(','), loadPhotoDimensions]);
1073
+ // Depend only on photo IDs to avoid re-running from dimension state changes
1074
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1075
+ }, [photos.map(p => p.id).join(',')]);
903
1076
 
904
1077
  // Group photos by date
905
1078
  const photosByDate = React.useMemo(() => {
@@ -1338,31 +1511,22 @@ const FileManagementScreen = ({
1338
1511
  })]
1339
1512
  }) : isImage && fileContent ? /*#__PURE__*/_jsx(View, {
1340
1513
  style: styles.imageContainer,
1341
- children: Platform.OS === 'web' ? /*#__PURE__*/_jsx("img", {
1342
- src: fileContent,
1343
- alt: openedFile.filename,
1344
- style: {
1345
- maxWidth: '100%',
1346
- maxHeight: '80vh',
1347
- objectFit: 'contain',
1348
- borderRadius: 8
1349
- },
1350
- onError: e => {
1351
- console.error('Image failed to load:', e);
1352
- }
1353
- }) : /*#__PURE__*/_jsx(Image, {
1514
+ children: /*#__PURE__*/_jsx(ExpoImage, {
1354
1515
  source: {
1355
1516
  uri: fileContent
1356
1517
  },
1357
1518
  style: {
1358
1519
  width: '100%',
1359
1520
  height: 400,
1360
- resizeMode: 'contain',
1361
1521
  borderRadius: 8
1362
1522
  },
1523
+ contentFit: "contain",
1524
+ transition: 120,
1525
+ cachePolicy: "memory-disk",
1363
1526
  onError: e => {
1364
- console.error('Image failed to load:', e);
1365
- }
1527
+ console.error('Image failed to load:', e?.nativeEvent ?? e);
1528
+ },
1529
+ accessibilityLabel: openedFile.filename
1366
1530
  })
1367
1531
  }) : isText && fileContent ? /*#__PURE__*/_jsx(View, {
1368
1532
  style: [styles.textContainer, {
@@ -1525,157 +1689,76 @@ const FileManagementScreen = ({
1525
1689
  });
1526
1690
  }
1527
1691
  return /*#__PURE__*/_jsxs(View, {
1528
- style: [styles.container, {
1529
- backgroundColor
1530
- }, isDragging && Platform.OS === 'web' && styles.dragOverlay],
1692
+ style: [styles.container, isDragging && Platform.OS === 'web' && styles.dragOverlay],
1531
1693
  ...(Platform.OS === 'web' && user?.id === targetUserId ? {
1532
1694
  onDragOver: handleDragOver,
1695
+ onDragEnter: handleDragEnter,
1533
1696
  onDragLeave: handleDragLeave,
1534
1697
  onDrop: handleDrop
1535
1698
  } : {}),
1536
- children: [/*#__PURE__*/_jsxs(View, {
1537
- style: [styles.header, {
1538
- borderBottomColor: borderColor,
1539
- backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
1540
- shadowColor: '#000000',
1541
- shadowOffset: {
1542
- width: 0,
1543
- height: 2
1544
- },
1545
- shadowOpacity: themeStyles.isDarkTheme ? 0.3 : 0.1,
1546
- shadowRadius: 8,
1547
- elevation: 4,
1548
- ...Platform.select({
1549
- web: {
1550
- boxShadow: '0 2px 8px rgba(0,0,0,0.15)'
1551
- },
1552
- default: {}
1553
- })
1554
- }],
1555
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
1556
- style: [styles.backButton, {
1557
- backgroundColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#F8F9FA',
1558
- borderRadius: 12
1699
+ children: [/*#__PURE__*/_jsx(Header, {
1700
+ title: viewMode === 'photos' ? 'Photos' : 'File Management',
1701
+ subtitle: `${filteredFiles.length} ${filteredFiles.length === 1 ? 'item' : 'items'}`,
1702
+ onBack: onClose || goBack,
1703
+ theme: theme,
1704
+ showBackButton: true,
1705
+ variant: "minimal",
1706
+ elevation: "none",
1707
+ titleAlignment: "left"
1708
+ }), /*#__PURE__*/_jsxs(View, {
1709
+ style: styles.controlsBar,
1710
+ children: [/*#__PURE__*/_jsxs(View, {
1711
+ style: [styles.viewModeToggle, {
1712
+ backgroundColor: themeStyles.isDarkTheme ? '#181818' : '#FFFFFF',
1713
+ borderWidth: 1,
1714
+ borderColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#E8E9EA'
1559
1715
  }],
1560
- onPress: onClose || goBack,
1561
- children: /*#__PURE__*/_jsx(Ionicons, {
1562
- name: "arrow-back",
1563
- size: 22,
1564
- color: themeStyles.textColor
1565
- })
1566
- }), /*#__PURE__*/_jsxs(View, {
1567
- style: styles.headerTitleContainer,
1568
- children: [/*#__PURE__*/_jsx(Text, {
1569
- style: [styles.headerTitle, {
1570
- color: themeStyles.textColor
1571
- }],
1572
- children: viewMode === 'photos' ? 'Photos' : 'File Management'
1573
- }), /*#__PURE__*/_jsxs(Text, {
1574
- style: [styles.headerSubtitle, {
1575
- color: themeStyles.isDarkTheme ? '#AAAAAA' : '#666666'
1576
- }],
1577
- children: [filteredFiles.length, " ", filteredFiles.length === 1 ? 'item' : 'items']
1578
- })]
1579
- }), /*#__PURE__*/_jsxs(View, {
1580
- style: styles.headerActions,
1581
- children: [/*#__PURE__*/_jsxs(View, {
1582
- style: [styles.viewModeToggle, {
1583
- backgroundColor: themeStyles.isDarkTheme ? '#2A2A2A' : '#F8F9FA',
1584
- borderWidth: 1,
1585
- borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
1586
- shadowColor: '#000000',
1587
- shadowOffset: {
1588
- width: 0,
1589
- height: 1
1590
- },
1591
- shadowOpacity: themeStyles.isDarkTheme ? 0.3 : 0.05,
1592
- shadowRadius: 4,
1593
- elevation: 2
1716
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
1717
+ style: [styles.viewModeButton, viewMode === 'all' && {
1718
+ backgroundColor: themeStyles.primaryColor
1594
1719
  }],
1595
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
1596
- style: [styles.viewModeButton, viewMode === 'all' && {
1597
- backgroundColor: themeStyles.primaryColor,
1598
- shadowColor: themeStyles.primaryColor,
1599
- shadowOffset: {
1600
- width: 0,
1601
- height: 2
1602
- },
1603
- shadowOpacity: 0.3,
1604
- shadowRadius: 4,
1605
- elevation: 3
1606
- }],
1607
- onPress: () => setViewMode('all'),
1608
- children: /*#__PURE__*/_jsx(Ionicons, {
1609
- name: "folder",
1610
- size: 18,
1611
- color: viewMode === 'all' ? '#FFFFFF' : themeStyles.textColor
1612
- })
1613
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
1614
- style: [styles.viewModeButton, viewMode === 'photos' && {
1615
- backgroundColor: themeStyles.primaryColor,
1616
- shadowColor: themeStyles.primaryColor,
1617
- shadowOffset: {
1618
- width: 0,
1619
- height: 2
1620
- },
1621
- shadowOpacity: 0.3,
1622
- shadowRadius: 4,
1623
- elevation: 3
1624
- }],
1625
- onPress: () => setViewMode('photos'),
1626
- children: /*#__PURE__*/_jsx(Ionicons, {
1627
- name: "images",
1628
- size: 18,
1629
- color: viewMode === 'photos' ? '#FFFFFF' : themeStyles.textColor
1630
- })
1631
- })]
1632
- }), user?.id === targetUserId && /*#__PURE__*/_jsx(TouchableOpacity, {
1633
- style: [styles.uploadButton, {
1634
- backgroundColor: themeStyles.primaryColor,
1635
- shadowColor: themeStyles.primaryColor,
1636
- shadowOffset: {
1637
- width: 0,
1638
- height: 3
1639
- },
1640
- shadowOpacity: 0.4,
1641
- shadowRadius: 6,
1642
- elevation: 5,
1643
- transform: uploading ? [{
1644
- scale: 0.95
1645
- }] : [{
1646
- scale: 1
1647
- }]
1720
+ onPress: () => setViewMode('all'),
1721
+ children: /*#__PURE__*/_jsx(Ionicons, {
1722
+ name: "folder",
1723
+ size: 18,
1724
+ color: viewMode === 'all' ? '#FFFFFF' : themeStyles.textColor
1725
+ })
1726
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
1727
+ style: [styles.viewModeButton, viewMode === 'photos' && {
1728
+ backgroundColor: themeStyles.primaryColor
1648
1729
  }],
1649
- onPress: handleFileUpload,
1650
- disabled: uploading,
1651
- children: uploading ? /*#__PURE__*/_jsxs(View, {
1652
- style: styles.uploadProgress,
1653
- children: [/*#__PURE__*/_jsx(ActivityIndicator, {
1654
- size: "small",
1655
- color: "#FFFFFF"
1656
- }), uploadProgress && /*#__PURE__*/_jsxs(Text, {
1657
- style: styles.uploadProgressText,
1658
- children: [uploadProgress.current, "/", uploadProgress.total]
1659
- })]
1660
- }) : /*#__PURE__*/_jsx(Ionicons, {
1661
- name: "add",
1662
- size: 26,
1663
- color: "#FFFFFF"
1730
+ onPress: () => setViewMode('photos'),
1731
+ children: /*#__PURE__*/_jsx(Ionicons, {
1732
+ name: "images",
1733
+ size: 18,
1734
+ color: viewMode === 'photos' ? '#FFFFFF' : themeStyles.textColor
1664
1735
  })
1665
1736
  })]
1737
+ }), user?.id === targetUserId && /*#__PURE__*/_jsx(TouchableOpacity, {
1738
+ style: [styles.uploadButton, {
1739
+ backgroundColor: themeStyles.primaryColor
1740
+ }],
1741
+ onPress: handleFileUpload,
1742
+ disabled: uploading,
1743
+ children: uploading ? /*#__PURE__*/_jsxs(View, {
1744
+ style: styles.uploadProgress,
1745
+ children: [/*#__PURE__*/_jsx(ActivityIndicator, {
1746
+ size: "small",
1747
+ color: "#FFFFFF"
1748
+ }), uploadProgress && /*#__PURE__*/_jsxs(Text, {
1749
+ style: styles.uploadProgressText,
1750
+ children: [uploadProgress.current, "/", uploadProgress.total]
1751
+ })]
1752
+ }) : /*#__PURE__*/_jsx(Ionicons, {
1753
+ name: "add",
1754
+ size: 22,
1755
+ color: "#FFFFFF"
1756
+ })
1666
1757
  })]
1667
1758
  }), files.length > 0 && (viewMode === 'all' || files.some(f => f.contentType.startsWith('image/'))) && /*#__PURE__*/_jsxs(View, {
1668
1759
  style: [styles.searchContainer, {
1669
1760
  backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
1670
- borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
1671
- shadowColor: '#000000',
1672
- shadowOffset: {
1673
- width: 0,
1674
- height: 1
1675
- },
1676
- shadowOpacity: themeStyles.isDarkTheme ? 0.2 : 0.05,
1677
- shadowRadius: 4,
1678
- elevation: 2
1761
+ borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA'
1679
1762
  }],
1680
1763
  children: [/*#__PURE__*/_jsx(Ionicons, {
1681
1764
  name: "search",
@@ -1701,15 +1784,7 @@ const FileManagementScreen = ({
1701
1784
  }), files.length > 0 && /*#__PURE__*/_jsxs(View, {
1702
1785
  style: [styles.statsContainer, {
1703
1786
  backgroundColor: themeStyles.isDarkTheme ? '#1A1A1A' : '#FFFFFF',
1704
- borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA',
1705
- shadowColor: '#000000',
1706
- shadowOffset: {
1707
- width: 0,
1708
- height: 1
1709
- },
1710
- shadowOpacity: themeStyles.isDarkTheme ? 0.2 : 0.05,
1711
- shadowRadius: 4,
1712
- elevation: 2
1787
+ borderColor: themeStyles.isDarkTheme ? '#3A3A3A' : '#E8E9EA'
1713
1788
  }],
1714
1789
  children: [/*#__PURE__*/_jsxs(View, {
1715
1790
  style: styles.statItem,
@@ -1756,7 +1831,7 @@ const FileManagementScreen = ({
1756
1831
  contentContainerStyle: styles.scrollContainer,
1757
1832
  refreshControl: /*#__PURE__*/_jsx(RefreshControl, {
1758
1833
  refreshing: refreshing,
1759
- onRefresh: () => loadFiles(true),
1834
+ onRefresh: () => loadFiles('refresh'),
1760
1835
  tintColor: themeStyles.primaryColor
1761
1836
  }),
1762
1837
  children: filteredFiles.length === 0 && searchQuery.length > 0 ? /*#__PURE__*/_jsxs(View, {
@@ -1789,10 +1864,37 @@ const FileManagementScreen = ({
1789
1864
  children: "Clear Search"
1790
1865
  })]
1791
1866
  })]
1792
- }) : filteredFiles.length === 0 ? renderEmptyState() : /*#__PURE__*/_jsx(_Fragment, {
1793
- children: filteredFiles.map(renderFileItem)
1867
+ }) : filteredFiles.length === 0 ? renderEmptyState() : /*#__PURE__*/_jsx(GroupedSection, {
1868
+ items: groupedFileItems,
1869
+ theme: theme
1870
+ })
1871
+ }), renderFileDetailsModal(), uploading && /*#__PURE__*/_jsx(View, {
1872
+ pointerEvents: "none",
1873
+ style: styles.uploadBannerContainer,
1874
+ children: /*#__PURE__*/_jsxs(View, {
1875
+ style: [styles.uploadBanner, {
1876
+ backgroundColor: themeStyles.isDarkTheme ? '#222831EE' : '#FFFFFFEE',
1877
+ borderColor: themeStyles.borderColor
1878
+ }],
1879
+ children: [/*#__PURE__*/_jsx(Ionicons, {
1880
+ name: "cloud-upload",
1881
+ size: 18,
1882
+ color: themeStyles.primaryColor
1883
+ }), /*#__PURE__*/_jsxs(Text, {
1884
+ style: [styles.uploadBannerText, {
1885
+ color: themeStyles.textColor
1886
+ }],
1887
+ children: ["Uploading", uploadProgress ? ` ${uploadProgress.current}/${uploadProgress.total}` : '...']
1888
+ }), /*#__PURE__*/_jsx(View, {
1889
+ style: styles.uploadBannerDots,
1890
+ children: [0, 1, 2].map(i => /*#__PURE__*/_jsx(View, {
1891
+ style: [styles.dot, {
1892
+ opacity: (Date.now() / 400 + i) % 3 < 1 ? 1 : 0.25
1893
+ }]
1894
+ }, i))
1895
+ })]
1794
1896
  })
1795
- }), renderFileDetailsModal(), isDragging && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
1897
+ }), isDragging && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
1796
1898
  style: styles.dragDropOverlay,
1797
1899
  children: /*#__PURE__*/_jsxs(View, {
1798
1900
  style: styles.dragDropContent,
@@ -1820,9 +1922,9 @@ const styles = StyleSheet.create({
1820
1922
  flex: 1
1821
1923
  },
1822
1924
  dragOverlay: {
1823
- backgroundColor: 'rgba(0, 122, 255, 0.1)',
1824
- borderWidth: 2,
1825
- borderColor: '#007AFF',
1925
+ backgroundColor: 'rgba(0, 122, 255, 0.06)',
1926
+ borderWidth: 1,
1927
+ borderColor: '#66AFFF',
1826
1928
  borderStyle: 'dashed'
1827
1929
  },
1828
1930
  centerContent: {
@@ -1833,8 +1935,8 @@ const styles = StyleSheet.create({
1833
1935
  flexDirection: 'row',
1834
1936
  alignItems: 'center',
1835
1937
  justifyContent: 'space-between',
1836
- paddingHorizontal: 24,
1837
- paddingVertical: 20,
1938
+ paddingHorizontal: 16,
1939
+ paddingVertical: 12,
1838
1940
  borderBottomWidth: 1,
1839
1941
  position: 'relative'
1840
1942
  },
@@ -1883,16 +1985,57 @@ const styles = StyleSheet.create({
1883
1985
  fontWeight: '600',
1884
1986
  marginTop: 2
1885
1987
  },
1988
+ uploadBannerContainer: {
1989
+ position: 'absolute',
1990
+ top: 72,
1991
+ // below header
1992
+ left: 0,
1993
+ right: 0,
1994
+ alignItems: 'center',
1995
+ zIndex: 50
1996
+ },
1997
+ uploadBanner: {
1998
+ flexDirection: 'row',
1999
+ alignItems: 'center',
2000
+ paddingHorizontal: 14,
2001
+ paddingVertical: 8,
2002
+ borderRadius: 24,
2003
+ gap: 8,
2004
+ borderWidth: 1,
2005
+ shadowColor: '#000',
2006
+ shadowOpacity: 0.1,
2007
+ shadowRadius: 6,
2008
+ shadowOffset: {
2009
+ width: 0,
2010
+ height: 2
2011
+ },
2012
+ elevation: 2
2013
+ },
2014
+ uploadBannerText: {
2015
+ fontSize: 13,
2016
+ fontWeight: '500'
2017
+ },
2018
+ uploadBannerDots: {
2019
+ flexDirection: 'row',
2020
+ gap: 4,
2021
+ marginLeft: 2
2022
+ },
2023
+ dot: {
2024
+ width: 6,
2025
+ height: 6,
2026
+ borderRadius: 3,
2027
+ backgroundColor: '#007AFF'
2028
+ },
1886
2029
  searchContainer: {
1887
2030
  flexDirection: 'row',
1888
2031
  alignItems: 'center',
1889
- paddingHorizontal: 20,
1890
- paddingVertical: 16,
1891
- marginHorizontal: 24,
1892
- marginTop: 20,
1893
- borderRadius: 16,
2032
+ paddingHorizontal: 14,
2033
+ paddingVertical: 10,
2034
+ marginHorizontal: 16,
2035
+ marginTop: 12,
2036
+ borderRadius: 10,
1894
2037
  borderWidth: 1,
1895
- gap: 16
2038
+ gap: 10
1896
2039
  },
1897
2040
  searchInput: {
1898
2041
  flex: 1,
@@ -1911,11 +2054,11 @@ const styles = StyleSheet.create({
1911
2054
  },
1912
2055
  statsContainer: {
1913
2056
  flexDirection: 'row',
1914
- paddingHorizontal: 24,
1915
- paddingVertical: 20,
1916
- marginHorizontal: 24,
1917
- marginTop: 20,
1918
- borderRadius: 16,
2057
+ paddingHorizontal: 14,
2058
+ paddingVertical: 10,
2059
+ marginHorizontal: 16,
2060
+ marginTop: 12,
2061
+ borderRadius: 10,
1919
2062
  borderWidth: 1
1920
2063
  },
1921
2064
  statItem: {
@@ -1924,31 +2067,31 @@ const styles = StyleSheet.create({
1924
2067
  paddingVertical: 4
1925
2068
  },
1926
2069
  statValue: {
1927
- fontSize: 28,
2070
+ fontSize: 20,
1928
2071
  fontWeight: '800',
1929
2072
  fontFamily: fontFamilies.phuduBold,
1930
2073
  letterSpacing: -0.5,
1931
- lineHeight: 32
2074
+ lineHeight: 24
1932
2075
  },
1933
2076
  statLabel: {
1934
- fontSize: 15,
2077
+ fontSize: 12,
1935
2078
  fontWeight: '500',
1936
2079
  fontFamily: fontFamilies.phuduMedium,
1937
- marginTop: 4,
1938
- letterSpacing: 0.3
2080
+ marginTop: 2,
2081
+ letterSpacing: 0.2
1939
2082
  },
1940
2083
  scrollView: {
1941
2084
  flex: 1
1942
2085
  },
1943
2086
  scrollContainer: {
1944
- padding: 20
2087
+ padding: 12
1945
2088
  },
1946
2089
  fileItem: {
1947
2090
  flexDirection: 'row',
1948
2091
  alignItems: 'center',
1949
- padding: 16,
1950
- marginBottom: 12,
1951
- borderRadius: 12,
2092
+ padding: 10,
2093
+ marginBottom: 8,
2094
+ borderRadius: 10,
1952
2095
  borderWidth: 1
1953
2096
  },
1954
2097
  fileContent: {
@@ -1964,15 +2107,15 @@ const styles = StyleSheet.create({
1964
2107
  marginRight: 12
1965
2108
  },
1966
2109
  filePreviewContainer: {
1967
- width: 60,
1968
- height: 60,
1969
- marginRight: 12
2110
+ width: 52,
2111
+ height: 52,
2112
+ marginRight: 10
1970
2113
  },
1971
2114
  filePreview: {
1972
2115
  width: '100%',
1973
2116
  height: '100%',
1974
2117
  borderRadius: 8,
1975
- backgroundColor: '#F5F5F5',
2118
+ backgroundColor: 'transparent',
1976
2119
  alignItems: 'center',
1977
2120
  justifyContent: 'center',
1978
2121
  overflow: 'hidden',
@@ -2015,7 +2158,7 @@ const styles = StyleSheet.create({
2015
2158
  bottom: 0,
2016
2159
  alignItems: 'center',
2017
2160
  justifyContent: 'center',
2018
- backgroundColor: '#F5F5F5',
2161
+ backgroundColor: 'transparent',
2019
2162
  borderRadius: 8
2020
2163
  },
2021
2164
  previewOverlay: {
@@ -2024,11 +2167,57 @@ const styles = StyleSheet.create({
2024
2167
  left: 0,
2025
2168
  right: 0,
2026
2169
  bottom: 0,
2027
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
2170
+ backgroundColor: 'rgba(0, 0, 0, 0.25)',
2028
2171
  alignItems: 'center',
2029
2172
  justifyContent: 'center',
2030
2173
  borderRadius: 8
2031
2174
  },
2175
+ groupedActions: {
2176
+ flexDirection: 'row',
2177
+ alignItems: 'center',
2178
+ gap: 6,
2179
+ marginLeft: 12
2180
+ },
2181
+ groupedActionBtn: {
2182
+ width: 34,
2183
+ height: 34,
2184
+ borderRadius: 8,
2185
+ alignItems: 'center',
2186
+ justifyContent: 'center'
2187
+ },
2188
+ groupedDescription: {
2189
+ fontSize: 12,
2190
+ lineHeight: 16,
2191
+ marginTop: 6
2192
+ },
2193
+ videoPreviewWrapper: {
2194
+ width: '100%',
2195
+ height: '100%',
2196
+ borderRadius: 8,
2197
+ overflow: 'hidden',
2198
+ backgroundColor: '#000000',
2199
+ alignItems: 'center',
2200
+ justifyContent: 'center'
2201
+ },
2202
+ videoPosterImage: {
2203
+ position: 'absolute',
2204
+ top: 0,
2205
+ left: 0,
2206
+ right: 0,
2207
+ bottom: 0,
2208
+ width: '100%',
2209
+ height: '100%'
2210
+ },
2211
+ videoOverlay: {
2212
+ position: 'absolute',
2213
+ top: 0,
2214
+ left: 0,
2215
+ right: 0,
2216
+ bottom: 0,
2217
+ alignItems: 'center',
2218
+ justifyContent: 'center',
2219
+ backgroundColor: 'rgba(0,0,0,0.25)'
2220
+ },
2032
2221
  fileInfo: {
2033
2222
  flex: 1
2034
2223
  },
@@ -2050,16 +2239,19 @@ const styles = StyleSheet.create({
2050
2239
  gap: 8
2051
2240
  },
2052
2241
  actionButton: {
2053
- width: 40,
2054
- height: 40,
2055
- borderRadius: 20,
2242
+ width: 36,
2243
+ height: 36,
2244
+ borderRadius: 18,
2056
2245
  alignItems: 'center',
2057
- justifyContent: 'center'
2246
+ justifyContent: 'center',
2247
+ borderWidth: 1,
2248
+ borderColor: '#E0E0E0',
2249
+ backgroundColor: 'transparent'
2058
2250
  },
2059
2251
  emptyState: {
2060
2252
  alignItems: 'center',
2061
- paddingVertical: 60,
2062
- paddingHorizontal: 40
2253
+ paddingVertical: 40,
2254
+ paddingHorizontal: 24
2063
2255
  },
2064
2256
  emptyStateTitle: {
2065
2257
  fontSize: 24,
@@ -2099,8 +2291,8 @@ const styles = StyleSheet.create({
2099
2291
  flexDirection: 'row',
2100
2292
  alignItems: 'center',
2101
2293
  justifyContent: 'space-between',
2102
- paddingHorizontal: 20,
2103
- paddingVertical: 16,
2294
+ paddingHorizontal: 16,
2295
+ paddingVertical: 12,
2104
2296
  borderBottomWidth: 1
2105
2297
  },
2106
2298
  modalCloseButton: {
@@ -2116,11 +2308,11 @@ const styles = StyleSheet.create({
2116
2308
  },
2117
2309
  modalContent: {
2118
2310
  flex: 1,
2119
- padding: 20
2311
+ padding: 16
2120
2312
  },
2121
2313
  fileDetailCard: {
2122
- padding: 24,
2123
- borderRadius: 16,
2314
+ padding: 18,
2315
+ borderRadius: 14,
2124
2316
  borderWidth: 1,
2125
2317
  alignItems: 'center'
2126
2318
  },
@@ -2182,25 +2374,25 @@ const styles = StyleSheet.create({
2182
2374
  left: 0,
2183
2375
  right: 0,
2184
2376
  bottom: 0,
2185
- backgroundColor: 'rgba(0, 0, 0, 0.8)',
2377
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
2186
2378
  justifyContent: 'center',
2187
2379
  alignItems: 'center',
2188
2380
  zIndex: 1000
2189
2381
  },
2190
2382
  dragDropContent: {
2191
2383
  alignItems: 'center',
2192
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
2193
- padding: 40,
2194
- borderRadius: 20,
2195
- borderWidth: 3,
2196
- borderColor: '#007AFF',
2384
+ backgroundColor: 'rgba(255, 255, 255, 0.95)',
2385
+ padding: 20,
2386
+ borderRadius: 14,
2387
+ borderWidth: 1,
2388
+ borderColor: '#66AFFF',
2197
2389
  borderStyle: 'dashed'
2198
2390
  },
2199
2391
  dragDropTitle: {
2200
- fontSize: 24,
2392
+ fontSize: 20,
2201
2393
  fontWeight: 'bold',
2202
- marginTop: 16,
2203
- marginBottom: 8
2394
+ marginTop: 12,
2395
+ marginBottom: 6
2204
2396
  },
2205
2397
  dragDropSubtitle: {
2206
2398
  fontSize: 16,
@@ -2213,8 +2405,8 @@ const styles = StyleSheet.create({
2213
2405
  fileViewerHeader: {
2214
2406
  flexDirection: 'row',
2215
2407
  alignItems: 'center',
2216
- paddingHorizontal: 20,
2217
- paddingVertical: 16,
2408
+ paddingHorizontal: 16,
2409
+ paddingVertical: 12,
2218
2410
  borderBottomWidth: 1
2219
2411
  },
2220
2412
  fileViewerTitleContainer: {
@@ -2242,7 +2434,7 @@ const styles = StyleSheet.create({
2242
2434
  },
2243
2435
  fileViewerContentContainer: {
2244
2436
  flexGrow: 1,
2245
- padding: 20
2437
+ padding: 14
2246
2438
  },
2247
2439
  fileViewerLoading: {
2248
2440
  flex: 1,
@@ -2260,10 +2452,10 @@ const styles = StyleSheet.create({
2260
2452
  },
2261
2453
  textContainer: {
2262
2454
  flex: 1,
2263
- borderRadius: 12,
2455
+ borderRadius: 10,
2264
2456
  borderWidth: 1,
2265
- padding: 16,
2266
- minHeight: 200,
2457
+ padding: 12,
2458
+ minHeight: 180,
2267
2459
  maxHeight: '80%'
2268
2460
  },
2269
2461
  textContent: {
@@ -2275,8 +2467,8 @@ const styles = StyleSheet.create({
2275
2467
  flex: 1,
2276
2468
  justifyContent: 'center',
2277
2469
  alignItems: 'center',
2278
- paddingVertical: 60,
2279
- paddingHorizontal: 40
2470
+ paddingVertical: 40,
2471
+ paddingHorizontal: 24
2280
2472
  },
2281
2473
  unsupportedFileTitle: {
2282
2474
  fontSize: 24,
@@ -2295,9 +2487,9 @@ const styles = StyleSheet.create({
2295
2487
  downloadButtonLarge: {
2296
2488
  flexDirection: 'row',
2297
2489
  alignItems: 'center',
2298
- paddingHorizontal: 24,
2299
- paddingVertical: 16,
2300
- borderRadius: 24,
2490
+ paddingHorizontal: 18,
2491
+ paddingVertical: 12,
2492
+ borderRadius: 20,
2301
2493
  gap: 8
2302
2494
  },
2303
2495
  downloadButtonText: {
@@ -2323,10 +2515,10 @@ const styles = StyleSheet.create({
2323
2515
  },
2324
2516
  // File Details in Viewer styles
2325
2517
  fileDetailsSection: {
2326
- margin: 16,
2518
+ margin: 12,
2327
2519
  marginTop: 0,
2328
- padding: 20,
2329
- borderRadius: 12,
2520
+ padding: 14,
2521
+ borderRadius: 10,
2330
2522
  borderWidth: 1
2331
2523
  },
2332
2524
  fileDetailsSectionTitle: {
@@ -2369,6 +2561,15 @@ const styles = StyleSheet.create({
2369
2561
  alignItems: 'center',
2370
2562
  gap: 16
2371
2563
  },
2564
+ controlsBar: {
2565
+ flexDirection: 'row',
2566
+ alignItems: 'center',
2567
+ justifyContent: 'space-between',
2568
+ paddingHorizontal: 16,
2569
+ paddingTop: 8,
2570
+ paddingBottom: 4,
2571
+ gap: 12
2572
+ },
2372
2573
  viewModeToggle: {
2373
2574
  flexDirection: 'row',
2374
2575
  borderRadius: 24,
@@ -2386,17 +2587,17 @@ const styles = StyleSheet.create({
2386
2587
  },
2387
2588
  // Photo Grid styles
2388
2589
  photoScrollContainer: {
2389
- padding: 16
2590
+ padding: 10
2390
2591
  },
2391
2592
  photoDateSection: {
2392
- marginBottom: 24
2593
+ marginBottom: 16
2393
2594
  },
2394
2595
  photoDateHeader: {
2395
- fontSize: 18,
2596
+ fontSize: 16,
2396
2597
  fontWeight: '600',
2397
2598
  fontFamily: fontFamilies.phuduSemiBold,
2398
- marginBottom: 12,
2399
- paddingHorizontal: 4
2599
+ marginBottom: 8,
2600
+ paddingHorizontal: 2
2400
2601
  },
2401
2602
  photoGrid: {
2402
2603
  flexDirection: 'row',
@@ -2448,7 +2649,7 @@ const styles = StyleSheet.create({
2448
2649
  position: 'relative',
2449
2650
  borderRadius: 6,
2450
2651
  overflow: 'hidden',
2451
- backgroundColor: '#F5F5F5'
2652
+ backgroundColor: 'transparent'
2452
2653
  },
2453
2654
  justifiedPhotoImage: {
2454
2655
  width: '100%',
@@ -2459,7 +2660,7 @@ const styles = StyleSheet.create({
2459
2660
  simplePhotoItem: {
2460
2661
  borderRadius: 8,
2461
2662
  overflow: 'hidden',
2462
- backgroundColor: '#F5F5F5'
2663
+ backgroundColor: 'transparent'
2463
2664
  },
2464
2665
  simplePhotoContainer: {
2465
2666
  width: '100%',