@oxyhq/services 5.3.11 → 5.4.1

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 (213) hide show
  1. package/README.md +21 -0
  2. package/lib/commonjs/assets/assets/icons/OxyServices.tsx +67 -0
  3. package/lib/commonjs/assets/assets/icons/logo_OxyServices.svg +1 -0
  4. package/lib/commonjs/assets/icons/OxyServices.js +53 -0
  5. package/lib/commonjs/assets/icons/OxyServices.js.map +1 -0
  6. package/lib/commonjs/assets/icons/logo_OxyServices.svg +1 -0
  7. package/lib/commonjs/core/index.js +119 -23
  8. package/lib/commonjs/core/index.js.map +1 -1
  9. package/lib/commonjs/index.js +2 -0
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/lib/sonner.js +15 -11
  12. package/lib/commonjs/lib/sonner.js.map +1 -1
  13. package/lib/commonjs/node/index.js +2 -0
  14. package/lib/commonjs/node/index.js.map +1 -1
  15. package/lib/commonjs/ui/components/GroupedItem.js +109 -0
  16. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -0
  17. package/lib/commonjs/ui/components/GroupedSection.js +33 -0
  18. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -0
  19. package/lib/commonjs/ui/components/OxyProvider.js +95 -112
  20. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  21. package/lib/commonjs/ui/components/ProfileCard.js +124 -0
  22. package/lib/commonjs/ui/components/ProfileCard.js.map +1 -0
  23. package/lib/commonjs/ui/components/QuickActions.js +87 -0
  24. package/lib/commonjs/ui/components/QuickActions.js.map +1 -0
  25. package/lib/commonjs/ui/components/Section.js +36 -0
  26. package/lib/commonjs/ui/components/Section.js.map +1 -0
  27. package/lib/commonjs/ui/components/SectionTitle.js +35 -0
  28. package/lib/commonjs/ui/components/SectionTitle.js.map +1 -0
  29. package/lib/commonjs/ui/components/bottomSheet/index.js +6 -6
  30. package/lib/commonjs/ui/components/index.js +97 -0
  31. package/lib/commonjs/ui/components/index.js.map +1 -0
  32. package/lib/commonjs/ui/navigation/OxyRouter.js +20 -3
  33. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  34. package/lib/commonjs/ui/screens/AccountCenterScreen.js +190 -207
  35. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  36. package/lib/commonjs/ui/screens/AccountManagementDemo.js +299 -0
  37. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -0
  38. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +669 -401
  39. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  40. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +695 -498
  41. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  42. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +451 -488
  43. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  44. package/lib/commonjs/ui/screens/AppInfoScreen.js +498 -185
  45. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  46. package/lib/commonjs/ui/screens/BillingManagementScreen.js +636 -0
  47. package/lib/commonjs/ui/screens/BillingManagementScreen.js.map +1 -0
  48. package/lib/commonjs/ui/screens/FileManagementScreen.js +2497 -0
  49. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -0
  50. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +1620 -0
  51. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -0
  52. package/lib/commonjs/ui/screens/ProfileScreen.js +117 -13
  53. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  54. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SignInScreen.js +1 -1
  56. package/lib/commonjs/ui/screens/SignUpScreen.js +1 -1
  57. package/lib/commonjs/utils/polyfills.js +42 -0
  58. package/lib/commonjs/utils/polyfills.js.map +1 -0
  59. package/lib/module/assets/assets/icons/OxyServices.tsx +67 -0
  60. package/lib/module/assets/assets/icons/logo_OxyServices.svg +1 -0
  61. package/lib/module/assets/icons/OxyServices.js +46 -0
  62. package/lib/module/assets/icons/OxyServices.js.map +1 -0
  63. package/lib/module/assets/icons/logo_OxyServices.svg +1 -0
  64. package/lib/module/core/index.js +119 -23
  65. package/lib/module/core/index.js.map +1 -1
  66. package/lib/module/index.js +3 -0
  67. package/lib/module/index.js.map +1 -1
  68. package/lib/module/lib/sonner.js +13 -1
  69. package/lib/module/lib/sonner.js.map +1 -1
  70. package/lib/module/node/index.js +3 -0
  71. package/lib/module/node/index.js.map +1 -1
  72. package/lib/module/ui/components/GroupedItem.js +104 -0
  73. package/lib/module/ui/components/GroupedItem.js.map +1 -0
  74. package/lib/module/ui/components/GroupedSection.js +28 -0
  75. package/lib/module/ui/components/GroupedSection.js.map +1 -0
  76. package/lib/module/ui/components/OxyProvider.js +97 -114
  77. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  78. package/lib/module/ui/components/ProfileCard.js +119 -0
  79. package/lib/module/ui/components/ProfileCard.js.map +1 -0
  80. package/lib/module/ui/components/QuickActions.js +82 -0
  81. package/lib/module/ui/components/QuickActions.js.map +1 -0
  82. package/lib/module/ui/components/Section.js +31 -0
  83. package/lib/module/ui/components/Section.js.map +1 -0
  84. package/lib/module/ui/components/SectionTitle.js +30 -0
  85. package/lib/module/ui/components/SectionTitle.js.map +1 -0
  86. package/lib/module/ui/components/bottomSheet/index.js +2 -5
  87. package/lib/module/ui/components/bottomSheet/index.js.map +1 -1
  88. package/lib/module/ui/components/index.js +18 -0
  89. package/lib/module/ui/components/index.js.map +1 -0
  90. package/lib/module/ui/navigation/OxyRouter.js +20 -3
  91. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  92. package/lib/module/ui/screens/AccountCenterScreen.js +191 -208
  93. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  94. package/lib/module/ui/screens/AccountManagementDemo.js +296 -0
  95. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -0
  96. package/lib/module/ui/screens/AccountOverviewScreen.js +671 -403
  97. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  98. package/lib/module/ui/screens/AccountSettingsScreen.js +698 -501
  99. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  100. package/lib/module/ui/screens/AccountSwitcherScreen.js +450 -488
  101. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  102. package/lib/module/ui/screens/AppInfoScreen.js +498 -186
  103. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  104. package/lib/module/ui/screens/BillingManagementScreen.js +631 -0
  105. package/lib/module/ui/screens/BillingManagementScreen.js.map +1 -0
  106. package/lib/module/ui/screens/FileManagementScreen.js +2492 -0
  107. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -0
  108. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +1615 -0
  109. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -0
  110. package/lib/module/ui/screens/ProfileScreen.js +118 -14
  111. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  112. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  113. package/lib/module/ui/screens/SignInScreen.js +1 -1
  114. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  115. package/lib/module/ui/screens/SignUpScreen.js +1 -1
  116. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  117. package/lib/module/utils/polyfills.js +36 -0
  118. package/lib/module/utils/polyfills.js.map +1 -0
  119. package/lib/typescript/assets/icons/OxyServices.d.ts +29 -0
  120. package/lib/typescript/assets/icons/OxyServices.d.ts.map +1 -0
  121. package/lib/typescript/core/index.d.ts +26 -1
  122. package/lib/typescript/core/index.d.ts.map +1 -1
  123. package/lib/typescript/index.d.ts +1 -0
  124. package/lib/typescript/index.d.ts.map +1 -1
  125. package/lib/typescript/lib/sonner.d.ts +5 -1
  126. package/lib/typescript/lib/sonner.d.ts.map +1 -1
  127. package/lib/typescript/models/interfaces.d.ts +1 -2
  128. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  129. package/lib/typescript/node/index.d.ts +1 -0
  130. package/lib/typescript/node/index.d.ts.map +1 -1
  131. package/lib/typescript/ui/components/GroupedItem.d.ts +17 -0
  132. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -0
  133. package/lib/typescript/ui/components/GroupedSection.d.ts +19 -0
  134. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -0
  135. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  136. package/lib/typescript/ui/components/ProfileCard.d.ts +20 -0
  137. package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -0
  138. package/lib/typescript/ui/components/QuickActions.d.ts +15 -0
  139. package/lib/typescript/ui/components/QuickActions.d.ts.map +1 -0
  140. package/lib/typescript/ui/components/Section.d.ts +11 -0
  141. package/lib/typescript/ui/components/Section.d.ts.map +1 -0
  142. package/lib/typescript/ui/components/SectionTitle.d.ts +9 -0
  143. package/lib/typescript/ui/components/SectionTitle.d.ts.map +1 -0
  144. package/lib/typescript/ui/components/bottomSheet/index.d.ts +3 -2
  145. package/lib/typescript/ui/components/bottomSheet/index.d.ts.map +1 -1
  146. package/lib/typescript/ui/components/index.d.ts +13 -0
  147. package/lib/typescript/ui/components/index.d.ts.map +1 -0
  148. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
  149. package/lib/typescript/ui/navigation/types.d.ts +8 -0
  150. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  151. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  152. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts +8 -0
  153. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts.map +1 -0
  154. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  155. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +1 -4
  156. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  157. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  158. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  159. package/lib/typescript/ui/screens/BillingManagementScreen.d.ts +5 -0
  160. package/lib/typescript/ui/screens/BillingManagementScreen.d.ts.map +1 -0
  161. package/lib/typescript/ui/screens/FileManagementScreen.d.ts +8 -0
  162. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -0
  163. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts +5 -0
  164. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -0
  165. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  166. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  167. package/lib/typescript/utils/polyfills.d.ts +6 -0
  168. package/lib/typescript/utils/polyfills.d.ts.map +1 -0
  169. package/package.json +11 -3
  170. package/src/__tests__/polyfills.test.ts +30 -0
  171. package/src/__tests__/setup.ts +43 -0
  172. package/src/__tests__/ui/screens/AccountSettingsScreen.test.tsx +8 -8
  173. package/src/assets/icons/OxyServices.tsx +67 -0
  174. package/src/assets/icons/logo_OxyServices.svg +1 -0
  175. package/src/core/index.ts +127 -19
  176. package/src/index.ts +3 -0
  177. package/src/lib/sonner.ts +10 -1
  178. package/src/models/interfaces.ts +1 -2
  179. package/src/node/index.ts +3 -0
  180. package/src/ui/components/GroupedItem.tsx +118 -0
  181. package/src/ui/components/GroupedSection.tsx +45 -0
  182. package/src/ui/components/OxyProvider.tsx +95 -120
  183. package/src/ui/components/ProfileCard.tsx +129 -0
  184. package/src/ui/components/QuickActions.tsx +90 -0
  185. package/src/ui/components/Section.tsx +37 -0
  186. package/src/ui/components/SectionTitle.tsx +31 -0
  187. package/src/ui/components/bottomSheet/index.tsx +13 -11
  188. package/src/ui/components/index.ts +15 -0
  189. package/src/ui/navigation/OxyRouter.tsx +20 -3
  190. package/src/ui/navigation/types.ts +10 -1
  191. package/src/ui/screens/AccountCenterScreen.tsx +188 -159
  192. package/src/ui/screens/AccountManagementDemo.tsx +297 -0
  193. package/src/ui/screens/AccountOverviewScreen.tsx +474 -310
  194. package/src/ui/screens/AccountSettingsScreen.tsx +648 -463
  195. package/src/ui/screens/AccountSwitcherScreen.tsx +385 -449
  196. package/src/ui/screens/AppInfoScreen.tsx +571 -140
  197. package/src/ui/screens/BillingManagementScreen.tsx +589 -0
  198. package/src/ui/screens/FileManagementScreen.tsx +2513 -0
  199. package/src/ui/screens/PremiumSubscriptionScreen.tsx +1628 -0
  200. package/src/ui/screens/ProfileScreen.tsx +101 -7
  201. package/src/ui/screens/SessionManagementScreen.tsx +1 -0
  202. package/src/ui/screens/SignInScreen.tsx +1 -1
  203. package/src/ui/screens/SignUpScreen.tsx +1 -1
  204. package/src/utils/polyfills.ts +34 -0
  205. package/lib/commonjs/lib/sonner.web.js +0 -17
  206. package/lib/commonjs/lib/sonner.web.js.map +0 -1
  207. package/lib/module/lib/sonner.web.js +0 -4
  208. package/lib/module/lib/sonner.web.js.map +0 -1
  209. package/lib/typescript/__tests__/ui/screens/AccountSettingsScreen.test.d.ts +0 -2
  210. package/lib/typescript/__tests__/ui/screens/AccountSettingsScreen.test.d.ts.map +0 -1
  211. package/lib/typescript/lib/sonner.web.d.ts +0 -2
  212. package/lib/typescript/lib/sonner.web.d.ts.map +0 -1
  213. package/src/lib/sonner.web.ts +0 -1
@@ -0,0 +1,2497 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _OxyContext = require("../context/OxyContext");
10
+ var _fonts = require("../styles/fonts");
11
+ var _sonner = require("../../lib/sonner");
12
+ var _vectorIcons = require("@expo/vector-icons");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
15
+ const FileManagementScreen = ({
16
+ onClose,
17
+ theme,
18
+ goBack,
19
+ navigate,
20
+ userId,
21
+ containerWidth = 400 // Fallback for when not provided by the router
22
+ }) => {
23
+ const {
24
+ user,
25
+ oxyServices
26
+ } = (0, _OxyContext.useOxy)();
27
+
28
+ // Debug: log the actual container width
29
+ (0, _react.useEffect)(() => {
30
+ console.log('[FileManagementScreen] Container width (full):', containerWidth);
31
+ // Padding structure:
32
+ // - containerWidth = full bottom sheet container width (measured from OxyProvider)
33
+ // - photoScrollContainer adds padding: 16 (32px total horizontal padding)
34
+ // - Available content width = containerWidth - 32
35
+ const availableContentWidth = containerWidth - 32;
36
+ console.log('[FileManagementScreen] Available content width:', availableContentWidth);
37
+ console.log('[FileManagementScreen] Spacing fix applied: 4px uniform gap both horizontal and vertical');
38
+ }, [containerWidth]);
39
+ const [files, setFiles] = (0, _react.useState)([]);
40
+ const [loading, setLoading] = (0, _react.useState)(true);
41
+ const [refreshing, setRefreshing] = (0, _react.useState)(false);
42
+ const [uploading, setUploading] = (0, _react.useState)(false);
43
+ const [uploadProgress, setUploadProgress] = (0, _react.useState)(null);
44
+ const [deleting, setDeleting] = (0, _react.useState)(null);
45
+ const [selectedFile, setSelectedFile] = (0, _react.useState)(null);
46
+ const [showFileDetails, setShowFileDetails] = (0, _react.useState)(false);
47
+ const [openedFile, setOpenedFile] = (0, _react.useState)(null);
48
+ const [fileContent, setFileContent] = (0, _react.useState)(null);
49
+ const [loadingFileContent, setLoadingFileContent] = (0, _react.useState)(false);
50
+ const [showFileDetailsInViewer, setShowFileDetailsInViewer] = (0, _react.useState)(false);
51
+ const [viewMode, setViewMode] = (0, _react.useState)('all');
52
+ const [searchQuery, setSearchQuery] = (0, _react.useState)('');
53
+ const [filteredFiles, setFilteredFiles] = (0, _react.useState)([]);
54
+ const [isDragging, setIsDragging] = (0, _react.useState)(false);
55
+ const [photoDimensions, setPhotoDimensions] = (0, _react.useState)({});
56
+ const [loadingDimensions, setLoadingDimensions] = (0, _react.useState)(false);
57
+ const [hoveredPreview, setHoveredPreview] = (0, _react.useState)(null);
58
+ const isDarkTheme = theme === 'dark';
59
+ const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
60
+ const backgroundColor = isDarkTheme ? '#121212' : '#f2f2f2';
61
+ const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#FFFFFF';
62
+ const borderColor = isDarkTheme ? '#444444' : '#E0E0E0';
63
+ const primaryColor = '#007AFF';
64
+ const dangerColor = '#FF3B30';
65
+ const successColor = '#34C759';
66
+ const targetUserId = userId || user?.id;
67
+ const loadFiles = (0, _react.useCallback)(async (isRefresh = false) => {
68
+ if (!targetUserId) return;
69
+ try {
70
+ if (isRefresh) {
71
+ setRefreshing(true);
72
+ } else {
73
+ setLoading(true);
74
+ }
75
+ const response = await oxyServices.listUserFiles(targetUserId);
76
+ setFiles(response.files || []);
77
+ } catch (error) {
78
+ console.error('Failed to load files:', error);
79
+ _sonner.toast.error(error.message || 'Failed to load files');
80
+ } finally {
81
+ setLoading(false);
82
+ setRefreshing(false);
83
+ }
84
+ }, [targetUserId, oxyServices]);
85
+
86
+ // Filter files based on search query and view mode
87
+ (0, _react.useEffect)(() => {
88
+ let filteredByMode = files;
89
+
90
+ // Filter by view mode first
91
+ if (viewMode === 'photos') {
92
+ filteredByMode = files.filter(file => file.contentType.startsWith('image/'));
93
+ }
94
+
95
+ // Then filter by search query
96
+ if (!searchQuery.trim()) {
97
+ setFilteredFiles(filteredByMode);
98
+ } else {
99
+ const query = searchQuery.toLowerCase();
100
+ const filtered = filteredByMode.filter(file => file.filename.toLowerCase().includes(query) || file.contentType.toLowerCase().includes(query) || file.metadata?.description && file.metadata.description.toLowerCase().includes(query));
101
+ setFilteredFiles(filtered);
102
+ }
103
+ }, [files, searchQuery, viewMode]);
104
+
105
+ // Load photo dimensions for justified grid
106
+ const loadPhotoDimensions = (0, _react.useCallback)(async photos => {
107
+ if (photos.length === 0) return;
108
+ setLoadingDimensions(true);
109
+ const newDimensions = {
110
+ ...photoDimensions
111
+ };
112
+ let hasNewDimensions = false;
113
+
114
+ // Only load dimensions for photos we don't have yet
115
+ const photosToLoad = photos.filter(photo => !newDimensions[photo.id]);
116
+ if (photosToLoad.length === 0) {
117
+ setLoadingDimensions(false);
118
+ return;
119
+ }
120
+ try {
121
+ await Promise.all(photosToLoad.map(async photo => {
122
+ try {
123
+ const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
124
+ if (_reactNative.Platform.OS === 'web') {
125
+ const img = new window.Image();
126
+ await new Promise((resolve, reject) => {
127
+ img.onload = () => {
128
+ newDimensions[photo.id] = {
129
+ width: img.naturalWidth,
130
+ height: img.naturalHeight
131
+ };
132
+ hasNewDimensions = true;
133
+ resolve();
134
+ };
135
+ img.onerror = () => {
136
+ // Fallback dimensions for failed loads
137
+ newDimensions[photo.id] = {
138
+ width: 1,
139
+ height: 1
140
+ };
141
+ hasNewDimensions = true;
142
+ resolve();
143
+ };
144
+ img.src = downloadUrl;
145
+ });
146
+ } else {
147
+ // For mobile, use Image.getSize from react-native
148
+ await new Promise(resolve => {
149
+ _reactNative.Image.getSize(downloadUrl, (width, height) => {
150
+ newDimensions[photo.id] = {
151
+ width,
152
+ height
153
+ };
154
+ hasNewDimensions = true;
155
+ resolve();
156
+ }, () => {
157
+ // Fallback dimensions
158
+ newDimensions[photo.id] = {
159
+ width: 1,
160
+ height: 1
161
+ };
162
+ hasNewDimensions = true;
163
+ resolve();
164
+ });
165
+ });
166
+ }
167
+ } catch (error) {
168
+ // Fallback dimensions for any errors
169
+ newDimensions[photo.id] = {
170
+ width: 1,
171
+ height: 1
172
+ };
173
+ hasNewDimensions = true;
174
+ }
175
+ }));
176
+ if (hasNewDimensions) {
177
+ setPhotoDimensions(newDimensions);
178
+ }
179
+ } catch (error) {
180
+ console.error('Error loading photo dimensions:', error);
181
+ } finally {
182
+ setLoadingDimensions(false);
183
+ }
184
+ }, [oxyServices, photoDimensions]);
185
+
186
+ // Create justified rows from photos with responsive algorithm
187
+ const createJustifiedRows = (0, _react.useCallback)(photos => {
188
+ if (photos.length === 0) return [];
189
+ const rows = [];
190
+ const photosPerRow = 3; // Fixed 3 photos per row for consistency
191
+
192
+ for (let i = 0; i < photos.length; i += photosPerRow) {
193
+ const rowPhotos = photos.slice(i, i + photosPerRow);
194
+ rows.push(rowPhotos);
195
+ }
196
+ return rows;
197
+ }, []);
198
+ const processFileUploads = async selectedFiles => {
199
+ if (selectedFiles.length === 0) return;
200
+ try {
201
+ // Show initial progress
202
+ setUploadProgress({
203
+ current: 0,
204
+ total: selectedFiles.length
205
+ });
206
+
207
+ // Validate file sizes (example: 50MB limit per file)
208
+ const maxSize = 50 * 1024 * 1024; // 50MB
209
+ const oversizedFiles = selectedFiles.filter(file => file.size > maxSize);
210
+ if (oversizedFiles.length > 0) {
211
+ const fileList = oversizedFiles.map(f => f.name).join('\n');
212
+ window.alert(`File Size Limit\n\nThe following files are too large (max 50MB):\n${fileList}`);
213
+ return;
214
+ }
215
+
216
+ // Option 1: Bulk upload (faster, all-or-nothing) for 5 or fewer files
217
+ if (selectedFiles.length <= 5) {
218
+ const filenames = selectedFiles.map(f => f.name);
219
+ const response = await oxyServices.uploadFiles(selectedFiles, filenames, {
220
+ userId: targetUserId,
221
+ uploadDate: new Date().toISOString()
222
+ });
223
+ _sonner.toast.success(`${response.files.length} file(s) uploaded successfully`);
224
+ // Small delay to ensure backend processing is complete
225
+ setTimeout(async () => {
226
+ await loadFiles();
227
+ }, 500);
228
+ } else {
229
+ // Option 2: Individual uploads for better progress and error handling
230
+ let successCount = 0;
231
+ let failureCount = 0;
232
+ const errors = [];
233
+ for (let i = 0; i < selectedFiles.length; i++) {
234
+ const file = selectedFiles[i];
235
+ setUploadProgress({
236
+ current: i + 1,
237
+ total: selectedFiles.length
238
+ });
239
+ try {
240
+ await oxyServices.uploadFile(file, file.name, {
241
+ userId: targetUserId,
242
+ uploadDate: new Date().toISOString()
243
+ });
244
+ successCount++;
245
+ } catch (error) {
246
+ failureCount++;
247
+ errors.push(`${file.name}: ${error.message || 'Upload failed'}`);
248
+ }
249
+ }
250
+
251
+ // Show results summary
252
+ if (successCount > 0) {
253
+ _sonner.toast.success(`${successCount} file(s) uploaded successfully`);
254
+ }
255
+ if (failureCount > 0) {
256
+ const errorMessage = `${failureCount} file(s) failed to upload${errors.length > 0 ? ':\n' + errors.slice(0, 3).join('\n') + (errors.length > 3 ? '\n...' : '') : ''}`;
257
+ _sonner.toast.error(errorMessage);
258
+ }
259
+
260
+ // Small delay to ensure backend processing is complete
261
+ setTimeout(async () => {
262
+ await loadFiles();
263
+ }, 500);
264
+ }
265
+ } catch (error) {
266
+ console.error('Upload error:', error);
267
+ _sonner.toast.error(error.message || 'Failed to upload files');
268
+ } finally {
269
+ setUploadProgress(null);
270
+ }
271
+ };
272
+ const handleFileUpload = async () => {
273
+ try {
274
+ setUploading(true);
275
+ setUploadProgress(null);
276
+ if (_reactNative.Platform.OS === 'web') {
277
+ // Web file picker implementation
278
+ const input = document.createElement('input');
279
+ input.type = 'file';
280
+ input.multiple = true;
281
+ input.accept = '*/*';
282
+ input.onchange = async e => {
283
+ const selectedFiles = Array.from(e.target.files);
284
+ await processFileUploads(selectedFiles);
285
+ };
286
+ input.click();
287
+ } else {
288
+ // Mobile - show info that file picker can be added
289
+ const installCommand = 'npm install expo-document-picker';
290
+ 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.`;
291
+ if (window.confirm(`${message}\n\nWould you like to copy the install command?`)) {
292
+ _sonner.toast.info(`Install: ${installCommand}`);
293
+ } else {
294
+ _sonner.toast.info('Mobile file upload requires expo-document-picker');
295
+ }
296
+ }
297
+ } catch (error) {
298
+ _sonner.toast.error(error.message || 'Failed to upload file');
299
+ } finally {
300
+ setUploading(false);
301
+ setUploadProgress(null);
302
+ }
303
+ };
304
+ const handleFileDelete = async (fileId, filename) => {
305
+ // Use web-compatible confirmation dialog
306
+ const confirmed = window.confirm(`Are you sure you want to delete "${filename}"? This action cannot be undone.`);
307
+ if (!confirmed) {
308
+ console.log('Delete cancelled by user');
309
+ return;
310
+ }
311
+ try {
312
+ console.log('Deleting file:', {
313
+ fileId,
314
+ filename
315
+ });
316
+ console.log('Target user ID:', targetUserId);
317
+ console.log('Current user ID:', user?.id);
318
+ setDeleting(fileId);
319
+ const result = await oxyServices.deleteFile(fileId);
320
+ console.log('Delete result:', result);
321
+ _sonner.toast.success('File deleted successfully');
322
+
323
+ // Reload files after successful deletion
324
+ setTimeout(async () => {
325
+ await loadFiles();
326
+ }, 500);
327
+ } catch (error) {
328
+ console.error('Delete error:', error);
329
+ console.error('Error details:', error.response?.data || error.message);
330
+
331
+ // Provide specific error messages
332
+ if (error.message?.includes('File not found') || error.message?.includes('404')) {
333
+ _sonner.toast.error('File not found. It may have already been deleted.');
334
+ // Still reload files to refresh the list
335
+ setTimeout(async () => {
336
+ await loadFiles();
337
+ }, 500);
338
+ } else if (error.message?.includes('permission') || error.message?.includes('403')) {
339
+ _sonner.toast.error('You do not have permission to delete this file.');
340
+ } else {
341
+ _sonner.toast.error(error.message || 'Failed to delete file');
342
+ }
343
+ } finally {
344
+ setDeleting(null);
345
+ }
346
+ };
347
+
348
+ // Drag and drop handlers for web
349
+ const handleDragOver = e => {
350
+ if (_reactNative.Platform.OS === 'web' && user?.id === targetUserId) {
351
+ e.preventDefault();
352
+ setIsDragging(true);
353
+ }
354
+ };
355
+ const handleDragLeave = e => {
356
+ if (_reactNative.Platform.OS === 'web') {
357
+ e.preventDefault();
358
+ setIsDragging(false);
359
+ }
360
+ };
361
+ const handleDrop = async e => {
362
+ if (_reactNative.Platform.OS === 'web' && user?.id === targetUserId) {
363
+ e.preventDefault();
364
+ setIsDragging(false);
365
+ setUploading(true);
366
+ try {
367
+ const files = Array.from(e.dataTransfer.files);
368
+ await processFileUploads(files);
369
+ } catch (error) {
370
+ _sonner.toast.error(error.message || 'Failed to upload files');
371
+ } finally {
372
+ setUploading(false);
373
+ }
374
+ }
375
+ };
376
+ const handleFileDownload = async (fileId, filename) => {
377
+ try {
378
+ if (_reactNative.Platform.OS === 'web') {
379
+ console.log('Downloading file:', {
380
+ fileId,
381
+ filename
382
+ });
383
+
384
+ // Use the public download URL method
385
+ const downloadUrl = oxyServices.getFileDownloadUrl(fileId);
386
+ console.log('Download URL:', downloadUrl);
387
+ try {
388
+ // Method 1: Try simple link download first
389
+ const link = document.createElement('a');
390
+ link.href = downloadUrl;
391
+ link.download = filename;
392
+ link.target = '_blank';
393
+ document.body.appendChild(link);
394
+ link.click();
395
+ document.body.removeChild(link);
396
+ _sonner.toast.success('File download started');
397
+ } catch (linkError) {
398
+ console.warn('Link download failed, trying fetch method:', linkError);
399
+
400
+ // Method 2: Fallback to fetch download
401
+ const response = await fetch(downloadUrl);
402
+ if (!response.ok) {
403
+ if (response.status === 404) {
404
+ throw new Error('File not found. It may have been deleted.');
405
+ } else {
406
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
407
+ }
408
+ }
409
+ const blob = await response.blob();
410
+ const url = window.URL.createObjectURL(blob);
411
+ const link = document.createElement('a');
412
+ link.href = url;
413
+ link.download = filename;
414
+ document.body.appendChild(link);
415
+ link.click();
416
+ document.body.removeChild(link);
417
+
418
+ // Clean up the blob URL
419
+ window.URL.revokeObjectURL(url);
420
+ _sonner.toast.success('File downloaded successfully');
421
+ }
422
+ } else {
423
+ _sonner.toast.info('File download not implemented for mobile yet');
424
+ }
425
+ } catch (error) {
426
+ console.error('Download error:', error);
427
+ _sonner.toast.error(error.message || 'Failed to download file');
428
+ }
429
+ };
430
+ const formatFileSize = bytes => {
431
+ if (bytes === 0) return '0 Bytes';
432
+ const k = 1024;
433
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
434
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
435
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
436
+ };
437
+ const getFileIcon = contentType => {
438
+ if (contentType.startsWith('image/')) return 'image';
439
+ if (contentType.startsWith('video/')) return 'videocam';
440
+ if (contentType.startsWith('audio/')) return 'musical-notes';
441
+ if (contentType.includes('pdf')) return 'document-text';
442
+ if (contentType.includes('word') || contentType.includes('doc')) return 'document';
443
+ if (contentType.includes('excel') || contentType.includes('sheet')) return 'grid';
444
+ if (contentType.includes('zip') || contentType.includes('archive')) return 'archive';
445
+ return 'document-outline';
446
+ };
447
+ const handleFileOpen = async file => {
448
+ try {
449
+ setLoadingFileContent(true);
450
+ setOpenedFile(file);
451
+
452
+ // For text files, images, and other viewable content, try to load the content
453
+ if (file.contentType.startsWith('text/') || file.contentType.includes('json') || file.contentType.includes('xml') || file.contentType.includes('javascript') || file.contentType.includes('typescript') || file.contentType.startsWith('image/') || file.contentType.includes('pdf') || file.contentType.startsWith('video/') || file.contentType.startsWith('audio/')) {
454
+ try {
455
+ const downloadUrl = oxyServices.getFileDownloadUrl(file.id);
456
+ const response = await fetch(downloadUrl);
457
+ if (response.ok) {
458
+ if (file.contentType.startsWith('image/') || file.contentType.includes('pdf') || file.contentType.startsWith('video/') || file.contentType.startsWith('audio/')) {
459
+ // For images, PDFs, videos, and audio, we'll use the URL directly
460
+ setFileContent(downloadUrl);
461
+ } else {
462
+ // For text files, get the content
463
+ const content = await response.text();
464
+ setFileContent(content);
465
+ }
466
+ } else {
467
+ if (response.status === 404) {
468
+ _sonner.toast.error('File not found. It may have been deleted.');
469
+ } else {
470
+ _sonner.toast.error(`Failed to load file: ${response.status} ${response.statusText}`);
471
+ }
472
+ setFileContent(null);
473
+ }
474
+ } catch (error) {
475
+ console.error('Failed to load file content:', error);
476
+ if (error.message?.includes('404') || error.message?.includes('not found')) {
477
+ _sonner.toast.error('File not found. It may have been deleted.');
478
+ } else {
479
+ _sonner.toast.error('Failed to load file content');
480
+ }
481
+ setFileContent(null);
482
+ }
483
+ } else {
484
+ // For non-viewable files, don't load content
485
+ setFileContent(null);
486
+ }
487
+ } catch (error) {
488
+ console.error('Failed to open file:', error);
489
+ _sonner.toast.error(error.message || 'Failed to open file');
490
+ } finally {
491
+ setLoadingFileContent(false);
492
+ }
493
+ };
494
+ const handleCloseFile = () => {
495
+ setOpenedFile(null);
496
+ setFileContent(null);
497
+ setShowFileDetailsInViewer(false);
498
+ // Don't reset view mode when closing a file
499
+ };
500
+ const showFileDetailsModal = file => {
501
+ setSelectedFile(file);
502
+ setShowFileDetails(true);
503
+ };
504
+ const renderSimplePhotoItem = (0, _react.useCallback)((photo, index) => {
505
+ const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
506
+
507
+ // Calculate photo item width based on actual container size from bottom sheet
508
+ let itemsPerRow = 3; // Default for mobile
509
+ if (containerWidth > 768) itemsPerRow = 4; // Desktop/tablet
510
+ else if (containerWidth > 480) itemsPerRow = 3; // Large mobile
511
+
512
+ // Account for the photoScrollContainer padding (16px on each side = 32px total)
513
+ const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
514
+ const gaps = (itemsPerRow - 1) * 4; // Gap between items (4px)
515
+ const availableWidth = containerWidth - scrollContainerPadding;
516
+ const itemWidth = (availableWidth - gaps) / itemsPerRow;
517
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
518
+ style: [styles.simplePhotoItem, {
519
+ width: itemWidth,
520
+ height: itemWidth,
521
+ marginRight: (index + 1) % itemsPerRow === 0 ? 0 : 4
522
+ }],
523
+ onPress: () => handleFileOpen(photo),
524
+ activeOpacity: 0.8,
525
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
526
+ style: styles.simplePhotoContainer,
527
+ children: _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
528
+ src: downloadUrl,
529
+ alt: photo.filename,
530
+ style: {
531
+ width: '100%',
532
+ height: '100%',
533
+ objectFit: 'cover',
534
+ borderRadius: 8,
535
+ transition: 'transform 0.2s ease'
536
+ },
537
+ loading: "lazy",
538
+ onError: e => {
539
+ console.error('Photo failed to load:', e);
540
+ },
541
+ onMouseEnter: e => {
542
+ e.currentTarget.style.transform = 'scale(1.05)';
543
+ },
544
+ onMouseLeave: e => {
545
+ e.currentTarget.style.transform = 'scale(1)';
546
+ }
547
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
548
+ source: {
549
+ uri: downloadUrl
550
+ },
551
+ style: styles.simplePhotoImage,
552
+ resizeMode: "cover",
553
+ onError: e => {
554
+ console.error('Photo failed to load:', e);
555
+ }
556
+ })
557
+ })
558
+ }, photo.id);
559
+ }, [oxyServices, containerWidth]);
560
+ const renderJustifiedPhotoItem = (0, _react.useCallback)((photo, width, height, isLast) => {
561
+ const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
562
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
563
+ style: [styles.justifiedPhotoItem, {
564
+ width,
565
+ height
566
+ }],
567
+ onPress: () => handleFileOpen(photo),
568
+ activeOpacity: 0.8,
569
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
570
+ style: styles.justifiedPhotoContainer,
571
+ children: _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
572
+ src: downloadUrl,
573
+ alt: photo.filename,
574
+ style: {
575
+ width: '100%',
576
+ height: '100%',
577
+ objectFit: 'cover',
578
+ borderRadius: 6,
579
+ transition: 'transform 0.2s ease, box-shadow 0.2s ease'
580
+ },
581
+ loading: "lazy",
582
+ onError: e => {
583
+ console.error('Photo failed to load:', e);
584
+ },
585
+ onMouseEnter: e => {
586
+ e.currentTarget.style.transform = 'scale(1.02)';
587
+ e.currentTarget.style.boxShadow = '0 8px 25px rgba(0,0,0,0.15)';
588
+ e.currentTarget.style.zIndex = '10';
589
+ },
590
+ onMouseLeave: e => {
591
+ e.currentTarget.style.transform = 'scale(1)';
592
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
593
+ e.currentTarget.style.zIndex = '1';
594
+ }
595
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
596
+ source: {
597
+ uri: downloadUrl
598
+ },
599
+ style: styles.justifiedPhotoImage,
600
+ resizeMode: "cover",
601
+ onError: e => {
602
+ console.error('Photo failed to load:', e);
603
+ }
604
+ })
605
+ })
606
+ }, photo.id);
607
+ }, [oxyServices]);
608
+ (0, _react.useEffect)(() => {
609
+ loadFiles();
610
+ }, [loadFiles]);
611
+ const renderFileItem = file => {
612
+ const isImage = file.contentType.startsWith('image/');
613
+ const isPDF = file.contentType.includes('pdf');
614
+ const isVideo = file.contentType.startsWith('video/');
615
+ const isAudio = file.contentType.startsWith('audio/');
616
+ const hasPreview = isImage || isPDF || isVideo;
617
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
618
+ style: [styles.fileItem, {
619
+ backgroundColor: secondaryBackgroundColor,
620
+ borderColor
621
+ }],
622
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
623
+ style: styles.fileContent,
624
+ onPress: () => handleFileOpen(file),
625
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
626
+ style: styles.filePreviewContainer,
627
+ children: hasPreview ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
628
+ style: styles.filePreview,
629
+ ...(_reactNative.Platform.OS === 'web' && {
630
+ onMouseEnter: () => setHoveredPreview(file.id),
631
+ onMouseLeave: () => setHoveredPreview(null)
632
+ }),
633
+ children: [isImage && (_reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
634
+ src: oxyServices.getFileDownloadUrl(file.id),
635
+ style: {
636
+ width: '100%',
637
+ height: '100%',
638
+ objectFit: 'cover',
639
+ borderRadius: 8,
640
+ transition: 'transform 0.2s ease',
641
+ transform: hoveredPreview === file.id ? 'scale(1.05)' : 'scale(1)'
642
+ },
643
+ onError: e => {
644
+ // Show fallback icon if image fails to load
645
+ e.currentTarget.style.display = 'none';
646
+ const fallbackElement = e.currentTarget.parentElement?.querySelector('[data-fallback="true"]');
647
+ if (fallbackElement) {
648
+ fallbackElement.style.display = 'flex';
649
+ }
650
+ }
651
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
652
+ source: {
653
+ uri: oxyServices.getFileDownloadUrl(file.id)
654
+ },
655
+ style: styles.previewImage,
656
+ resizeMode: "cover",
657
+ onError: () => {
658
+ // For React Native, you might want to set an error state
659
+ console.warn('Failed to load image preview for file:', file.id);
660
+ }
661
+ })), isPDF && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
662
+ style: styles.pdfPreview,
663
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
664
+ name: "document",
665
+ size: 32,
666
+ color: primaryColor
667
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
668
+ style: [styles.pdfLabel, {
669
+ color: primaryColor
670
+ }],
671
+ children: "PDF"
672
+ })]
673
+ }), isVideo && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
674
+ style: styles.videoPreview,
675
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
676
+ name: "play-circle",
677
+ size: 32,
678
+ color: primaryColor
679
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
680
+ style: [styles.videoLabel, {
681
+ color: primaryColor
682
+ }],
683
+ children: "VIDEO"
684
+ })]
685
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
686
+ style: [styles.fallbackIcon, {
687
+ display: isImage ? 'none' : 'flex'
688
+ }],
689
+ ...(_reactNative.Platform.OS === 'web' && {
690
+ 'data-fallback': 'true'
691
+ }),
692
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
693
+ name: getFileIcon(file.contentType),
694
+ size: 32,
695
+ color: primaryColor
696
+ })
697
+ }), _reactNative.Platform.OS === 'web' && hoveredPreview === file.id && isImage && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
698
+ style: styles.previewOverlay,
699
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
700
+ name: "eye",
701
+ size: 24,
702
+ color: "#FFFFFF"
703
+ })
704
+ })]
705
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
706
+ style: styles.fileIconContainer,
707
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
708
+ name: getFileIcon(file.contentType),
709
+ size: 32,
710
+ color: primaryColor
711
+ })
712
+ })
713
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
714
+ style: styles.fileInfo,
715
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
716
+ style: [styles.fileName, {
717
+ color: textColor
718
+ }],
719
+ numberOfLines: 1,
720
+ children: file.filename
721
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
722
+ style: [styles.fileDetails, {
723
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
724
+ }],
725
+ children: [formatFileSize(file.length), " \u2022 ", new Date(file.uploadDate).toLocaleDateString()]
726
+ }), file.metadata?.description && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
727
+ style: [styles.fileDescription, {
728
+ color: isDarkTheme ? '#AAAAAA' : '#888888'
729
+ }],
730
+ numberOfLines: 2,
731
+ children: file.metadata.description
732
+ })]
733
+ })]
734
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
735
+ style: styles.fileActions,
736
+ children: [hasPreview && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
737
+ style: [styles.actionButton, {
738
+ backgroundColor: isDarkTheme ? '#333333' : '#F0F0F0'
739
+ }],
740
+ onPress: () => handleFileOpen(file),
741
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
742
+ name: "eye",
743
+ size: 20,
744
+ color: primaryColor
745
+ })
746
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
747
+ style: [styles.actionButton, {
748
+ backgroundColor: isDarkTheme ? '#333333' : '#F0F0F0'
749
+ }],
750
+ onPress: () => handleFileDownload(file.id, file.filename),
751
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
752
+ name: "download",
753
+ size: 20,
754
+ color: primaryColor
755
+ })
756
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
757
+ style: [styles.actionButton, {
758
+ backgroundColor: isDarkTheme ? '#400000' : '#FFEBEE'
759
+ }],
760
+ onPress: () => {
761
+ handleFileDelete(file.id, file.filename);
762
+ },
763
+ disabled: deleting === file.id,
764
+ children: deleting === file.id ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
765
+ size: "small",
766
+ color: dangerColor
767
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
768
+ name: "trash",
769
+ size: 20,
770
+ color: dangerColor
771
+ })
772
+ })]
773
+ })]
774
+ }, file.id);
775
+ };
776
+ const renderPhotoGrid = (0, _react.useCallback)(() => {
777
+ const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
778
+ if (photos.length === 0) {
779
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
780
+ style: styles.emptyState,
781
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
782
+ name: "images-outline",
783
+ size: 64,
784
+ color: isDarkTheme ? '#666666' : '#CCCCCC'
785
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
786
+ style: [styles.emptyStateTitle, {
787
+ color: textColor
788
+ }],
789
+ children: "No Photos Yet"
790
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
791
+ style: [styles.emptyStateDescription, {
792
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
793
+ }],
794
+ children: user?.id === targetUserId ? `Upload photos to get started. You can select multiple photos at once${_reactNative.Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}` : "This user hasn't uploaded any photos yet"
795
+ }), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
796
+ style: [styles.emptyStateButton, {
797
+ backgroundColor: primaryColor
798
+ }],
799
+ onPress: handleFileUpload,
800
+ disabled: uploading,
801
+ children: uploading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
802
+ size: "small",
803
+ color: "#FFFFFF"
804
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
805
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
806
+ name: "cloud-upload",
807
+ size: 20,
808
+ color: "#FFFFFF"
809
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
810
+ style: styles.emptyStateButtonText,
811
+ children: "Upload Photos"
812
+ })]
813
+ })
814
+ })]
815
+ });
816
+ }
817
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
818
+ style: styles.scrollView,
819
+ contentContainerStyle: styles.photoScrollContainer,
820
+ refreshControl: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.RefreshControl, {
821
+ refreshing: refreshing,
822
+ onRefresh: () => loadFiles(true),
823
+ tintColor: primaryColor
824
+ }),
825
+ showsVerticalScrollIndicator: false,
826
+ children: [loadingDimensions && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
827
+ style: styles.dimensionsLoadingIndicator,
828
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
829
+ size: "small",
830
+ color: primaryColor
831
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
832
+ style: [styles.dimensionsLoadingText, {
833
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
834
+ }],
835
+ children: "Loading photo layout..."
836
+ })]
837
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(JustifiedPhotoGrid, {
838
+ photos: photos,
839
+ photoDimensions: photoDimensions,
840
+ loadPhotoDimensions: loadPhotoDimensions,
841
+ createJustifiedRows: createJustifiedRows,
842
+ renderJustifiedPhotoItem: renderJustifiedPhotoItem,
843
+ renderSimplePhotoItem: renderPhotoItem,
844
+ textColor: textColor,
845
+ containerWidth: containerWidth
846
+ })]
847
+ });
848
+ }, [filteredFiles, isDarkTheme, textColor, user?.id, targetUserId, uploading, primaryColor, handleFileUpload, refreshing, loadFiles, loadingDimensions, photoDimensions, loadPhotoDimensions, createJustifiedRows, renderJustifiedPhotoItem]);
849
+
850
+ // Separate component for the photo grid to optimize rendering
851
+ const JustifiedPhotoGrid = /*#__PURE__*/_react.default.memo(({
852
+ photos,
853
+ photoDimensions,
854
+ loadPhotoDimensions,
855
+ createJustifiedRows,
856
+ renderJustifiedPhotoItem,
857
+ renderSimplePhotoItem,
858
+ textColor,
859
+ containerWidth
860
+ }) => {
861
+ // Load dimensions for new photos
862
+ _react.default.useEffect(() => {
863
+ loadPhotoDimensions(photos);
864
+ }, [photos.map(p => p.id).join(','), loadPhotoDimensions]);
865
+
866
+ // Group photos by date
867
+ const photosByDate = _react.default.useMemo(() => {
868
+ return photos.reduce((groups, photo) => {
869
+ const date = new Date(photo.uploadDate).toDateString();
870
+ if (!groups[date]) {
871
+ groups[date] = [];
872
+ }
873
+ groups[date].push(photo);
874
+ return groups;
875
+ }, {});
876
+ }, [photos]);
877
+ const sortedDates = _react.default.useMemo(() => {
878
+ return Object.keys(photosByDate).sort((a, b) => new Date(b).getTime() - new Date(a).getTime());
879
+ }, [photosByDate]);
880
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
881
+ children: sortedDates.map(date => {
882
+ const dayPhotos = photosByDate[date];
883
+ const justifiedRows = createJustifiedRows(dayPhotos, containerWidth);
884
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
885
+ style: styles.photoDateSection,
886
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
887
+ style: [styles.photoDateHeader, {
888
+ color: textColor
889
+ }],
890
+ children: new Date(date).toLocaleDateString('en-US', {
891
+ weekday: 'long',
892
+ year: 'numeric',
893
+ month: 'long',
894
+ day: 'numeric'
895
+ })
896
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
897
+ style: styles.justifiedPhotoGrid,
898
+ children: justifiedRows.map((row, rowIndex) => {
899
+ // Calculate row height based on available width
900
+ const gap = 4;
901
+ let totalAspectRatio = 0;
902
+
903
+ // Calculate total aspect ratio for this row
904
+ row.forEach(photo => {
905
+ const dimensions = photoDimensions[photo.id];
906
+ const aspectRatio = dimensions ? dimensions.width / dimensions.height : 1.33; // Default 4:3 ratio
907
+ totalAspectRatio += aspectRatio;
908
+ });
909
+
910
+ // Calculate the height that makes the row fill the available width
911
+ // Account for photoScrollContainer padding (32px total) and gaps between photos
912
+ const scrollContainerPadding = 32;
913
+ const availableWidth = containerWidth - scrollContainerPadding - gap * (row.length - 1);
914
+ const calculatedHeight = availableWidth / totalAspectRatio;
915
+
916
+ // Clamp height for visual consistency
917
+ const rowHeight = Math.max(120, Math.min(calculatedHeight, 300));
918
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
919
+ style: [styles.justifiedPhotoRow, {
920
+ height: rowHeight,
921
+ maxWidth: containerWidth - 32,
922
+ // Account for scroll container padding
923
+ gap: 4 // Add horizontal gap between photos in row
924
+ }],
925
+ children: row.map((photo, photoIndex) => {
926
+ const dimensions = photoDimensions[photo.id];
927
+ const aspectRatio = dimensions ? dimensions.width / dimensions.height : 1.33; // Default 4:3 ratio
928
+
929
+ const photoWidth = rowHeight * aspectRatio;
930
+ const isLast = photoIndex === row.length - 1;
931
+ return renderJustifiedPhotoItem(photo, photoWidth, rowHeight, isLast);
932
+ })
933
+ }, `row-${rowIndex}`);
934
+ })
935
+ })]
936
+ }, date);
937
+ })
938
+ });
939
+ });
940
+ const renderPhotoItem = (photo, index) => {
941
+ const downloadUrl = oxyServices.getFileDownloadUrl(photo.id);
942
+
943
+ // Calculate photo item width based on actual container size from bottom sheet
944
+ let itemsPerRow = 3; // Default for mobile
945
+ if (containerWidth > 768) itemsPerRow = 6; // Tablet/Desktop
946
+ else if (containerWidth > 480) itemsPerRow = 4; // Large mobile
947
+
948
+ // Account for the photoScrollContainer padding (16px on each side = 32px total)
949
+ const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
950
+ const gaps = (itemsPerRow - 1) * 4; // Gap between items
951
+ const availableWidth = containerWidth - scrollContainerPadding;
952
+ const itemWidth = (availableWidth - gaps) / itemsPerRow;
953
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
954
+ style: [styles.photoItem, {
955
+ width: itemWidth,
956
+ height: itemWidth
957
+ }],
958
+ onPress: () => handleFileOpen(photo),
959
+ activeOpacity: 0.8,
960
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
961
+ style: styles.photoContainer,
962
+ children: _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
963
+ src: downloadUrl,
964
+ alt: photo.filename,
965
+ style: {
966
+ width: '100%',
967
+ height: '100%',
968
+ objectFit: 'cover',
969
+ borderRadius: 8,
970
+ transition: 'transform 0.2s ease'
971
+ },
972
+ loading: "lazy",
973
+ onError: e => {
974
+ console.error('Photo failed to load:', e);
975
+ // Could replace with placeholder image
976
+ },
977
+ onMouseEnter: e => {
978
+ e.currentTarget.style.transform = 'scale(1.02)';
979
+ },
980
+ onMouseLeave: e => {
981
+ e.currentTarget.style.transform = 'scale(1)';
982
+ }
983
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
984
+ source: {
985
+ uri: downloadUrl
986
+ },
987
+ style: styles.photoImage,
988
+ resizeMode: "cover",
989
+ onError: e => {
990
+ console.error('Photo failed to load:', e);
991
+ }
992
+ })
993
+ })
994
+ }, photo.id);
995
+ };
996
+ const renderFileDetailsModal = () => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
997
+ visible: showFileDetails,
998
+ animationType: "slide",
999
+ presentationStyle: "pageSheet",
1000
+ onRequestClose: () => setShowFileDetails(false),
1001
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1002
+ style: [styles.modalContainer, {
1003
+ backgroundColor
1004
+ }],
1005
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1006
+ style: [styles.modalHeader, {
1007
+ borderBottomColor: borderColor
1008
+ }],
1009
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1010
+ style: styles.modalCloseButton,
1011
+ onPress: () => setShowFileDetails(false),
1012
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1013
+ name: "close",
1014
+ size: 24,
1015
+ color: textColor
1016
+ })
1017
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1018
+ style: [styles.modalTitle, {
1019
+ color: textColor
1020
+ }],
1021
+ children: "File Details"
1022
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1023
+ style: styles.modalPlaceholder
1024
+ })]
1025
+ }), selectedFile && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
1026
+ style: styles.modalContent,
1027
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1028
+ style: [styles.fileDetailCard, {
1029
+ backgroundColor: secondaryBackgroundColor,
1030
+ borderColor
1031
+ }],
1032
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1033
+ style: styles.fileDetailIcon,
1034
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1035
+ name: getFileIcon(selectedFile.contentType),
1036
+ size: 64,
1037
+ color: primaryColor
1038
+ })
1039
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1040
+ style: [styles.fileDetailName, {
1041
+ color: textColor
1042
+ }],
1043
+ children: selectedFile.filename
1044
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1045
+ style: styles.fileDetailInfo,
1046
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1047
+ style: styles.detailRow,
1048
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1049
+ style: [styles.detailLabel, {
1050
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1051
+ }],
1052
+ children: "Size:"
1053
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1054
+ style: [styles.detailValue, {
1055
+ color: textColor
1056
+ }],
1057
+ children: formatFileSize(selectedFile.length)
1058
+ })]
1059
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1060
+ style: styles.detailRow,
1061
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1062
+ style: [styles.detailLabel, {
1063
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1064
+ }],
1065
+ children: "Type:"
1066
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1067
+ style: [styles.detailValue, {
1068
+ color: textColor
1069
+ }],
1070
+ children: selectedFile.contentType
1071
+ })]
1072
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1073
+ style: styles.detailRow,
1074
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1075
+ style: [styles.detailLabel, {
1076
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1077
+ }],
1078
+ children: "Uploaded:"
1079
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1080
+ style: [styles.detailValue, {
1081
+ color: textColor
1082
+ }],
1083
+ children: new Date(selectedFile.uploadDate).toLocaleString()
1084
+ })]
1085
+ }), selectedFile.metadata?.description && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1086
+ style: styles.detailRow,
1087
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1088
+ style: [styles.detailLabel, {
1089
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1090
+ }],
1091
+ children: "Description:"
1092
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1093
+ style: [styles.detailValue, {
1094
+ color: textColor
1095
+ }],
1096
+ children: selectedFile.metadata.description
1097
+ })]
1098
+ })]
1099
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1100
+ style: styles.modalActions,
1101
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1102
+ style: [styles.modalActionButton, {
1103
+ backgroundColor: primaryColor
1104
+ }],
1105
+ onPress: () => {
1106
+ handleFileDownload(selectedFile.id, selectedFile.filename);
1107
+ setShowFileDetails(false);
1108
+ },
1109
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1110
+ name: "download",
1111
+ size: 20,
1112
+ color: "#FFFFFF"
1113
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1114
+ style: styles.modalActionText,
1115
+ children: "Download"
1116
+ })]
1117
+ }), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1118
+ style: [styles.modalActionButton, {
1119
+ backgroundColor: dangerColor
1120
+ }],
1121
+ onPress: () => {
1122
+ setShowFileDetails(false);
1123
+ handleFileDelete(selectedFile.id, selectedFile.filename);
1124
+ },
1125
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1126
+ name: "trash",
1127
+ size: 20,
1128
+ color: "#FFFFFF"
1129
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1130
+ style: styles.modalActionText,
1131
+ children: "Delete"
1132
+ })]
1133
+ })]
1134
+ })]
1135
+ })
1136
+ })]
1137
+ })
1138
+ });
1139
+ const renderFileViewer = () => {
1140
+ if (!openedFile) return null;
1141
+ const isImage = openedFile.contentType.startsWith('image/');
1142
+ const isText = openedFile.contentType.startsWith('text/') || openedFile.contentType.includes('json') || openedFile.contentType.includes('xml') || openedFile.contentType.includes('javascript') || openedFile.contentType.includes('typescript');
1143
+ const isPDF = openedFile.contentType.includes('pdf');
1144
+ const isVideo = openedFile.contentType.startsWith('video/');
1145
+ const isAudio = openedFile.contentType.startsWith('audio/');
1146
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1147
+ style: [styles.fileViewerContainer, {
1148
+ backgroundColor
1149
+ }],
1150
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1151
+ style: [styles.fileViewerHeader, {
1152
+ borderBottomColor: borderColor
1153
+ }],
1154
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1155
+ style: styles.backButton,
1156
+ onPress: handleCloseFile,
1157
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1158
+ name: "arrow-back",
1159
+ size: 24,
1160
+ color: textColor
1161
+ })
1162
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1163
+ style: styles.fileViewerTitleContainer,
1164
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1165
+ style: [styles.fileViewerTitle, {
1166
+ color: textColor
1167
+ }],
1168
+ numberOfLines: 1,
1169
+ children: openedFile.filename
1170
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1171
+ style: [styles.fileViewerSubtitle, {
1172
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1173
+ }],
1174
+ children: [formatFileSize(openedFile.length), " \u2022 ", openedFile.contentType]
1175
+ })]
1176
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1177
+ style: styles.fileViewerActions,
1178
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1179
+ style: [styles.actionButton, {
1180
+ backgroundColor: isDarkTheme ? '#333333' : '#F0F0F0'
1181
+ }],
1182
+ onPress: () => handleFileDownload(openedFile.id, openedFile.filename),
1183
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1184
+ name: "download",
1185
+ size: 20,
1186
+ color: primaryColor
1187
+ })
1188
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1189
+ style: [styles.actionButton, {
1190
+ backgroundColor: showFileDetailsInViewer ? primaryColor : isDarkTheme ? '#333333' : '#F0F0F0'
1191
+ }],
1192
+ onPress: () => setShowFileDetailsInViewer(!showFileDetailsInViewer),
1193
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1194
+ name: showFileDetailsInViewer ? "chevron-up" : "information-circle",
1195
+ size: 20,
1196
+ color: showFileDetailsInViewer ? "#FFFFFF" : primaryColor
1197
+ })
1198
+ })]
1199
+ })]
1200
+ }), showFileDetailsInViewer && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1201
+ style: [styles.fileDetailsSection, {
1202
+ backgroundColor: secondaryBackgroundColor,
1203
+ borderColor
1204
+ }],
1205
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1206
+ style: styles.fileDetailsSectionHeader,
1207
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1208
+ style: [styles.fileDetailsSectionTitle, {
1209
+ color: textColor
1210
+ }],
1211
+ children: "File Details"
1212
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1213
+ style: styles.fileDetailsSectionToggle,
1214
+ onPress: () => setShowFileDetailsInViewer(false),
1215
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1216
+ name: "chevron-up",
1217
+ size: 20,
1218
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1219
+ })
1220
+ })]
1221
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1222
+ style: styles.fileDetailInfo,
1223
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1224
+ style: styles.detailRow,
1225
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1226
+ style: [styles.detailLabel, {
1227
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1228
+ }],
1229
+ children: "File Name:"
1230
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1231
+ style: [styles.detailValue, {
1232
+ color: textColor
1233
+ }],
1234
+ children: openedFile.filename
1235
+ })]
1236
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1237
+ style: styles.detailRow,
1238
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1239
+ style: [styles.detailLabel, {
1240
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1241
+ }],
1242
+ children: "Size:"
1243
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1244
+ style: [styles.detailValue, {
1245
+ color: textColor
1246
+ }],
1247
+ children: formatFileSize(openedFile.length)
1248
+ })]
1249
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1250
+ style: styles.detailRow,
1251
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1252
+ style: [styles.detailLabel, {
1253
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1254
+ }],
1255
+ children: "Type:"
1256
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1257
+ style: [styles.detailValue, {
1258
+ color: textColor
1259
+ }],
1260
+ children: openedFile.contentType
1261
+ })]
1262
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1263
+ style: styles.detailRow,
1264
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1265
+ style: [styles.detailLabel, {
1266
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1267
+ }],
1268
+ children: "Uploaded:"
1269
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1270
+ style: [styles.detailValue, {
1271
+ color: textColor
1272
+ }],
1273
+ children: new Date(openedFile.uploadDate).toLocaleString()
1274
+ })]
1275
+ }), openedFile.metadata?.description && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1276
+ style: styles.detailRow,
1277
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1278
+ style: [styles.detailLabel, {
1279
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1280
+ }],
1281
+ children: "Description:"
1282
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1283
+ style: [styles.detailValue, {
1284
+ color: textColor
1285
+ }],
1286
+ children: openedFile.metadata.description
1287
+ })]
1288
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1289
+ style: styles.detailRow,
1290
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1291
+ style: [styles.detailLabel, {
1292
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1293
+ }],
1294
+ children: "File ID:"
1295
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1296
+ style: [styles.detailValue, {
1297
+ color: textColor,
1298
+ fontSize: 12,
1299
+ fontFamily: _reactNative.Platform.OS === 'web' ? 'monospace' : 'Courier'
1300
+ }],
1301
+ children: openedFile.id
1302
+ })]
1303
+ })]
1304
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1305
+ style: styles.fileDetailsActions,
1306
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1307
+ style: [styles.fileDetailsActionButton, {
1308
+ backgroundColor: primaryColor
1309
+ }],
1310
+ onPress: () => handleFileDownload(openedFile.id, openedFile.filename),
1311
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1312
+ name: "download",
1313
+ size: 16,
1314
+ color: "#FFFFFF"
1315
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1316
+ style: styles.fileDetailsActionText,
1317
+ children: "Download"
1318
+ })]
1319
+ }), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1320
+ style: [styles.fileDetailsActionButton, {
1321
+ backgroundColor: dangerColor
1322
+ }],
1323
+ onPress: () => {
1324
+ handleCloseFile();
1325
+ handleFileDelete(openedFile.id, openedFile.filename);
1326
+ },
1327
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1328
+ name: "trash",
1329
+ size: 16,
1330
+ color: "#FFFFFF"
1331
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1332
+ style: styles.fileDetailsActionText,
1333
+ children: "Delete"
1334
+ })]
1335
+ })]
1336
+ })]
1337
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
1338
+ style: [styles.fileViewerContent, showFileDetailsInViewer && styles.fileViewerContentWithDetails],
1339
+ contentContainerStyle: styles.fileViewerContentContainer,
1340
+ children: loadingFileContent ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1341
+ style: styles.fileViewerLoading,
1342
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
1343
+ size: "large",
1344
+ color: primaryColor
1345
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1346
+ style: [styles.fileViewerLoadingText, {
1347
+ color: textColor
1348
+ }],
1349
+ children: "Loading file content..."
1350
+ })]
1351
+ }) : isImage && fileContent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1352
+ style: styles.imageContainer,
1353
+ children: _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
1354
+ src: fileContent,
1355
+ alt: openedFile.filename,
1356
+ style: {
1357
+ maxWidth: '100%',
1358
+ maxHeight: '80vh',
1359
+ objectFit: 'contain',
1360
+ borderRadius: 8
1361
+ },
1362
+ onError: e => {
1363
+ console.error('Image failed to load:', e);
1364
+ }
1365
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
1366
+ source: {
1367
+ uri: fileContent
1368
+ },
1369
+ style: {
1370
+ width: '100%',
1371
+ height: 400,
1372
+ resizeMode: 'contain',
1373
+ borderRadius: 8
1374
+ },
1375
+ onError: e => {
1376
+ console.error('Image failed to load:', e);
1377
+ }
1378
+ })
1379
+ }) : isText && fileContent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1380
+ style: [styles.textContainer, {
1381
+ backgroundColor: secondaryBackgroundColor,
1382
+ borderColor
1383
+ }],
1384
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
1385
+ style: {
1386
+ flex: 1
1387
+ },
1388
+ nestedScrollEnabled: true,
1389
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1390
+ style: [styles.textContent, {
1391
+ color: textColor
1392
+ }],
1393
+ children: fileContent
1394
+ })
1395
+ })
1396
+ }) : isPDF && fileContent && _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1397
+ style: styles.pdfContainer,
1398
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("iframe", {
1399
+ src: fileContent,
1400
+ width: "100%",
1401
+ height: "600px",
1402
+ style: {
1403
+ border: 'none',
1404
+ borderRadius: 8
1405
+ },
1406
+ title: openedFile.filename
1407
+ })
1408
+ }) : isVideo && fileContent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1409
+ style: styles.mediaContainer,
1410
+ children: _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("video", {
1411
+ controls: true,
1412
+ style: {
1413
+ width: '100%',
1414
+ maxHeight: '70vh',
1415
+ borderRadius: 8
1416
+ },
1417
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("source", {
1418
+ src: fileContent,
1419
+ type: openedFile.contentType
1420
+ }), "Your browser does not support the video tag."]
1421
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1422
+ style: [styles.unsupportedText, {
1423
+ color: textColor
1424
+ }],
1425
+ children: "Video playback not supported on mobile"
1426
+ })
1427
+ }) : isAudio && fileContent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1428
+ style: styles.mediaContainer,
1429
+ children: _reactNative.Platform.OS === 'web' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("audio", {
1430
+ controls: true,
1431
+ style: {
1432
+ width: '100%',
1433
+ borderRadius: 8
1434
+ },
1435
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("source", {
1436
+ src: fileContent,
1437
+ type: openedFile.contentType
1438
+ }), "Your browser does not support the audio tag."]
1439
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1440
+ style: [styles.unsupportedText, {
1441
+ color: textColor
1442
+ }],
1443
+ children: "Audio playback not supported on mobile"
1444
+ })
1445
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1446
+ style: styles.unsupportedFileContainer,
1447
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1448
+ name: getFileIcon(openedFile.contentType),
1449
+ size: 64,
1450
+ color: isDarkTheme ? '#666666' : '#CCCCCC'
1451
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1452
+ style: [styles.unsupportedFileTitle, {
1453
+ color: textColor
1454
+ }],
1455
+ children: "Preview Not Available"
1456
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1457
+ style: [styles.unsupportedFileDescription, {
1458
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1459
+ }],
1460
+ children: ["This file type cannot be previewed in the browser.", '\n', "Download the file to view its contents."]
1461
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1462
+ style: [styles.downloadButtonLarge, {
1463
+ backgroundColor: primaryColor
1464
+ }],
1465
+ onPress: () => handleFileDownload(openedFile.id, openedFile.filename),
1466
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1467
+ name: "download",
1468
+ size: 20,
1469
+ color: "#FFFFFF"
1470
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1471
+ style: styles.downloadButtonText,
1472
+ children: "Download File"
1473
+ })]
1474
+ })]
1475
+ })
1476
+ })]
1477
+ });
1478
+ };
1479
+ const renderEmptyState = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1480
+ style: styles.emptyState,
1481
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1482
+ name: "folder-open-outline",
1483
+ size: 64,
1484
+ color: isDarkTheme ? '#666666' : '#CCCCCC'
1485
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1486
+ style: [styles.emptyStateTitle, {
1487
+ color: textColor
1488
+ }],
1489
+ children: "No Files Yet"
1490
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1491
+ style: [styles.emptyStateDescription, {
1492
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1493
+ }],
1494
+ children: user?.id === targetUserId ? `Upload files to get started. You can select multiple files at once${_reactNative.Platform.OS === 'web' ? ' or drag & drop them here.' : '.'}` : "This user hasn't uploaded any files yet"
1495
+ }), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1496
+ style: [styles.emptyStateButton, {
1497
+ backgroundColor: primaryColor
1498
+ }],
1499
+ onPress: handleFileUpload,
1500
+ disabled: uploading,
1501
+ children: uploading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
1502
+ size: "small",
1503
+ color: "#FFFFFF"
1504
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1505
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1506
+ name: "cloud-upload",
1507
+ size: 20,
1508
+ color: "#FFFFFF"
1509
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1510
+ style: styles.emptyStateButtonText,
1511
+ children: "Upload Files"
1512
+ })]
1513
+ })
1514
+ })]
1515
+ });
1516
+ if (loading) {
1517
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1518
+ style: [styles.container, styles.centerContent, {
1519
+ backgroundColor
1520
+ }],
1521
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
1522
+ size: "large",
1523
+ color: primaryColor
1524
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1525
+ style: [styles.loadingText, {
1526
+ color: textColor
1527
+ }],
1528
+ children: "Loading files..."
1529
+ })]
1530
+ });
1531
+ }
1532
+
1533
+ // If a file is opened, show the file viewer
1534
+ if (openedFile) {
1535
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1536
+ children: [renderFileViewer(), renderFileDetailsModal()]
1537
+ });
1538
+ }
1539
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1540
+ style: [styles.container, {
1541
+ backgroundColor
1542
+ }, isDragging && _reactNative.Platform.OS === 'web' && styles.dragOverlay],
1543
+ ...(_reactNative.Platform.OS === 'web' && user?.id === targetUserId ? {
1544
+ onDragOver: handleDragOver,
1545
+ onDragLeave: handleDragLeave,
1546
+ onDrop: handleDrop
1547
+ } : {}),
1548
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1549
+ style: [styles.header, {
1550
+ borderBottomColor: borderColor,
1551
+ backgroundColor: isDarkTheme ? '#1A1A1A' : '#FFFFFF',
1552
+ shadowColor: '#000000',
1553
+ shadowOffset: {
1554
+ width: 0,
1555
+ height: 2
1556
+ },
1557
+ shadowOpacity: isDarkTheme ? 0.3 : 0.1,
1558
+ shadowRadius: 8,
1559
+ elevation: 4
1560
+ }],
1561
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1562
+ style: [styles.backButton, {
1563
+ backgroundColor: isDarkTheme ? '#2A2A2A' : '#F8F9FA',
1564
+ borderRadius: 12
1565
+ }],
1566
+ onPress: onClose || goBack,
1567
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1568
+ name: "arrow-back",
1569
+ size: 22,
1570
+ color: textColor
1571
+ })
1572
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1573
+ style: styles.headerTitleContainer,
1574
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1575
+ style: [styles.headerTitle, {
1576
+ color: textColor
1577
+ }],
1578
+ children: viewMode === 'photos' ? 'Photos' : 'File Management'
1579
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1580
+ style: [styles.headerSubtitle, {
1581
+ color: isDarkTheme ? '#AAAAAA' : '#666666'
1582
+ }],
1583
+ children: [filteredFiles.length, " ", filteredFiles.length === 1 ? 'item' : 'items']
1584
+ })]
1585
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1586
+ style: styles.headerActions,
1587
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1588
+ style: [styles.viewModeToggle, {
1589
+ backgroundColor: isDarkTheme ? '#2A2A2A' : '#F8F9FA',
1590
+ borderWidth: 1,
1591
+ borderColor: isDarkTheme ? '#3A3A3A' : '#E8E9EA',
1592
+ shadowColor: '#000000',
1593
+ shadowOffset: {
1594
+ width: 0,
1595
+ height: 1
1596
+ },
1597
+ shadowOpacity: isDarkTheme ? 0.3 : 0.05,
1598
+ shadowRadius: 4,
1599
+ elevation: 2
1600
+ }],
1601
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1602
+ style: [styles.viewModeButton, viewMode === 'all' && {
1603
+ backgroundColor: primaryColor,
1604
+ shadowColor: primaryColor,
1605
+ shadowOffset: {
1606
+ width: 0,
1607
+ height: 2
1608
+ },
1609
+ shadowOpacity: 0.3,
1610
+ shadowRadius: 4,
1611
+ elevation: 3
1612
+ }],
1613
+ onPress: () => setViewMode('all'),
1614
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1615
+ name: "folder",
1616
+ size: 18,
1617
+ color: viewMode === 'all' ? '#FFFFFF' : textColor
1618
+ })
1619
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1620
+ style: [styles.viewModeButton, viewMode === 'photos' && {
1621
+ backgroundColor: primaryColor,
1622
+ shadowColor: primaryColor,
1623
+ shadowOffset: {
1624
+ width: 0,
1625
+ height: 2
1626
+ },
1627
+ shadowOpacity: 0.3,
1628
+ shadowRadius: 4,
1629
+ elevation: 3
1630
+ }],
1631
+ onPress: () => setViewMode('photos'),
1632
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1633
+ name: "images",
1634
+ size: 18,
1635
+ color: viewMode === 'photos' ? '#FFFFFF' : textColor
1636
+ })
1637
+ })]
1638
+ }), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1639
+ style: [styles.uploadButton, {
1640
+ backgroundColor: primaryColor,
1641
+ shadowColor: primaryColor,
1642
+ shadowOffset: {
1643
+ width: 0,
1644
+ height: 3
1645
+ },
1646
+ shadowOpacity: 0.4,
1647
+ shadowRadius: 6,
1648
+ elevation: 5,
1649
+ transform: uploading ? [{
1650
+ scale: 0.95
1651
+ }] : [{
1652
+ scale: 1
1653
+ }]
1654
+ }],
1655
+ onPress: handleFileUpload,
1656
+ disabled: uploading,
1657
+ children: uploading ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1658
+ style: styles.uploadProgress,
1659
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
1660
+ size: "small",
1661
+ color: "#FFFFFF"
1662
+ }), uploadProgress && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1663
+ style: styles.uploadProgressText,
1664
+ children: [uploadProgress.current, "/", uploadProgress.total]
1665
+ })]
1666
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1667
+ name: "add",
1668
+ size: 26,
1669
+ color: "#FFFFFF"
1670
+ })
1671
+ })]
1672
+ })]
1673
+ }), files.length > 0 && (viewMode === 'all' || files.some(f => f.contentType.startsWith('image/'))) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1674
+ style: [styles.searchContainer, {
1675
+ backgroundColor: isDarkTheme ? '#1A1A1A' : '#FFFFFF',
1676
+ borderColor: isDarkTheme ? '#3A3A3A' : '#E8E9EA',
1677
+ shadowColor: '#000000',
1678
+ shadowOffset: {
1679
+ width: 0,
1680
+ height: 1
1681
+ },
1682
+ shadowOpacity: isDarkTheme ? 0.2 : 0.05,
1683
+ shadowRadius: 4,
1684
+ elevation: 2
1685
+ }],
1686
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1687
+ name: "search",
1688
+ size: 22,
1689
+ color: isDarkTheme ? '#888888' : '#666666'
1690
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1691
+ style: [styles.searchInput, {
1692
+ color: textColor
1693
+ }],
1694
+ placeholder: viewMode === 'photos' ? 'Search photos...' : 'Search files...',
1695
+ placeholderTextColor: isDarkTheme ? '#888888' : '#999999',
1696
+ value: searchQuery,
1697
+ onChangeText: setSearchQuery
1698
+ }), searchQuery.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1699
+ onPress: () => setSearchQuery(''),
1700
+ style: styles.searchClearButton,
1701
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1702
+ name: "close-circle",
1703
+ size: 22,
1704
+ color: isDarkTheme ? '#888888' : '#666666'
1705
+ })
1706
+ })]
1707
+ }), files.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1708
+ style: [styles.statsContainer, {
1709
+ backgroundColor: isDarkTheme ? '#1A1A1A' : '#FFFFFF',
1710
+ borderColor: isDarkTheme ? '#3A3A3A' : '#E8E9EA',
1711
+ shadowColor: '#000000',
1712
+ shadowOffset: {
1713
+ width: 0,
1714
+ height: 1
1715
+ },
1716
+ shadowOpacity: isDarkTheme ? 0.2 : 0.05,
1717
+ shadowRadius: 4,
1718
+ elevation: 2
1719
+ }],
1720
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1721
+ style: styles.statItem,
1722
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1723
+ style: [styles.statValue, {
1724
+ color: textColor
1725
+ }],
1726
+ children: filteredFiles.length
1727
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1728
+ style: [styles.statLabel, {
1729
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1730
+ }],
1731
+ children: searchQuery.length > 0 ? 'Found' : filteredFiles.length === 1 ? viewMode === 'photos' ? 'Photo' : 'File' : viewMode === 'photos' ? 'Photos' : 'Files'
1732
+ })]
1733
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1734
+ style: styles.statItem,
1735
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1736
+ style: [styles.statValue, {
1737
+ color: textColor
1738
+ }],
1739
+ children: formatFileSize(filteredFiles.reduce((total, file) => total + file.length, 0))
1740
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1741
+ style: [styles.statLabel, {
1742
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1743
+ }],
1744
+ children: searchQuery.length > 0 ? 'Size' : 'Total Size'
1745
+ })]
1746
+ }), searchQuery.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1747
+ style: styles.statItem,
1748
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1749
+ style: [styles.statValue, {
1750
+ color: textColor
1751
+ }],
1752
+ children: files.length
1753
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1754
+ style: [styles.statLabel, {
1755
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1756
+ }],
1757
+ children: "Total"
1758
+ })]
1759
+ })]
1760
+ }), viewMode === 'photos' ? renderPhotoGrid() : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
1761
+ style: styles.scrollView,
1762
+ contentContainerStyle: styles.scrollContainer,
1763
+ refreshControl: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.RefreshControl, {
1764
+ refreshing: refreshing,
1765
+ onRefresh: () => loadFiles(true),
1766
+ tintColor: primaryColor
1767
+ }),
1768
+ children: filteredFiles.length === 0 && searchQuery.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1769
+ style: styles.emptyState,
1770
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1771
+ name: "search",
1772
+ size: 64,
1773
+ color: isDarkTheme ? '#666666' : '#CCCCCC'
1774
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1775
+ style: [styles.emptyStateTitle, {
1776
+ color: textColor
1777
+ }],
1778
+ children: "No Results Found"
1779
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1780
+ style: [styles.emptyStateDescription, {
1781
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1782
+ }],
1783
+ children: ["No files match your search for \"", searchQuery, "\""]
1784
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1785
+ style: [styles.emptyStateButton, {
1786
+ backgroundColor: primaryColor
1787
+ }],
1788
+ onPress: () => setSearchQuery(''),
1789
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1790
+ name: "refresh",
1791
+ size: 20,
1792
+ color: "#FFFFFF"
1793
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1794
+ style: styles.emptyStateButtonText,
1795
+ children: "Clear Search"
1796
+ })]
1797
+ })]
1798
+ }) : filteredFiles.length === 0 ? renderEmptyState() : /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
1799
+ children: filteredFiles.map(renderFileItem)
1800
+ })
1801
+ }), renderFileDetailsModal(), isDragging && _reactNative.Platform.OS === 'web' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1802
+ style: styles.dragDropOverlay,
1803
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1804
+ style: styles.dragDropContent,
1805
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
1806
+ name: "cloud-upload",
1807
+ size: 64,
1808
+ color: primaryColor
1809
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1810
+ style: [styles.dragDropTitle, {
1811
+ color: primaryColor
1812
+ }],
1813
+ children: "Drop files to upload"
1814
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1815
+ style: [styles.dragDropSubtitle, {
1816
+ color: isDarkTheme ? '#BBBBBB' : '#666666'
1817
+ }],
1818
+ children: ["Release to upload", uploadProgress ? ` (${uploadProgress.current}/${uploadProgress.total})` : ' multiple files']
1819
+ })]
1820
+ })
1821
+ })]
1822
+ });
1823
+ };
1824
+ const styles = _reactNative.StyleSheet.create({
1825
+ container: {
1826
+ flex: 1
1827
+ },
1828
+ dragOverlay: {
1829
+ backgroundColor: 'rgba(0, 122, 255, 0.1)',
1830
+ borderWidth: 2,
1831
+ borderColor: '#007AFF',
1832
+ borderStyle: 'dashed'
1833
+ },
1834
+ centerContent: {
1835
+ justifyContent: 'center',
1836
+ alignItems: 'center'
1837
+ },
1838
+ header: {
1839
+ flexDirection: 'row',
1840
+ alignItems: 'center',
1841
+ justifyContent: 'space-between',
1842
+ paddingHorizontal: 24,
1843
+ paddingVertical: 20,
1844
+ borderBottomWidth: 1,
1845
+ position: 'relative'
1846
+ },
1847
+ backButton: {
1848
+ padding: 12,
1849
+ borderRadius: 12,
1850
+ alignItems: 'center',
1851
+ justifyContent: 'center',
1852
+ minWidth: 44,
1853
+ minHeight: 44
1854
+ },
1855
+ headerTitleContainer: {
1856
+ flex: 1,
1857
+ alignItems: 'center',
1858
+ justifyContent: 'center',
1859
+ marginHorizontal: 16
1860
+ },
1861
+ headerTitle: {
1862
+ fontSize: 22,
1863
+ fontWeight: '700',
1864
+ fontFamily: _fonts.fontFamilies.phuduBold,
1865
+ letterSpacing: -0.5,
1866
+ lineHeight: 28
1867
+ },
1868
+ headerSubtitle: {
1869
+ fontSize: 13,
1870
+ fontWeight: '500',
1871
+ fontFamily: _fonts.fontFamilies.phuduMedium,
1872
+ marginTop: 2,
1873
+ letterSpacing: 0.2
1874
+ },
1875
+ uploadButton: {
1876
+ width: 44,
1877
+ height: 44,
1878
+ borderRadius: 22,
1879
+ alignItems: 'center',
1880
+ justifyContent: 'center'
1881
+ },
1882
+ uploadProgress: {
1883
+ alignItems: 'center',
1884
+ justifyContent: 'center'
1885
+ },
1886
+ uploadProgressText: {
1887
+ color: '#FFFFFF',
1888
+ fontSize: 10,
1889
+ fontWeight: '600',
1890
+ marginTop: 2
1891
+ },
1892
+ searchContainer: {
1893
+ flexDirection: 'row',
1894
+ alignItems: 'center',
1895
+ paddingHorizontal: 20,
1896
+ paddingVertical: 16,
1897
+ marginHorizontal: 24,
1898
+ marginTop: 20,
1899
+ borderRadius: 16,
1900
+ borderWidth: 1,
1901
+ gap: 16
1902
+ },
1903
+ searchInput: {
1904
+ flex: 1,
1905
+ fontSize: 16,
1906
+ fontFamily: _fonts.fontFamilies.phudu,
1907
+ lineHeight: 20
1908
+ },
1909
+ searchClearButton: {
1910
+ padding: 4,
1911
+ borderRadius: 12,
1912
+ alignItems: 'center',
1913
+ justifyContent: 'center'
1914
+ },
1915
+ searchIcon: {
1916
+ marginRight: 8
1917
+ },
1918
+ statsContainer: {
1919
+ flexDirection: 'row',
1920
+ paddingHorizontal: 24,
1921
+ paddingVertical: 20,
1922
+ marginHorizontal: 24,
1923
+ marginTop: 20,
1924
+ borderRadius: 16,
1925
+ borderWidth: 1
1926
+ },
1927
+ statItem: {
1928
+ flex: 1,
1929
+ alignItems: 'center',
1930
+ paddingVertical: 4
1931
+ },
1932
+ statValue: {
1933
+ fontSize: 28,
1934
+ fontWeight: '800',
1935
+ fontFamily: _fonts.fontFamilies.phuduBold,
1936
+ letterSpacing: -0.5,
1937
+ lineHeight: 32
1938
+ },
1939
+ statLabel: {
1940
+ fontSize: 15,
1941
+ fontWeight: '500',
1942
+ fontFamily: _fonts.fontFamilies.phuduMedium,
1943
+ marginTop: 4,
1944
+ letterSpacing: 0.3
1945
+ },
1946
+ scrollView: {
1947
+ flex: 1
1948
+ },
1949
+ scrollContainer: {
1950
+ padding: 20
1951
+ },
1952
+ fileItem: {
1953
+ flexDirection: 'row',
1954
+ alignItems: 'center',
1955
+ padding: 16,
1956
+ marginBottom: 12,
1957
+ borderRadius: 12,
1958
+ borderWidth: 1
1959
+ },
1960
+ fileContent: {
1961
+ flexDirection: 'row',
1962
+ alignItems: 'center',
1963
+ flex: 1
1964
+ },
1965
+ fileIconContainer: {
1966
+ width: 50,
1967
+ height: 50,
1968
+ alignItems: 'center',
1969
+ justifyContent: 'center',
1970
+ marginRight: 12
1971
+ },
1972
+ filePreviewContainer: {
1973
+ width: 60,
1974
+ height: 60,
1975
+ marginRight: 12
1976
+ },
1977
+ filePreview: {
1978
+ width: '100%',
1979
+ height: '100%',
1980
+ borderRadius: 8,
1981
+ backgroundColor: '#F5F5F5',
1982
+ alignItems: 'center',
1983
+ justifyContent: 'center',
1984
+ overflow: 'hidden',
1985
+ position: 'relative'
1986
+ },
1987
+ previewImage: {
1988
+ width: '100%',
1989
+ height: '100%',
1990
+ borderRadius: 8
1991
+ },
1992
+ pdfPreview: {
1993
+ alignItems: 'center',
1994
+ justifyContent: 'center',
1995
+ width: '100%',
1996
+ height: '100%',
1997
+ backgroundColor: '#FF6B6B20'
1998
+ },
1999
+ pdfLabel: {
2000
+ fontSize: 8,
2001
+ fontWeight: 'bold',
2002
+ marginTop: 2
2003
+ },
2004
+ videoPreview: {
2005
+ alignItems: 'center',
2006
+ justifyContent: 'center',
2007
+ width: '100%',
2008
+ height: '100%',
2009
+ backgroundColor: '#4ECDC420'
2010
+ },
2011
+ videoLabel: {
2012
+ fontSize: 8,
2013
+ fontWeight: 'bold',
2014
+ marginTop: 2
2015
+ },
2016
+ fallbackIcon: {
2017
+ position: 'absolute',
2018
+ top: 0,
2019
+ left: 0,
2020
+ right: 0,
2021
+ bottom: 0,
2022
+ alignItems: 'center',
2023
+ justifyContent: 'center',
2024
+ backgroundColor: '#F5F5F5',
2025
+ borderRadius: 8
2026
+ },
2027
+ previewOverlay: {
2028
+ position: 'absolute',
2029
+ top: 0,
2030
+ left: 0,
2031
+ right: 0,
2032
+ bottom: 0,
2033
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
2034
+ alignItems: 'center',
2035
+ justifyContent: 'center',
2036
+ borderRadius: 8
2037
+ },
2038
+ fileInfo: {
2039
+ flex: 1
2040
+ },
2041
+ fileName: {
2042
+ fontSize: 16,
2043
+ fontWeight: '600',
2044
+ marginBottom: 4
2045
+ },
2046
+ fileDetails: {
2047
+ fontSize: 14,
2048
+ marginBottom: 2
2049
+ },
2050
+ fileDescription: {
2051
+ fontSize: 12,
2052
+ fontStyle: 'italic'
2053
+ },
2054
+ fileActions: {
2055
+ flexDirection: 'row',
2056
+ gap: 8
2057
+ },
2058
+ actionButton: {
2059
+ width: 40,
2060
+ height: 40,
2061
+ borderRadius: 20,
2062
+ alignItems: 'center',
2063
+ justifyContent: 'center'
2064
+ },
2065
+ emptyState: {
2066
+ alignItems: 'center',
2067
+ paddingVertical: 60,
2068
+ paddingHorizontal: 40
2069
+ },
2070
+ emptyStateTitle: {
2071
+ fontSize: 24,
2072
+ fontWeight: 'bold',
2073
+ fontFamily: _fonts.fontFamilies.phuduBold,
2074
+ marginTop: 16,
2075
+ marginBottom: 8
2076
+ },
2077
+ emptyStateDescription: {
2078
+ fontSize: 16,
2079
+ textAlign: 'center',
2080
+ lineHeight: 24,
2081
+ marginBottom: 32
2082
+ },
2083
+ emptyStateButton: {
2084
+ flexDirection: 'row',
2085
+ alignItems: 'center',
2086
+ paddingHorizontal: 24,
2087
+ paddingVertical: 12,
2088
+ borderRadius: 24,
2089
+ gap: 8
2090
+ },
2091
+ emptyStateButtonText: {
2092
+ color: '#FFFFFF',
2093
+ fontSize: 16,
2094
+ fontWeight: '600'
2095
+ },
2096
+ loadingText: {
2097
+ fontSize: 16,
2098
+ marginTop: 16
2099
+ },
2100
+ // Modal styles
2101
+ modalContainer: {
2102
+ flex: 1
2103
+ },
2104
+ modalHeader: {
2105
+ flexDirection: 'row',
2106
+ alignItems: 'center',
2107
+ justifyContent: 'space-between',
2108
+ paddingHorizontal: 20,
2109
+ paddingVertical: 16,
2110
+ borderBottomWidth: 1
2111
+ },
2112
+ modalCloseButton: {
2113
+ padding: 8
2114
+ },
2115
+ modalTitle: {
2116
+ fontSize: 18,
2117
+ fontWeight: '600',
2118
+ fontFamily: _fonts.fontFamilies.phuduSemiBold
2119
+ },
2120
+ modalPlaceholder: {
2121
+ width: 40
2122
+ },
2123
+ modalContent: {
2124
+ flex: 1,
2125
+ padding: 20
2126
+ },
2127
+ fileDetailCard: {
2128
+ padding: 24,
2129
+ borderRadius: 16,
2130
+ borderWidth: 1,
2131
+ alignItems: 'center'
2132
+ },
2133
+ fileDetailIcon: {
2134
+ marginBottom: 16
2135
+ },
2136
+ fileDetailName: {
2137
+ fontSize: 20,
2138
+ fontWeight: 'bold',
2139
+ fontFamily: _fonts.fontFamilies.phuduBold,
2140
+ textAlign: 'center',
2141
+ marginBottom: 24
2142
+ },
2143
+ fileDetailInfo: {
2144
+ width: '100%',
2145
+ marginBottom: 32
2146
+ },
2147
+ detailRow: {
2148
+ flexDirection: 'row',
2149
+ justifyContent: 'space-between',
2150
+ alignItems: 'flex-start',
2151
+ marginBottom: 12,
2152
+ flexWrap: 'wrap'
2153
+ },
2154
+ detailLabel: {
2155
+ fontSize: 16,
2156
+ fontWeight: '500',
2157
+ flex: 1,
2158
+ minWidth: 100
2159
+ },
2160
+ detailValue: {
2161
+ fontSize: 16,
2162
+ flex: 2,
2163
+ textAlign: 'right'
2164
+ },
2165
+ modalActions: {
2166
+ flexDirection: 'row',
2167
+ gap: 12,
2168
+ width: '100%'
2169
+ },
2170
+ modalActionButton: {
2171
+ flex: 1,
2172
+ flexDirection: 'row',
2173
+ alignItems: 'center',
2174
+ justifyContent: 'center',
2175
+ paddingVertical: 16,
2176
+ borderRadius: 12,
2177
+ gap: 8
2178
+ },
2179
+ modalActionText: {
2180
+ color: '#FFFFFF',
2181
+ fontSize: 16,
2182
+ fontWeight: '600'
2183
+ },
2184
+ // Drag and Drop styles
2185
+ dragDropOverlay: {
2186
+ position: 'absolute',
2187
+ top: 0,
2188
+ left: 0,
2189
+ right: 0,
2190
+ bottom: 0,
2191
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
2192
+ justifyContent: 'center',
2193
+ alignItems: 'center',
2194
+ zIndex: 1000
2195
+ },
2196
+ dragDropContent: {
2197
+ alignItems: 'center',
2198
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
2199
+ padding: 40,
2200
+ borderRadius: 20,
2201
+ borderWidth: 3,
2202
+ borderColor: '#007AFF',
2203
+ borderStyle: 'dashed'
2204
+ },
2205
+ dragDropTitle: {
2206
+ fontSize: 24,
2207
+ fontWeight: 'bold',
2208
+ marginTop: 16,
2209
+ marginBottom: 8
2210
+ },
2211
+ dragDropSubtitle: {
2212
+ fontSize: 16,
2213
+ textAlign: 'center'
2214
+ },
2215
+ // File Viewer styles
2216
+ fileViewerContainer: {
2217
+ flex: 1
2218
+ },
2219
+ fileViewerHeader: {
2220
+ flexDirection: 'row',
2221
+ alignItems: 'center',
2222
+ paddingHorizontal: 20,
2223
+ paddingVertical: 16,
2224
+ borderBottomWidth: 1
2225
+ },
2226
+ fileViewerTitleContainer: {
2227
+ flex: 1,
2228
+ marginHorizontal: 16
2229
+ },
2230
+ fileViewerTitle: {
2231
+ fontSize: 18,
2232
+ fontWeight: '600',
2233
+ fontFamily: _fonts.fontFamilies.phuduSemiBold,
2234
+ marginBottom: 2
2235
+ },
2236
+ fileViewerSubtitle: {
2237
+ fontSize: 14
2238
+ },
2239
+ fileViewerActions: {
2240
+ flexDirection: 'row',
2241
+ gap: 8
2242
+ },
2243
+ fileViewerContent: {
2244
+ flex: 1
2245
+ },
2246
+ fileViewerContentWithDetails: {
2247
+ paddingBottom: 20
2248
+ },
2249
+ fileViewerContentContainer: {
2250
+ flexGrow: 1,
2251
+ padding: 20
2252
+ },
2253
+ fileViewerLoading: {
2254
+ flex: 1,
2255
+ justifyContent: 'center',
2256
+ alignItems: 'center'
2257
+ },
2258
+ fileViewerLoadingText: {
2259
+ fontSize: 16,
2260
+ marginTop: 16
2261
+ },
2262
+ imageContainer: {
2263
+ alignItems: 'center',
2264
+ justifyContent: 'center',
2265
+ flex: 1
2266
+ },
2267
+ textContainer: {
2268
+ flex: 1,
2269
+ borderRadius: 12,
2270
+ borderWidth: 1,
2271
+ padding: 16,
2272
+ minHeight: 200,
2273
+ maxHeight: '80%'
2274
+ },
2275
+ textContent: {
2276
+ fontSize: 14,
2277
+ fontFamily: _reactNative.Platform.OS === 'web' ? 'monospace' : 'Courier',
2278
+ lineHeight: 20
2279
+ },
2280
+ unsupportedFileContainer: {
2281
+ flex: 1,
2282
+ justifyContent: 'center',
2283
+ alignItems: 'center',
2284
+ paddingVertical: 60,
2285
+ paddingHorizontal: 40
2286
+ },
2287
+ unsupportedFileTitle: {
2288
+ fontSize: 24,
2289
+ fontWeight: 'bold',
2290
+ fontFamily: _fonts.fontFamilies.phuduBold,
2291
+ marginTop: 16,
2292
+ marginBottom: 8,
2293
+ textAlign: 'center'
2294
+ },
2295
+ unsupportedFileDescription: {
2296
+ fontSize: 16,
2297
+ textAlign: 'center',
2298
+ lineHeight: 24,
2299
+ marginBottom: 32
2300
+ },
2301
+ downloadButtonLarge: {
2302
+ flexDirection: 'row',
2303
+ alignItems: 'center',
2304
+ paddingHorizontal: 24,
2305
+ paddingVertical: 16,
2306
+ borderRadius: 24,
2307
+ gap: 8
2308
+ },
2309
+ downloadButtonText: {
2310
+ color: '#FFFFFF',
2311
+ fontSize: 16,
2312
+ fontWeight: '600'
2313
+ },
2314
+ pdfContainer: {
2315
+ flex: 1,
2316
+ alignItems: 'center',
2317
+ justifyContent: 'center'
2318
+ },
2319
+ mediaContainer: {
2320
+ flex: 1,
2321
+ alignItems: 'center',
2322
+ justifyContent: 'center',
2323
+ padding: 20
2324
+ },
2325
+ unsupportedText: {
2326
+ fontSize: 16,
2327
+ textAlign: 'center',
2328
+ fontStyle: 'italic'
2329
+ },
2330
+ // File Details in Viewer styles
2331
+ fileDetailsSection: {
2332
+ margin: 16,
2333
+ marginTop: 0,
2334
+ padding: 20,
2335
+ borderRadius: 12,
2336
+ borderWidth: 1
2337
+ },
2338
+ fileDetailsSectionTitle: {
2339
+ fontSize: 18,
2340
+ fontWeight: '600',
2341
+ fontFamily: _fonts.fontFamilies.phuduSemiBold,
2342
+ flex: 1
2343
+ },
2344
+ fileDetailsSectionHeader: {
2345
+ flexDirection: 'row',
2346
+ alignItems: 'center',
2347
+ justifyContent: 'space-between',
2348
+ marginBottom: 16
2349
+ },
2350
+ fileDetailsSectionToggle: {
2351
+ padding: 4
2352
+ },
2353
+ fileDetailsActions: {
2354
+ flexDirection: 'row',
2355
+ gap: 12,
2356
+ marginTop: 16
2357
+ },
2358
+ fileDetailsActionButton: {
2359
+ flex: 1,
2360
+ flexDirection: 'row',
2361
+ alignItems: 'center',
2362
+ justifyContent: 'center',
2363
+ paddingVertical: 12,
2364
+ borderRadius: 8,
2365
+ gap: 6
2366
+ },
2367
+ fileDetailsActionText: {
2368
+ color: '#FFFFFF',
2369
+ fontSize: 14,
2370
+ fontWeight: '600'
2371
+ },
2372
+ // Header styles
2373
+ headerActions: {
2374
+ flexDirection: 'row',
2375
+ alignItems: 'center',
2376
+ gap: 16
2377
+ },
2378
+ viewModeToggle: {
2379
+ flexDirection: 'row',
2380
+ borderRadius: 24,
2381
+ padding: 3,
2382
+ overflow: 'hidden'
2383
+ },
2384
+ viewModeButton: {
2385
+ paddingHorizontal: 14,
2386
+ paddingVertical: 10,
2387
+ borderRadius: 20,
2388
+ minWidth: 44,
2389
+ alignItems: 'center',
2390
+ justifyContent: 'center',
2391
+ marginHorizontal: 1
2392
+ },
2393
+ // Photo Grid styles
2394
+ photoScrollContainer: {
2395
+ padding: 16
2396
+ },
2397
+ photoDateSection: {
2398
+ marginBottom: 24
2399
+ },
2400
+ photoDateHeader: {
2401
+ fontSize: 18,
2402
+ fontWeight: '600',
2403
+ fontFamily: _fonts.fontFamilies.phuduSemiBold,
2404
+ marginBottom: 12,
2405
+ paddingHorizontal: 4
2406
+ },
2407
+ photoGrid: {
2408
+ flexDirection: 'row',
2409
+ flexWrap: 'wrap',
2410
+ gap: 4,
2411
+ justifyContent: 'flex-start'
2412
+ },
2413
+ photoItem: {
2414
+ borderRadius: 8,
2415
+ overflow: 'hidden'
2416
+ },
2417
+ photoContainer: {
2418
+ width: '100%',
2419
+ height: '100%',
2420
+ position: 'relative',
2421
+ borderRadius: 8,
2422
+ overflow: 'hidden'
2423
+ },
2424
+ photoImage: {
2425
+ width: '100%',
2426
+ height: '100%'
2427
+ },
2428
+ // Justified Grid styles
2429
+ dimensionsLoadingIndicator: {
2430
+ flexDirection: 'row',
2431
+ alignItems: 'center',
2432
+ justifyContent: 'center',
2433
+ paddingVertical: 16,
2434
+ gap: 8
2435
+ },
2436
+ dimensionsLoadingText: {
2437
+ fontSize: 14,
2438
+ fontStyle: 'italic'
2439
+ },
2440
+ justifiedPhotoGrid: {
2441
+ gap: 4
2442
+ },
2443
+ justifiedPhotoRow: {
2444
+ flexDirection: 'row'
2445
+ },
2446
+ justifiedPhotoItem: {
2447
+ borderRadius: 6,
2448
+ overflow: 'hidden',
2449
+ position: 'relative'
2450
+ },
2451
+ justifiedPhotoContainer: {
2452
+ width: '100%',
2453
+ height: '100%',
2454
+ position: 'relative',
2455
+ borderRadius: 6,
2456
+ overflow: 'hidden',
2457
+ backgroundColor: '#F5F5F5'
2458
+ },
2459
+ justifiedPhotoImage: {
2460
+ width: '100%',
2461
+ height: '100%',
2462
+ borderRadius: 6
2463
+ },
2464
+ // Simple Photo Grid styles
2465
+ simplePhotoItem: {
2466
+ borderRadius: 8,
2467
+ overflow: 'hidden',
2468
+ backgroundColor: '#F5F5F5'
2469
+ },
2470
+ simplePhotoContainer: {
2471
+ width: '100%',
2472
+ height: '100%',
2473
+ position: 'relative',
2474
+ borderRadius: 8,
2475
+ overflow: 'hidden'
2476
+ },
2477
+ simplePhotoImage: {
2478
+ width: '100%',
2479
+ height: '100%',
2480
+ borderRadius: 8
2481
+ },
2482
+ // Loading skeleton styles
2483
+ photoSkeletonGrid: {
2484
+ flexDirection: 'row',
2485
+ flexWrap: 'wrap',
2486
+ gap: 4,
2487
+ marginTop: 20
2488
+ },
2489
+ photoSkeletonItem: {
2490
+ width: '32%',
2491
+ aspectRatio: 1,
2492
+ borderRadius: 8,
2493
+ marginBottom: 4
2494
+ }
2495
+ });
2496
+ var _default = exports.default = FileManagementScreen;
2497
+ //# sourceMappingURL=FileManagementScreen.js.map