@oxyhq/services 5.5.9 → 5.6.0

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