@oxyhq/services 22.6.1 → 22.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js +99 -77
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +33 -40
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js +92 -88
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +169 -310
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/FileListSection.js +17 -14
- package/lib/commonjs/ui/screens/fileManagement/FileListSection.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +16 -20
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/UploadBar.js +29 -12
- package/lib/commonjs/ui/screens/fileManagement/UploadBar.js.map +1 -1
- package/lib/commonjs/ui/server.js +1 -1
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/module/ui/components/fileManagement/FileViewer.js +100 -78
- package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js +34 -41
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js +94 -90
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +171 -313
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/FileListSection.js +17 -14
- package/lib/module/ui/screens/fileManagement/FileListSection.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +17 -21
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/UploadBar.js +30 -13
- package/lib/module/ui/screens/fileManagement/UploadBar.js.map +1 -1
- package/lib/module/ui/server.js +1 -1
- package/lib/module/ui/server.js.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/UploadBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/UploadBar.d.ts.map +1 -1
- package/lib/typescript/module/ui/server.d.ts +1 -1
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/components/fileManagement/FileViewer.tsx +82 -68
- package/src/ui/components/fileManagement/UploadPreview.tsx +34 -29
- package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +14 -12
- package/src/ui/screens/FileManagementScreen.tsx +123 -300
- package/src/ui/screens/fileManagement/FileListSection.tsx +10 -10
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +16 -19
- package/src/ui/screens/fileManagement/UploadBar.tsx +29 -14
- package/src/ui/server.ts +1 -1
- package/lib/commonjs/ui/components/fileManagement/styles.js +0 -832
- package/lib/commonjs/ui/components/fileManagement/styles.js.map +0 -1
- package/lib/module/ui/components/fileManagement/styles.js +0 -828
- package/lib/module/ui/components/fileManagement/styles.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts +0 -825
- package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/fileManagement/styles.d.ts +0 -825
- package/lib/typescript/module/ui/components/fileManagement/styles.d.ts.map +0 -1
- package/src/ui/components/fileManagement/styles.ts +0 -823
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import { useMemo, useState, useEffect } from 'react';
|
|
3
|
-
import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator, Image } from 'react-native';
|
|
3
|
+
import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator, Image, StyleSheet } from 'react-native';
|
|
4
4
|
import { Image as ExpoImage } from 'expo-image';
|
|
5
5
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
6
6
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
7
7
|
import type { FileMetadata } from '@oxyhq/core';
|
|
8
8
|
import { formatFileSize } from '../../utils/fileManagement';
|
|
9
|
-
import { fileManagementStyles } from './styles';
|
|
10
9
|
import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list';
|
|
11
10
|
|
|
12
11
|
interface FileViewerProps {
|
|
@@ -21,6 +20,30 @@ interface FileViewerProps {
|
|
|
21
20
|
isOwner: boolean;
|
|
22
21
|
}
|
|
23
22
|
|
|
23
|
+
// Genuinely-inline-only styles: the full-bleed blurred background is an
|
|
24
|
+
// `expo-image` (no className remap), and `imageContainer` is a measurement
|
|
25
|
+
// wrapper whose `onLayout` never fires on web for a className'd component.
|
|
26
|
+
const viewerStyles = StyleSheet.create({
|
|
27
|
+
backgroundImage: {
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
top: 0,
|
|
30
|
+
left: 0,
|
|
31
|
+
right: 0,
|
|
32
|
+
bottom: 0,
|
|
33
|
+
width: '100%',
|
|
34
|
+
height: '100%',
|
|
35
|
+
zIndex: 0,
|
|
36
|
+
},
|
|
37
|
+
imageContainer: {
|
|
38
|
+
alignItems: 'center',
|
|
39
|
+
justifyContent: 'center',
|
|
40
|
+
flex: 1,
|
|
41
|
+
marginTop: 56,
|
|
42
|
+
marginBottom: 8,
|
|
43
|
+
paddingHorizontal: 12,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
24
47
|
export const FileViewer: React.FC<FileViewerProps> = ({
|
|
25
48
|
file,
|
|
26
49
|
fileContent,
|
|
@@ -43,10 +66,6 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
43
66
|
const isVideo = file.contentType.startsWith('video/');
|
|
44
67
|
const isAudio = file.contentType.startsWith('audio/');
|
|
45
68
|
|
|
46
|
-
const bgColor = isImage && fileContent
|
|
47
|
-
? 'transparent'
|
|
48
|
-
: undefined;
|
|
49
|
-
|
|
50
69
|
const [containerWidth, setContainerWidth] = useState<number>(0);
|
|
51
70
|
const [imageDimensions, setImageDimensions] = useState<{ width: number; height: number } | null>(null);
|
|
52
71
|
|
|
@@ -75,27 +94,27 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
75
94
|
// Return default size while loading
|
|
76
95
|
return { width: 400, height: 400 };
|
|
77
96
|
}
|
|
78
|
-
|
|
97
|
+
|
|
79
98
|
const maxWidth = containerWidth - 24; // Account for padding
|
|
80
99
|
const maxHeight = 500;
|
|
81
100
|
const aspectRatio = imageDimensions.width / imageDimensions.height;
|
|
82
|
-
|
|
101
|
+
|
|
83
102
|
// Start with natural dimensions
|
|
84
103
|
let displayWidth = imageDimensions.width;
|
|
85
104
|
let displayHeight = imageDimensions.height;
|
|
86
|
-
|
|
105
|
+
|
|
87
106
|
// Only scale down if exceeds max width
|
|
88
107
|
if (displayWidth > maxWidth) {
|
|
89
108
|
displayWidth = maxWidth;
|
|
90
109
|
displayHeight = displayWidth / aspectRatio;
|
|
91
110
|
}
|
|
92
|
-
|
|
111
|
+
|
|
93
112
|
// Only scale down if exceeds max height
|
|
94
113
|
if (displayHeight > maxHeight) {
|
|
95
114
|
displayHeight = maxHeight;
|
|
96
115
|
displayWidth = displayHeight * aspectRatio;
|
|
97
116
|
}
|
|
98
|
-
|
|
117
|
+
|
|
99
118
|
return { width: displayWidth, height: displayHeight };
|
|
100
119
|
}, [imageDimensions, containerWidth]);
|
|
101
120
|
|
|
@@ -154,28 +173,30 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
154
173
|
|
|
155
174
|
return (
|
|
156
175
|
<View
|
|
157
|
-
className={isImage && fileContent ?
|
|
158
|
-
style={[fileManagementStyles.fileViewerContainer, bgColor ? { backgroundColor: bgColor } : undefined]}
|
|
176
|
+
className={`flex-1 relative ${isImage && fileContent ? '' : 'bg-bg'}`}
|
|
159
177
|
>
|
|
160
178
|
{/* Blurred Background Image - only for images */}
|
|
161
179
|
{isImage && fileContent && (
|
|
162
180
|
<>
|
|
163
181
|
<ExpoImage
|
|
164
182
|
source={{ uri: fileContent }}
|
|
165
|
-
style={
|
|
183
|
+
style={viewerStyles.backgroundImage}
|
|
166
184
|
contentFit="cover"
|
|
167
185
|
blurRadius={50}
|
|
168
186
|
transition={120}
|
|
169
187
|
cachePolicy="memory-disk"
|
|
170
188
|
/>
|
|
171
|
-
<View
|
|
189
|
+
<View
|
|
190
|
+
className="absolute top-0 left-0 right-0 bottom-0"
|
|
191
|
+
style={{ backgroundColor: colors.overlay, zIndex: 1 }}
|
|
192
|
+
/>
|
|
172
193
|
</>
|
|
173
194
|
)}
|
|
174
195
|
|
|
175
196
|
{/* Floating Back Button */}
|
|
176
197
|
<TouchableOpacity
|
|
177
|
-
className="bg-card"
|
|
178
|
-
style={
|
|
198
|
+
className="bg-card absolute top-[12px] left-[12px] w-[40px] h-[40px] rounded-full items-center justify-center"
|
|
199
|
+
style={{ zIndex: 10 }}
|
|
179
200
|
onPress={onClose}
|
|
180
201
|
>
|
|
181
202
|
<MaterialCommunityIcons name="arrow-left" size={20} color={colors.text} />
|
|
@@ -183,8 +204,8 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
183
204
|
|
|
184
205
|
{/* Floating Download Button */}
|
|
185
206
|
<TouchableOpacity
|
|
186
|
-
className="bg-card"
|
|
187
|
-
style={
|
|
207
|
+
className="bg-card absolute top-[12px] right-[12px] w-[40px] h-[40px] rounded-full items-center justify-center"
|
|
208
|
+
style={{ zIndex: 10 }}
|
|
188
209
|
onPress={() => onDownload(file.id, file.filename)}
|
|
189
210
|
>
|
|
190
211
|
<MaterialCommunityIcons name="download" size={20} color={colors.primary} />
|
|
@@ -192,19 +213,20 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
192
213
|
|
|
193
214
|
{/* File Content */}
|
|
194
215
|
<ScrollView
|
|
195
|
-
|
|
196
|
-
|
|
216
|
+
className="flex-1 relative"
|
|
217
|
+
style={{ zIndex: 2 }}
|
|
218
|
+
contentContainerClassName="grow px-[12px] pt-0 pb-[8px]"
|
|
197
219
|
>
|
|
198
220
|
{loadingFileContent ? (
|
|
199
|
-
<View
|
|
221
|
+
<View className="flex-1 justify-center items-center">
|
|
200
222
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
201
|
-
<Text className="text-text"
|
|
223
|
+
<Text className="text-text text-[16px] mt-[16px]">
|
|
202
224
|
Loading file content...
|
|
203
225
|
</Text>
|
|
204
226
|
</View>
|
|
205
227
|
) : isImage && fileContent ? (
|
|
206
228
|
<View
|
|
207
|
-
style={
|
|
229
|
+
style={viewerStyles.imageContainer}
|
|
208
230
|
onLayout={(e) => {
|
|
209
231
|
const width = e.nativeEvent.layout.width;
|
|
210
232
|
if (width > 0) {
|
|
@@ -213,13 +235,11 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
213
235
|
}}
|
|
214
236
|
>
|
|
215
237
|
<View
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
]}
|
|
238
|
+
className="rounded-[24px] overflow-hidden self-center"
|
|
239
|
+
style={{
|
|
240
|
+
width: imageDisplaySize.width,
|
|
241
|
+
height: imageDisplaySize.height,
|
|
242
|
+
}}
|
|
223
243
|
>
|
|
224
244
|
<ExpoImage
|
|
225
245
|
source={{ uri: fileContent }}
|
|
@@ -238,116 +258,112 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
238
258
|
</View>
|
|
239
259
|
</View>
|
|
240
260
|
) : isText && fileContent ? (
|
|
241
|
-
<View className="bg-card" style={
|
|
242
|
-
<ScrollView
|
|
243
|
-
<Text className="text-text" style={
|
|
261
|
+
<View className="bg-card flex-1 rounded-[18px] p-[12px] min-h-[180px]" style={{ maxHeight: '80%' }}>
|
|
262
|
+
<ScrollView className="flex-1" nestedScrollEnabled>
|
|
263
|
+
<Text className="text-text text-[14px] leading-[20px]" style={{ fontFamily: 'monospace' }}>
|
|
244
264
|
{fileContent}
|
|
245
265
|
</Text>
|
|
246
266
|
</ScrollView>
|
|
247
267
|
</View>
|
|
248
268
|
) : isPDF && fileContent ? (
|
|
249
|
-
<View
|
|
269
|
+
<View className="flex-1 justify-center items-center py-[32px] px-[24px]">
|
|
250
270
|
<MaterialCommunityIcons
|
|
251
271
|
name="file-pdf-box"
|
|
252
272
|
size={64}
|
|
253
273
|
color={colors.textSecondary}
|
|
254
274
|
/>
|
|
255
|
-
<Text className="text-text"
|
|
275
|
+
<Text className="text-text text-[24px] font-bold mt-[16px] mb-[8px] text-center">
|
|
256
276
|
PDF Preview Not Available
|
|
257
277
|
</Text>
|
|
258
|
-
<Text className="text-text-secondary"
|
|
278
|
+
<Text className="text-text-secondary text-[16px] text-center leading-[24px] mb-[32px]">
|
|
259
279
|
PDF files cannot be previewed in this viewer.{'\n'}
|
|
260
280
|
Download the file to view its contents.
|
|
261
281
|
</Text>
|
|
262
282
|
<TouchableOpacity
|
|
263
|
-
className="bg-primary"
|
|
264
|
-
style={fileManagementStyles.downloadButtonLarge}
|
|
283
|
+
className="bg-primary flex-row items-center px-[18px] py-[12px] rounded-full gap-[8px]"
|
|
265
284
|
onPress={() => onDownload(file.id, file.filename)}
|
|
266
285
|
>
|
|
267
286
|
<MaterialCommunityIcons name="download" size={18} color={colors.primaryForeground} />
|
|
268
|
-
<Text
|
|
287
|
+
<Text className="text-[16px] font-semibold" style={{ color: colors.primaryForeground }}>Download PDF</Text>
|
|
269
288
|
</TouchableOpacity>
|
|
270
289
|
</View>
|
|
271
290
|
) : isVideo && fileContent ? (
|
|
272
|
-
<View
|
|
291
|
+
<View className="flex-1 justify-center items-center py-[32px] px-[24px]">
|
|
273
292
|
<MaterialCommunityIcons
|
|
274
293
|
name="video-outline"
|
|
275
294
|
size={64}
|
|
276
295
|
color={colors.textSecondary}
|
|
277
296
|
/>
|
|
278
|
-
<Text className="text-text"
|
|
297
|
+
<Text className="text-text text-[24px] font-bold mt-[16px] mb-[8px] text-center">
|
|
279
298
|
Video Playback Not Available
|
|
280
299
|
</Text>
|
|
281
|
-
<Text className="text-text-secondary"
|
|
300
|
+
<Text className="text-text-secondary text-[16px] text-center leading-[24px] mb-[32px]">
|
|
282
301
|
Video playback is not supported in this viewer.{'\n'}
|
|
283
302
|
Download the file to view it.
|
|
284
303
|
</Text>
|
|
285
304
|
<TouchableOpacity
|
|
286
|
-
className="bg-primary"
|
|
287
|
-
style={fileManagementStyles.downloadButtonLarge}
|
|
305
|
+
className="bg-primary flex-row items-center px-[18px] py-[12px] rounded-full gap-[8px]"
|
|
288
306
|
onPress={() => onDownload(file.id, file.filename)}
|
|
289
307
|
>
|
|
290
308
|
<MaterialCommunityIcons name="download" size={18} color={colors.primaryForeground} />
|
|
291
|
-
<Text
|
|
309
|
+
<Text className="text-[16px] font-semibold" style={{ color: colors.primaryForeground }}>Download Video</Text>
|
|
292
310
|
</TouchableOpacity>
|
|
293
311
|
</View>
|
|
294
312
|
) : isAudio && fileContent ? (
|
|
295
|
-
<View
|
|
313
|
+
<View className="flex-1 justify-center items-center py-[32px] px-[24px]">
|
|
296
314
|
<MaterialCommunityIcons
|
|
297
315
|
name="music-note-outline"
|
|
298
316
|
size={64}
|
|
299
317
|
color={colors.textSecondary}
|
|
300
318
|
/>
|
|
301
|
-
<Text className="text-text"
|
|
319
|
+
<Text className="text-text text-[24px] font-bold mt-[16px] mb-[8px] text-center">
|
|
302
320
|
Audio Playback Not Available
|
|
303
321
|
</Text>
|
|
304
|
-
<Text className="text-text-secondary"
|
|
322
|
+
<Text className="text-text-secondary text-[16px] text-center leading-[24px] mb-[32px]">
|
|
305
323
|
Audio playback is not supported in this viewer.{'\n'}
|
|
306
324
|
Download the file to listen to it.
|
|
307
325
|
</Text>
|
|
308
326
|
<TouchableOpacity
|
|
309
|
-
className="bg-primary"
|
|
310
|
-
style={fileManagementStyles.downloadButtonLarge}
|
|
327
|
+
className="bg-primary flex-row items-center px-[18px] py-[12px] rounded-full gap-[8px]"
|
|
311
328
|
onPress={() => onDownload(file.id, file.filename)}
|
|
312
329
|
>
|
|
313
330
|
<MaterialCommunityIcons name="download" size={18} color={colors.primaryForeground} />
|
|
314
|
-
<Text
|
|
331
|
+
<Text className="text-[16px] font-semibold" style={{ color: colors.primaryForeground }}>Download Audio</Text>
|
|
315
332
|
</TouchableOpacity>
|
|
316
333
|
</View>
|
|
317
334
|
) : (
|
|
318
|
-
<View
|
|
335
|
+
<View className="flex-1 justify-center items-center py-[32px] px-[24px]">
|
|
319
336
|
<MaterialCommunityIcons
|
|
320
337
|
name="file-outline"
|
|
321
338
|
size={64}
|
|
322
339
|
color={colors.textSecondary}
|
|
323
340
|
/>
|
|
324
|
-
<Text className="text-text"
|
|
341
|
+
<Text className="text-text text-[24px] font-bold mt-[16px] mb-[8px] text-center">
|
|
325
342
|
Preview Not Available
|
|
326
343
|
</Text>
|
|
327
|
-
<Text className="text-text-secondary"
|
|
344
|
+
<Text className="text-text-secondary text-[16px] text-center leading-[24px] mb-[32px]">
|
|
328
345
|
This file type cannot be previewed.{'\n'}
|
|
329
346
|
Download the file to view its contents.
|
|
330
347
|
</Text>
|
|
331
348
|
<TouchableOpacity
|
|
332
|
-
className="bg-primary"
|
|
333
|
-
style={fileManagementStyles.downloadButtonLarge}
|
|
349
|
+
className="bg-primary flex-row items-center px-[18px] py-[12px] rounded-full gap-[8px]"
|
|
334
350
|
onPress={() => onDownload(file.id, file.filename)}
|
|
335
351
|
>
|
|
336
352
|
<MaterialCommunityIcons name="download" size={18} color={colors.primaryForeground} />
|
|
337
|
-
<Text
|
|
353
|
+
<Text className="text-[16px] font-semibold" style={{ color: colors.primaryForeground }}>Download File</Text>
|
|
338
354
|
</TouchableOpacity>
|
|
339
355
|
</View>
|
|
340
356
|
)}
|
|
341
357
|
</ScrollView>
|
|
342
358
|
|
|
343
359
|
{/* File Details Section - at bottom */}
|
|
344
|
-
<View className="bg-card" style={
|
|
345
|
-
<View
|
|
346
|
-
<Text className="text-text"
|
|
360
|
+
<View className="bg-card mx-[12px] mt-[8px] mb-[12px] rounded-[18px] overflow-hidden relative" style={{ zIndex: 2 }}>
|
|
361
|
+
<View className="flex-row items-center justify-between px-[12px] pt-[10px] pb-[6px]">
|
|
362
|
+
<Text className="text-text text-[18px] font-semibold flex-1">
|
|
347
363
|
File Details
|
|
348
364
|
</Text>
|
|
349
365
|
<TouchableOpacity
|
|
350
|
-
|
|
366
|
+
className="p-[4px]"
|
|
351
367
|
onPress={onToggleDetails}
|
|
352
368
|
>
|
|
353
369
|
<MaterialCommunityIcons
|
|
@@ -360,7 +376,7 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
360
376
|
|
|
361
377
|
{showFileDetailsInViewer && (
|
|
362
378
|
<>
|
|
363
|
-
<View
|
|
379
|
+
<View className="px-[12px] pb-[8px]">
|
|
364
380
|
<SettingsListGroup>
|
|
365
381
|
{fileDetailItems.map((item) => (
|
|
366
382
|
<SettingsListItem
|
|
@@ -375,17 +391,16 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
375
391
|
</View>
|
|
376
392
|
|
|
377
393
|
{isOwner && (
|
|
378
|
-
<View
|
|
394
|
+
<View className="flex-row gap-[12px] mt-[8px] px-[12px] pb-[10px]">
|
|
379
395
|
<TouchableOpacity
|
|
380
|
-
className="bg-destructive"
|
|
381
|
-
style={fileManagementStyles.fileDetailsActionButton}
|
|
396
|
+
className="bg-destructive flex-1 flex-row items-center justify-center py-[12px] rounded-full gap-[6px]"
|
|
382
397
|
onPress={() => {
|
|
383
398
|
onClose();
|
|
384
399
|
onDelete(file.id, file.filename);
|
|
385
400
|
}}
|
|
386
401
|
>
|
|
387
402
|
<MaterialCommunityIcons name="delete" size={16} color={colors.negativeForeground} />
|
|
388
|
-
<Text
|
|
403
|
+
<Text className="text-[14px] font-semibold" style={{ color: colors.negativeForeground }}>Delete</Text>
|
|
389
404
|
</TouchableOpacity>
|
|
390
405
|
</View>
|
|
391
406
|
)}
|
|
@@ -395,4 +410,3 @@ export const FileViewer: React.FC<FileViewerProps> = ({
|
|
|
395
410
|
</View>
|
|
396
411
|
);
|
|
397
412
|
};
|
|
398
|
-
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
|
|
2
|
+
import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from 'react-native';
|
|
3
3
|
import { Image as ExpoImage } from 'expo-image';
|
|
4
4
|
import { Ionicons } from '@expo/vector-icons';
|
|
5
5
|
import { Dialog, type DialogControlProps } from '@oxyhq/bloom';
|
|
6
6
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
7
7
|
import type { RNFileDescriptor } from '@oxyhq/core';
|
|
8
8
|
import { formatFileSize, getFileIcon } from '../../utils/fileManagement';
|
|
9
|
-
import { fileManagementStyles } from './styles';
|
|
10
9
|
|
|
11
10
|
interface PendingFile {
|
|
12
11
|
file: File | Blob | RNFileDescriptor;
|
|
@@ -25,6 +24,15 @@ interface UploadPreviewProps {
|
|
|
25
24
|
inline?: boolean;
|
|
26
25
|
}
|
|
27
26
|
|
|
27
|
+
// `expo-image` takes no className remap — the thumbnail size stays inline.
|
|
28
|
+
const previewStyles = StyleSheet.create({
|
|
29
|
+
thumbnail: {
|
|
30
|
+
width: 60,
|
|
31
|
+
height: 60,
|
|
32
|
+
borderRadius: 8,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
28
36
|
const UploadPreviewContent: React.FC<{
|
|
29
37
|
pendingFiles: PendingFile[];
|
|
30
38
|
onConfirm: () => void;
|
|
@@ -38,13 +46,13 @@ const UploadPreviewContent: React.FC<{
|
|
|
38
46
|
onRemoveFile,
|
|
39
47
|
showActions = true,
|
|
40
48
|
}) => {
|
|
41
|
-
const { colors
|
|
49
|
+
const { colors } = useTheme();
|
|
42
50
|
const totalSize = pendingFiles.reduce((sum, f) => sum + f.size, 0);
|
|
43
51
|
|
|
44
52
|
return (
|
|
45
|
-
<View
|
|
46
|
-
<View className="border-b border-border"
|
|
47
|
-
<Text className="text-text"
|
|
53
|
+
<View className="bg-bg flex-1">
|
|
54
|
+
<View className="border-b border-border flex-row items-center justify-between px-[16px] py-[16px]">
|
|
55
|
+
<Text className="text-text text-[20px] font-bold">
|
|
48
56
|
Review Files ({pendingFiles.length})
|
|
49
57
|
</Text>
|
|
50
58
|
<TouchableOpacity onPress={onCancel}>
|
|
@@ -52,23 +60,25 @@ const UploadPreviewContent: React.FC<{
|
|
|
52
60
|
</TouchableOpacity>
|
|
53
61
|
</View>
|
|
54
62
|
|
|
55
|
-
<ScrollView
|
|
63
|
+
<ScrollView className="flex-1 p-[16px]">
|
|
56
64
|
{pendingFiles.map((pendingFile, index) => {
|
|
57
65
|
const isImage = pendingFile.type.startsWith('image/');
|
|
58
66
|
return (
|
|
59
67
|
<View
|
|
60
68
|
key={index}
|
|
61
|
-
className="bg-secondary border-border"
|
|
62
|
-
style={fileManagementStyles.uploadPreviewItem}
|
|
69
|
+
className="bg-secondary border-border flex-row items-center p-[12px] rounded-[12px] border mb-[12px] gap-[12px]"
|
|
63
70
|
>
|
|
64
71
|
{isImage && pendingFile.preview ? (
|
|
65
72
|
<ExpoImage
|
|
66
73
|
source={{ uri: pendingFile.preview }}
|
|
67
|
-
style={
|
|
74
|
+
style={previewStyles.thumbnail}
|
|
68
75
|
contentFit="cover"
|
|
69
76
|
/>
|
|
70
77
|
) : (
|
|
71
|
-
<View
|
|
78
|
+
<View
|
|
79
|
+
className="w-[60px] h-[60px] rounded-[8px] items-center justify-center"
|
|
80
|
+
style={{ backgroundColor: colors.backgroundSecondary }}
|
|
81
|
+
>
|
|
72
82
|
<Ionicons
|
|
73
83
|
name={getFileIcon(pendingFile.type)}
|
|
74
84
|
size={32}
|
|
@@ -76,16 +86,16 @@ const UploadPreviewContent: React.FC<{
|
|
|
76
86
|
/>
|
|
77
87
|
</View>
|
|
78
88
|
)}
|
|
79
|
-
<View
|
|
80
|
-
<Text className="text-text"
|
|
89
|
+
<View className="flex-1 min-w-0">
|
|
90
|
+
<Text className="text-text text-[16px] font-semibold mb-[4px]" numberOfLines={1}>
|
|
81
91
|
{pendingFile.name}
|
|
82
92
|
</Text>
|
|
83
|
-
<Text className="text-text-secondary"
|
|
93
|
+
<Text className="text-text-secondary text-[13px]">
|
|
84
94
|
{formatFileSize(pendingFile.size)} • {pendingFile.type}
|
|
85
95
|
</Text>
|
|
86
96
|
</View>
|
|
87
97
|
<TouchableOpacity
|
|
88
|
-
|
|
98
|
+
className="p-[4px]"
|
|
89
99
|
onPress={() => onRemoveFile(index)}
|
|
90
100
|
>
|
|
91
101
|
<Ionicons name="close-circle" size={24} color={colors.error} />
|
|
@@ -95,36 +105,31 @@ const UploadPreviewContent: React.FC<{
|
|
|
95
105
|
})}
|
|
96
106
|
</ScrollView>
|
|
97
107
|
|
|
98
|
-
<View className="border-t border-border"
|
|
99
|
-
<View
|
|
100
|
-
<Text className="text-text"
|
|
108
|
+
<View className="border-t border-border p-[16px]">
|
|
109
|
+
<View className="flex-row justify-between mb-[16px]">
|
|
110
|
+
<Text className="text-text text-[15px] font-semibold">
|
|
101
111
|
{pendingFiles.length} file{pendingFiles.length !== 1 ? 's' : ''}
|
|
102
112
|
</Text>
|
|
103
|
-
<Text className="text-text"
|
|
113
|
+
<Text className="text-text text-[15px] font-semibold">
|
|
104
114
|
{formatFileSize(totalSize)}
|
|
105
115
|
</Text>
|
|
106
116
|
</View>
|
|
107
117
|
{showActions && (
|
|
108
|
-
<View
|
|
118
|
+
<View className="flex-row gap-[12px]">
|
|
109
119
|
<TouchableOpacity
|
|
110
|
-
className="border-border"
|
|
111
|
-
style={[
|
|
112
|
-
fileManagementStyles.uploadPreviewCancelButton,
|
|
113
|
-
{ backgroundColor: 'transparent' }
|
|
114
|
-
]}
|
|
120
|
+
className="bg-transparent border-border flex-1 py-[14px] rounded-[12px] border items-center justify-center"
|
|
115
121
|
onPress={onCancel}
|
|
116
122
|
>
|
|
117
|
-
<Text className="text-text"
|
|
123
|
+
<Text className="text-text text-[16px] font-semibold">
|
|
118
124
|
Cancel
|
|
119
125
|
</Text>
|
|
120
126
|
</TouchableOpacity>
|
|
121
127
|
<TouchableOpacity
|
|
122
|
-
className="bg-primary"
|
|
123
|
-
style={fileManagementStyles.uploadPreviewConfirmButton}
|
|
128
|
+
className="bg-primary flex-[2] flex-row items-center justify-center py-[14px] rounded-[12px] gap-[8px]"
|
|
124
129
|
onPress={onConfirm}
|
|
125
130
|
>
|
|
126
131
|
<Ionicons name="cloud-upload" size={20} color={colors.primaryForeground} />
|
|
127
|
-
<Text
|
|
132
|
+
<Text className="text-[16px] font-semibold" style={{ color: colors.primaryForeground }}>Upload</Text>
|
|
128
133
|
</TouchableOpacity>
|
|
129
134
|
</View>
|
|
130
135
|
)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState, useCallback } from 'react';
|
|
2
|
-
import { View, Text, type LayoutChangeEvent } from 'react-native';
|
|
2
|
+
import { View, Text, useWindowDimensions, type LayoutChangeEvent } from 'react-native';
|
|
3
3
|
import type { FileMetadata } from '@oxyhq/core';
|
|
4
4
|
|
|
5
5
|
export interface JustifiedPhotoGridProps {
|
|
@@ -37,15 +37,22 @@ const JustifiedPhotoGrid: React.FC<JustifiedPhotoGridProps> = ({
|
|
|
37
37
|
maxRowHeight = 300,
|
|
38
38
|
dateFormatLocale = 'en-US',
|
|
39
39
|
}) => {
|
|
40
|
-
// Responsive width measurement if not explicitly provided
|
|
40
|
+
// Responsive width measurement if not explicitly provided. Never gate the
|
|
41
|
+
// whole grid on measurement — onLayout is unreliable on web (same class of
|
|
42
|
+
// bug fixed in PhotoPickerSection). Fall back to window width until measured.
|
|
43
|
+
const { width: windowWidth } = useWindowDimensions();
|
|
41
44
|
const [measuredWidth, setMeasuredWidth] = useState<number | null>(null);
|
|
42
|
-
const
|
|
45
|
+
const resolvedExplicitWidth =
|
|
46
|
+
explicitWidth != null && explicitWidth > 0 ? explicitWidth : null;
|
|
47
|
+
const resolvedMeasuredWidth =
|
|
48
|
+
measuredWidth != null && measuredWidth > 0 ? measuredWidth : null;
|
|
49
|
+
const effectiveWidth = resolvedExplicitWidth ?? resolvedMeasuredWidth ?? windowWidth;
|
|
43
50
|
|
|
44
51
|
const onLayoutContainer = useCallback((e: LayoutChangeEvent) => {
|
|
45
|
-
if (
|
|
52
|
+
if (resolvedExplicitWidth != null) return; // ignore if controlled
|
|
46
53
|
const w = e.nativeEvent.layout.width;
|
|
47
54
|
setMeasuredWidth(prev => (prev === w ? prev : w));
|
|
48
|
-
}, [
|
|
55
|
+
}, [resolvedExplicitWidth]);
|
|
49
56
|
// Ensure dimensions are loaded for displayed photos
|
|
50
57
|
useEffect(() => {
|
|
51
58
|
loadPhotoDimensions(photos);
|
|
@@ -72,11 +79,8 @@ const JustifiedPhotoGrid: React.FC<JustifiedPhotoGridProps> = ({
|
|
|
72
79
|
|
|
73
80
|
return (
|
|
74
81
|
<View style={{ width: '100%' }} onLayout={onLayoutContainer}>
|
|
75
|
-
{
|
|
76
|
-
|
|
77
|
-
<>
|
|
78
|
-
{sortedDates.map((date: string) => {
|
|
79
|
-
const dayPhotos = photosByDate[date];
|
|
82
|
+
{sortedDates.map((date: string) => {
|
|
83
|
+
const dayPhotos = photosByDate[date];
|
|
80
84
|
// createJustifiedRows should build rows such that the "ideal" height (availableWidth / totalAspect) stays within min/max.
|
|
81
85
|
// We pass the effective container width.
|
|
82
86
|
const dateWidth = dateWidths[date] ?? effectiveWidth; // fallback to overall width until measured
|
|
@@ -148,8 +152,6 @@ const JustifiedPhotoGrid: React.FC<JustifiedPhotoGridProps> = ({
|
|
|
148
152
|
</View>
|
|
149
153
|
);
|
|
150
154
|
})}
|
|
151
|
-
</>
|
|
152
|
-
)}
|
|
153
155
|
</View>
|
|
154
156
|
);
|
|
155
157
|
};
|