@oxyhq/services 6.9.3 → 6.9.5
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/client.js +0 -7
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/IconButton/IconButton.js +3 -3
- package/lib/commonjs/ui/components/IconButton/IconButton.js.map +1 -1
- package/lib/commonjs/ui/components/feedback/FormInput.js.map +1 -1
- package/lib/commonjs/ui/components/icon/OxyIcon.js.map +1 -1
- package/lib/commonjs/ui/components/types.js +4 -0
- package/lib/commonjs/ui/screens/AppInfoScreen.js +66 -60
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +139 -79
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +39 -29
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/client.js +0 -1
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/IconButton/IconButton.js +3 -3
- package/lib/module/ui/components/IconButton/IconButton.js.map +1 -1
- package/lib/module/ui/components/feedback/FormInput.js.map +1 -1
- package/lib/module/ui/components/icon/OxyIcon.js.map +1 -1
- package/lib/module/ui/components/types.js +2 -0
- package/lib/module/ui/screens/AppInfoScreen.js +66 -60
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +139 -79
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js +39 -29
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +0 -1
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/types.d.ts +18 -17
- package/lib/typescript/commonjs/ui/components/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +0 -1
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/types.d.ts +18 -17
- package/lib/typescript/module/ui/components/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/ui/client.ts +0 -1
- package/src/ui/components/IconButton/IconButton.tsx +2 -2
- package/src/ui/components/feedback/FormInput.tsx +1 -1
- package/src/ui/components/icon/OxyIcon.tsx +1 -1
- package/src/ui/components/types.tsx +19 -17
- package/src/ui/screens/AppInfoScreen.tsx +63 -61
- package/src/ui/screens/FileManagementScreen.tsx +130 -121
- package/src/ui/screens/SessionManagementScreen.tsx +30 -28
- package/lib/commonjs/ui/components/AnimationExample.js +0 -213
- package/lib/commonjs/ui/components/AnimationExample.js.map +0 -1
- package/lib/commonjs/ui/components/ErrorBoundary.js +0 -145
- package/lib/commonjs/ui/components/ErrorBoundary.js.map +0 -1
- package/lib/commonjs/ui/components/WebOxyProvider.js +0 -106
- package/lib/commonjs/ui/components/WebOxyProvider.js.map +0 -1
- package/lib/module/ui/components/AnimationExample.js +0 -209
- package/lib/module/ui/components/AnimationExample.js.map +0 -1
- package/lib/module/ui/components/ErrorBoundary.js +0 -139
- package/lib/module/ui/components/ErrorBoundary.js.map +0 -1
- package/lib/module/ui/components/WebOxyProvider.js +0 -102
- package/lib/module/ui/components/WebOxyProvider.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/AnimationExample.d.ts +0 -4
- package/lib/typescript/commonjs/ui/components/AnimationExample.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts +0 -31
- package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/WebOxyProvider.d.ts +0 -52
- package/lib/typescript/commonjs/ui/components/WebOxyProvider.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/AnimationExample.d.ts +0 -4
- package/lib/typescript/module/ui/components/AnimationExample.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/ErrorBoundary.d.ts +0 -31
- package/lib/typescript/module/ui/components/ErrorBoundary.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/WebOxyProvider.d.ts +0 -52
- package/lib/typescript/module/ui/components/WebOxyProvider.d.ts.map +0 -1
- package/src/ui/components/AnimationExample.tsx +0 -195
- package/src/ui/components/ErrorBoundary.tsx +0 -154
- package/src/ui/components/WebOxyProvider.tsx +0 -117
|
@@ -17,6 +17,7 @@ var _useThemeStyles = require("../hooks/useThemeStyles.js");
|
|
|
17
17
|
var _useColorScheme = require("../hooks/useColorScheme.js");
|
|
18
18
|
var _themeUtils = require("../utils/themeUtils.js");
|
|
19
19
|
var _OxyContext = require("../context/OxyContext.js");
|
|
20
|
+
var _useI18n = require("../hooks/useI18n.js");
|
|
20
21
|
var _useAccountMutations = require("../hooks/mutations/useAccountMutations.js");
|
|
21
22
|
var _fileManagement = require("../utils/fileManagement.js");
|
|
22
23
|
var _FileViewer = require("../components/fileManagement/FileViewer.js");
|
|
@@ -41,6 +42,9 @@ const loadDocumentPicker = async () => {
|
|
|
41
42
|
|
|
42
43
|
// @ts-ignore - MaterialCommunityIcons is available at runtime
|
|
43
44
|
|
|
45
|
+
/** Extract error message from unknown error type */
|
|
46
|
+
const getErrorMessage = error => error instanceof Error ? getErrorMessage(error) : typeof error === 'string' ? error : undefined;
|
|
47
|
+
|
|
44
48
|
// Animated button component for smooth transitions
|
|
45
49
|
const AnimatedButton = ({
|
|
46
50
|
isSelected,
|
|
@@ -109,6 +113,9 @@ const FileManagementScreen = ({
|
|
|
109
113
|
user,
|
|
110
114
|
oxyServices
|
|
111
115
|
} = (0, _OxyContext.useOxy)();
|
|
116
|
+
const {
|
|
117
|
+
t
|
|
118
|
+
} = (0, _useI18n.useI18n)();
|
|
112
119
|
const uploadFileMutation = (0, _useAccountMutations.useUploadFile)();
|
|
113
120
|
const files = (0, _fileStore.useFiles)();
|
|
114
121
|
// Ensure containerWidth is a number (TypeScript guard)
|
|
@@ -197,7 +204,7 @@ const FileManagementScreen = ({
|
|
|
197
204
|
if (disabledMimeTypes.length) {
|
|
198
205
|
const blocked = disabledMimeTypes.some(mt => file.contentType === mt || file.contentType.startsWith(mt.endsWith('/') ? mt : `${mt}/`));
|
|
199
206
|
if (blocked) {
|
|
200
|
-
_sonner.toast.error('
|
|
207
|
+
_sonner.toast.error(t('fileManagement.toasts.fileTypeBlocked'));
|
|
201
208
|
return;
|
|
202
209
|
}
|
|
203
210
|
}
|
|
@@ -237,7 +244,9 @@ const FileManagementScreen = ({
|
|
|
237
244
|
const already = next.has(file.id);
|
|
238
245
|
if (!already) {
|
|
239
246
|
if (maxSelection && next.size >= maxSelection) {
|
|
240
|
-
_sonner.toast.error(
|
|
247
|
+
_sonner.toast.error(t('fileManagement.toasts.maxSelection', {
|
|
248
|
+
max: maxSelection
|
|
249
|
+
}));
|
|
241
250
|
return prev;
|
|
242
251
|
}
|
|
243
252
|
next.add(file.id);
|
|
@@ -371,7 +380,7 @@ const FileManagementScreen = ({
|
|
|
371
380
|
}));
|
|
372
381
|
}
|
|
373
382
|
} catch (error) {
|
|
374
|
-
_sonner.toast.error(error
|
|
383
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.loadFailed'));
|
|
375
384
|
} finally {
|
|
376
385
|
setLoading(false);
|
|
377
386
|
setRefreshing(false);
|
|
@@ -473,7 +482,9 @@ const FileManagementScreen = ({
|
|
|
473
482
|
const oversizedFiles = selectedFiles.filter(file => file.size > maxSize);
|
|
474
483
|
if (oversizedFiles.length > 0) {
|
|
475
484
|
const fileList = oversizedFiles.map(f => f.name).join(', ');
|
|
476
|
-
_sonner.toast.error(
|
|
485
|
+
_sonner.toast.error(t('fileManagement.toasts.filesTooLarge', {
|
|
486
|
+
files: fileList
|
|
487
|
+
}));
|
|
477
488
|
return [];
|
|
478
489
|
}
|
|
479
490
|
let successCount = 0;
|
|
@@ -562,7 +573,7 @@ const FileManagementScreen = ({
|
|
|
562
573
|
}
|
|
563
574
|
} catch (error) {
|
|
564
575
|
failureCount++;
|
|
565
|
-
const errorMessage = error
|
|
576
|
+
const errorMessage = getErrorMessage(error) || String(error) || 'Upload failed';
|
|
566
577
|
const fullError = `${fileName}: ${errorMessage}`;
|
|
567
578
|
errors.push(fullError);
|
|
568
579
|
if (__DEV__) {
|
|
@@ -571,7 +582,7 @@ const FileManagementScreen = ({
|
|
|
571
582
|
fileSize: raw.size,
|
|
572
583
|
fileType: raw.type,
|
|
573
584
|
error: errorMessage,
|
|
574
|
-
stack: error.stack
|
|
585
|
+
stack: error instanceof Error ? error.stack : undefined
|
|
575
586
|
});
|
|
576
587
|
}
|
|
577
588
|
|
|
@@ -582,24 +593,30 @@ const FileManagementScreen = ({
|
|
|
582
593
|
|
|
583
594
|
// Show success/error messages
|
|
584
595
|
if (successCount > 0) {
|
|
585
|
-
_sonner.toast.success(
|
|
596
|
+
_sonner.toast.success(t('fileManagement.toasts.uploadSuccess', {
|
|
597
|
+
count: successCount
|
|
598
|
+
}));
|
|
586
599
|
}
|
|
587
600
|
if (failureCount > 0) {
|
|
588
601
|
// Show detailed error message with first few errors
|
|
589
602
|
const errorDetails = errors.length > 0 ? `\n${errors.slice(0, 3).join('\n')}${errors.length > 3 ? `\n...and ${errors.length - 3} more` : ''}` : '';
|
|
590
|
-
_sonner.toast.error(`${
|
|
603
|
+
_sonner.toast.error(`${t('fileManagement.toasts.uploadFailed', {
|
|
604
|
+
count: failureCount
|
|
605
|
+
})}${errorDetails}`);
|
|
591
606
|
}
|
|
592
607
|
// Silent background refresh to ensure metadata/variants updated
|
|
593
608
|
setTimeout(() => {
|
|
594
609
|
loadFiles('silent');
|
|
595
610
|
}, 1200);
|
|
596
611
|
} catch (error) {
|
|
597
|
-
_sonner.toast.error(error
|
|
612
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.uploadError'));
|
|
598
613
|
} finally {
|
|
599
614
|
storeSetUploadProgress(null);
|
|
600
615
|
}
|
|
601
616
|
return uploadedFiles;
|
|
602
617
|
};
|
|
618
|
+
|
|
619
|
+
// biome-ignore lint/suspicious/noExplicitAny: Files from document picker may have extra properties like uri
|
|
603
620
|
const handleFileSelection = (0, _react.useCallback)(async selectedFiles => {
|
|
604
621
|
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
|
|
605
622
|
const processedFiles = [];
|
|
@@ -609,34 +626,41 @@ const FileManagementScreen = ({
|
|
|
609
626
|
if (__DEV__) {
|
|
610
627
|
console.error('Invalid file: file is null or undefined');
|
|
611
628
|
}
|
|
612
|
-
_sonner.toast.error('
|
|
629
|
+
_sonner.toast.error(t('fileManagement.toasts.invalidFileMissing'));
|
|
613
630
|
continue;
|
|
614
631
|
}
|
|
615
632
|
if (!file.name || typeof file.name !== 'string') {
|
|
616
633
|
if (__DEV__) {
|
|
617
634
|
console.error('Invalid file: missing or invalid name property', file);
|
|
618
635
|
}
|
|
619
|
-
_sonner.toast.error('
|
|
636
|
+
_sonner.toast.error(t('fileManagement.toasts.invalidFileName'));
|
|
620
637
|
continue;
|
|
621
638
|
}
|
|
622
639
|
if (file.size === undefined || file.size === null || Number.isNaN(file.size)) {
|
|
623
640
|
if (__DEV__) {
|
|
624
641
|
console.error('Invalid file: missing or invalid size property', file);
|
|
625
642
|
}
|
|
626
|
-
_sonner.toast.error(
|
|
643
|
+
_sonner.toast.error(t('fileManagement.toasts.invalidFileSize', {
|
|
644
|
+
name: file.name || 'unknown'
|
|
645
|
+
}));
|
|
627
646
|
continue;
|
|
628
647
|
}
|
|
629
648
|
if (file.size <= 0) {
|
|
630
649
|
if (__DEV__) {
|
|
631
650
|
console.error('Invalid file: file size is zero or negative', file);
|
|
632
651
|
}
|
|
633
|
-
_sonner.toast.error(
|
|
652
|
+
_sonner.toast.error(t('fileManagement.toasts.fileEmpty', {
|
|
653
|
+
name: file.name
|
|
654
|
+
}));
|
|
634
655
|
continue;
|
|
635
656
|
}
|
|
636
657
|
|
|
637
658
|
// Validate file size
|
|
638
659
|
if (file.size > MAX_FILE_SIZE) {
|
|
639
|
-
_sonner.toast.error(
|
|
660
|
+
_sonner.toast.error(t('fileManagement.toasts.fileTooLarge', {
|
|
661
|
+
name: file.name,
|
|
662
|
+
maxSize: (0, _fileManagement.formatFileSize)(MAX_FILE_SIZE)
|
|
663
|
+
}));
|
|
640
664
|
continue;
|
|
641
665
|
}
|
|
642
666
|
|
|
@@ -673,7 +697,7 @@ const FileManagementScreen = ({
|
|
|
673
697
|
});
|
|
674
698
|
}
|
|
675
699
|
if (processedFiles.length === 0) {
|
|
676
|
-
_sonner.toast.error('
|
|
700
|
+
_sonner.toast.error(t('fileManagement.toasts.noValidFiles'));
|
|
677
701
|
return;
|
|
678
702
|
}
|
|
679
703
|
|
|
@@ -728,7 +752,7 @@ const FileManagementScreen = ({
|
|
|
728
752
|
}
|
|
729
753
|
endUpload();
|
|
730
754
|
} catch (error) {
|
|
731
|
-
_sonner.toast.error(error
|
|
755
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.uploadError'));
|
|
732
756
|
endUpload();
|
|
733
757
|
}
|
|
734
758
|
};
|
|
@@ -761,7 +785,7 @@ const FileManagementScreen = ({
|
|
|
761
785
|
const handleFileUpload = async () => {
|
|
762
786
|
// Prevent concurrent document picker calls
|
|
763
787
|
if (isPickingDocument) {
|
|
764
|
-
_sonner.toast.error('
|
|
788
|
+
_sonner.toast.error(t('fileManagement.toasts.waitForSelection'));
|
|
765
789
|
return;
|
|
766
790
|
}
|
|
767
791
|
try {
|
|
@@ -783,7 +807,7 @@ const FileManagementScreen = ({
|
|
|
783
807
|
}
|
|
784
808
|
if (!result.assets || result.assets.length === 0) {
|
|
785
809
|
setIsPickingDocument(false);
|
|
786
|
-
_sonner.toast.error('
|
|
810
|
+
_sonner.toast.error(t('fileManagement.toasts.noFilesSelected'));
|
|
787
811
|
return;
|
|
788
812
|
}
|
|
789
813
|
|
|
@@ -810,7 +834,7 @@ const FileManagementScreen = ({
|
|
|
810
834
|
}
|
|
811
835
|
return null;
|
|
812
836
|
}).catch(error => {
|
|
813
|
-
errors.push(`File "${doc.name || 'file'}": ${error
|
|
837
|
+
errors.push(`File "${doc.name || 'file'}": ${getErrorMessage(error) || 'Failed to process'}`);
|
|
814
838
|
return null;
|
|
815
839
|
}));
|
|
816
840
|
const convertedFiles = await Promise.all(conversionPromises);
|
|
@@ -825,7 +849,9 @@ const FileManagementScreen = ({
|
|
|
825
849
|
// Show errors if any
|
|
826
850
|
if (errors.length > 0) {
|
|
827
851
|
const errorMessage = errors.slice(0, 3).join('\n') + (errors.length > 3 ? `\n...and ${errors.length - 3} more` : '');
|
|
828
|
-
_sonner.toast.error(
|
|
852
|
+
_sonner.toast.error(t('fileManagement.toasts.loadSomeFailed', {
|
|
853
|
+
errors: errorMessage
|
|
854
|
+
}));
|
|
829
855
|
}
|
|
830
856
|
|
|
831
857
|
// Process successfully converted files
|
|
@@ -833,20 +859,20 @@ const FileManagementScreen = ({
|
|
|
833
859
|
await handleFileSelection(files);
|
|
834
860
|
} else {
|
|
835
861
|
// Files were selected but none could be converted
|
|
836
|
-
_sonner.toast.error('
|
|
862
|
+
_sonner.toast.error(t('fileManagement.toasts.noFilesProcessed'));
|
|
837
863
|
}
|
|
838
864
|
} catch (error) {
|
|
839
865
|
if (__DEV__) {
|
|
840
866
|
console.error('File upload error:', error);
|
|
841
867
|
}
|
|
842
|
-
if (error
|
|
843
|
-
if (error
|
|
844
|
-
_sonner.toast.error('
|
|
868
|
+
if (getErrorMessage(error)?.includes('expo-document-picker') || getErrorMessage(error)?.includes('Different document picking in progress')) {
|
|
869
|
+
if (getErrorMessage(error)?.includes('Different document picking in progress')) {
|
|
870
|
+
_sonner.toast.error(t('fileManagement.toasts.waitForSelection'));
|
|
845
871
|
} else {
|
|
846
|
-
_sonner.toast.error('
|
|
872
|
+
_sonner.toast.error(t('fileManagement.toasts.filePickerNotAvailable'));
|
|
847
873
|
}
|
|
848
874
|
} else {
|
|
849
|
-
_sonner.toast.error(error
|
|
875
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.selectFilesFailed'));
|
|
850
876
|
}
|
|
851
877
|
} finally {
|
|
852
878
|
// Always reset the picking state, even if there was an error
|
|
@@ -855,14 +881,16 @@ const FileManagementScreen = ({
|
|
|
855
881
|
};
|
|
856
882
|
const handleFileDelete = async (fileId, filename) => {
|
|
857
883
|
// Use platform-aware confirmation dialog
|
|
858
|
-
const confirmed = await (0, _fileManagement.confirmAction)(
|
|
884
|
+
const confirmed = await (0, _fileManagement.confirmAction)(t('fileManagement.confirms.deleteFile', {
|
|
885
|
+
filename
|
|
886
|
+
}), t('fileManagement.deleteFile'), t('fileManagement.confirm'), t('common.cancel'));
|
|
859
887
|
if (!confirmed) {
|
|
860
888
|
return;
|
|
861
889
|
}
|
|
862
890
|
try {
|
|
863
891
|
storeSetDeleting(fileId);
|
|
864
892
|
await oxyServices.deleteFile(fileId);
|
|
865
|
-
_sonner.toast.success('
|
|
893
|
+
_sonner.toast.success(t('fileManagement.toasts.deleteSuccess'));
|
|
866
894
|
|
|
867
895
|
// Reload files after successful deletion
|
|
868
896
|
// Optimistic remove
|
|
@@ -871,14 +899,14 @@ const FileManagementScreen = ({
|
|
|
871
899
|
setTimeout(() => loadFiles('silent'), 800);
|
|
872
900
|
} catch (error) {
|
|
873
901
|
// Provide specific error messages
|
|
874
|
-
if (error
|
|
875
|
-
_sonner.toast.error('
|
|
902
|
+
if (getErrorMessage(error)?.includes('File not found') || getErrorMessage(error)?.includes('404')) {
|
|
903
|
+
_sonner.toast.error(t('fileManagement.toasts.fileNotFound'));
|
|
876
904
|
// Still reload files to refresh the list
|
|
877
905
|
setTimeout(() => loadFiles('silent'), 800);
|
|
878
|
-
} else if (error
|
|
879
|
-
_sonner.toast.error('
|
|
906
|
+
} else if (getErrorMessage(error)?.includes('permission') || getErrorMessage(error)?.includes('403')) {
|
|
907
|
+
_sonner.toast.error(t('fileManagement.toasts.noPermission'));
|
|
880
908
|
} else {
|
|
881
|
-
_sonner.toast.error(error
|
|
909
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.deleteFailed'));
|
|
882
910
|
}
|
|
883
911
|
} finally {
|
|
884
912
|
storeSetDeleting(null);
|
|
@@ -891,7 +919,9 @@ const FileManagementScreen = ({
|
|
|
891
919
|
fileMap[f.id] = f;
|
|
892
920
|
});
|
|
893
921
|
const selectedFiles = Array.from(selectedIds).map(id => fileMap[id]).filter(Boolean);
|
|
894
|
-
const confirmed = await (0, _fileManagement.confirmAction)(
|
|
922
|
+
const confirmed = await (0, _fileManagement.confirmAction)(t('fileManagement.confirms.deleteFiles', {
|
|
923
|
+
count: selectedFiles.length
|
|
924
|
+
}), t('fileManagement.deleteFiles'), t('fileManagement.confirm'), t('common.cancel'));
|
|
895
925
|
if (!confirmed) return;
|
|
896
926
|
try {
|
|
897
927
|
const deletePromises = Array.from(selectedIds).map(async fileId => {
|
|
@@ -914,15 +944,19 @@ const FileManagementScreen = ({
|
|
|
914
944
|
const successful = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
|
|
915
945
|
const failed = results.length - successful;
|
|
916
946
|
if (successful > 0) {
|
|
917
|
-
_sonner.toast.success(
|
|
947
|
+
_sonner.toast.success(t('fileManagement.toasts.bulkDeleteSuccess', {
|
|
948
|
+
count: successful
|
|
949
|
+
}));
|
|
918
950
|
}
|
|
919
951
|
if (failed > 0) {
|
|
920
|
-
_sonner.toast.error(
|
|
952
|
+
_sonner.toast.error(t('fileManagement.toasts.bulkDeleteFailed', {
|
|
953
|
+
count: failed
|
|
954
|
+
}));
|
|
921
955
|
}
|
|
922
956
|
setSelectedIds(new Set());
|
|
923
957
|
setTimeout(() => loadFiles('silent'), 800);
|
|
924
958
|
} catch (error) {
|
|
925
|
-
_sonner.toast.error(error
|
|
959
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.bulkDeleteError'));
|
|
926
960
|
}
|
|
927
961
|
}, [selectedIds, files, oxyServices, loadFiles]);
|
|
928
962
|
const handleBulkVisibilityChange = (0, _react.useCallback)(async visibility => {
|
|
@@ -947,7 +981,10 @@ const FileManagementScreen = ({
|
|
|
947
981
|
const successful = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
|
|
948
982
|
const failed = results.length - successful;
|
|
949
983
|
if (successful > 0) {
|
|
950
|
-
_sonner.toast.success(
|
|
984
|
+
_sonner.toast.success(t('fileManagement.toasts.visibilitySuccess', {
|
|
985
|
+
count: successful,
|
|
986
|
+
visibility
|
|
987
|
+
}));
|
|
951
988
|
// Update file metadata in store
|
|
952
989
|
Array.from(selectedIds).forEach(fileId => {
|
|
953
990
|
_fileStore.useFileStore.getState().updateFile(fileId, {
|
|
@@ -959,11 +996,13 @@ const FileManagementScreen = ({
|
|
|
959
996
|
});
|
|
960
997
|
}
|
|
961
998
|
if (failed > 0) {
|
|
962
|
-
_sonner.toast.error(
|
|
999
|
+
_sonner.toast.error(t('fileManagement.toasts.visibilityFailed', {
|
|
1000
|
+
count: failed
|
|
1001
|
+
}));
|
|
963
1002
|
}
|
|
964
1003
|
setTimeout(() => loadFiles('silent'), 800);
|
|
965
1004
|
} catch (error) {
|
|
966
|
-
_sonner.toast.error(error
|
|
1005
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.visibilityError'));
|
|
967
1006
|
}
|
|
968
1007
|
}, [selectedIds, oxyServices, files, loadFiles]);
|
|
969
1008
|
|
|
@@ -985,7 +1024,7 @@ const FileManagementScreen = ({
|
|
|
985
1024
|
document.body.appendChild(link);
|
|
986
1025
|
link.click();
|
|
987
1026
|
document.body.removeChild(link);
|
|
988
|
-
_sonner.toast.success('
|
|
1027
|
+
_sonner.toast.success(t('fileManagement.toasts.downloadStarted'));
|
|
989
1028
|
} catch (linkError) {
|
|
990
1029
|
// Fallback to authenticated download
|
|
991
1030
|
const blob = await oxyServices.getFileContentAsBlob(fileId);
|
|
@@ -999,16 +1038,16 @@ const FileManagementScreen = ({
|
|
|
999
1038
|
|
|
1000
1039
|
// Clean up the blob URL
|
|
1001
1040
|
URL.revokeObjectURL(url);
|
|
1002
|
-
_sonner.toast.success('
|
|
1041
|
+
_sonner.toast.success(t('fileManagement.toasts.downloadSuccess'));
|
|
1003
1042
|
}
|
|
1004
1043
|
} else {
|
|
1005
1044
|
// For mobile, open the URL (user can save from browser)
|
|
1006
1045
|
// Note: This is a simplified approach - for full mobile support,
|
|
1007
1046
|
// consider using expo-file-system or react-native-fs
|
|
1008
|
-
_sonner.toast.info('
|
|
1047
|
+
_sonner.toast.info(t('fileManagement.toasts.downloadMobile'));
|
|
1009
1048
|
}
|
|
1010
1049
|
} catch (error) {
|
|
1011
|
-
_sonner.toast.error(error
|
|
1050
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.downloadFailed'));
|
|
1012
1051
|
}
|
|
1013
1052
|
};
|
|
1014
1053
|
const handleFileOpen = async file => {
|
|
@@ -1033,10 +1072,10 @@ const FileManagementScreen = ({
|
|
|
1033
1072
|
setFileContent(content);
|
|
1034
1073
|
}
|
|
1035
1074
|
} catch (error) {
|
|
1036
|
-
if (error
|
|
1037
|
-
_sonner.toast.error('
|
|
1075
|
+
if (getErrorMessage(error)?.includes('404') || getErrorMessage(error)?.includes('not found')) {
|
|
1076
|
+
_sonner.toast.error(t('fileManagement.toasts.fileNotFoundContent'));
|
|
1038
1077
|
} else {
|
|
1039
|
-
_sonner.toast.error('
|
|
1078
|
+
_sonner.toast.error(t('fileManagement.toasts.loadContentFailed'));
|
|
1040
1079
|
}
|
|
1041
1080
|
setFileContent(null);
|
|
1042
1081
|
}
|
|
@@ -1045,7 +1084,7 @@ const FileManagementScreen = ({
|
|
|
1045
1084
|
setFileContent(null);
|
|
1046
1085
|
}
|
|
1047
1086
|
} catch (error) {
|
|
1048
|
-
_sonner.toast.error(error
|
|
1087
|
+
_sonner.toast.error(getErrorMessage(error) || t('fileManagement.toasts.openFailed'));
|
|
1049
1088
|
} finally {
|
|
1050
1089
|
setLoadingFileContent(false);
|
|
1051
1090
|
}
|
|
@@ -1320,6 +1359,7 @@ const FileManagementScreen = ({
|
|
|
1320
1359
|
};
|
|
1321
1360
|
|
|
1322
1361
|
// GroupedSection-based file items (for 'all' view) replacing legacy flat list look
|
|
1362
|
+
// biome-ignore lint/suspicious/noExplicitAny: GroupedSection items have dynamic props
|
|
1323
1363
|
const groupedFileItems = (0, _react.useMemo)(() => {
|
|
1324
1364
|
// filteredFiles is already sorted, so just use it directly
|
|
1325
1365
|
const sortedFiles = filteredFiles;
|
|
@@ -1499,6 +1539,7 @@ const FileManagementScreen = ({
|
|
|
1499
1539
|
numberOfLines: 2,
|
|
1500
1540
|
children: file.metadata.description
|
|
1501
1541
|
}) : undefined
|
|
1542
|
+
// biome-ignore lint/suspicious/noExplicitAny: GroupedSectionItem has dynamic properties
|
|
1502
1543
|
};
|
|
1503
1544
|
});
|
|
1504
1545
|
}, [filteredFiles, theme, themeStyles, deleting, handleFileDownload, handleFileDelete, handleFileOpen, getSafeDownloadUrlCallback, selectMode, selectedIds]);
|
|
@@ -1635,12 +1676,12 @@ const FileManagementScreen = ({
|
|
|
1635
1676
|
style: [_styles.fileManagementStyles.emptyStateTitle, {
|
|
1636
1677
|
color: themeStyles.textColor
|
|
1637
1678
|
}],
|
|
1638
|
-
children:
|
|
1679
|
+
children: t('fileManagement.emptyPhotos.title')
|
|
1639
1680
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
1640
1681
|
style: [_styles.fileManagementStyles.emptyStateDescription, {
|
|
1641
1682
|
color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'
|
|
1642
1683
|
}],
|
|
1643
|
-
children: [" ", user?.id === targetUserId ?
|
|
1684
|
+
children: [" ", user?.id === targetUserId ? t('fileManagement.emptyPhotos.ownDescription') : t('fileManagement.emptyPhotos.otherDescription'), " "]
|
|
1644
1685
|
}), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
1645
1686
|
style: [_styles.fileManagementStyles.emptyStateButton, {
|
|
1646
1687
|
backgroundColor: themeStyles.primaryColor
|
|
@@ -1660,7 +1701,7 @@ const FileManagementScreen = ({
|
|
|
1660
1701
|
color: "#FFFFFF"
|
|
1661
1702
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1662
1703
|
style: _styles.fileManagementStyles.emptyStateButtonText,
|
|
1663
|
-
children:
|
|
1704
|
+
children: t('fileManagement.uploadPhotos')
|
|
1664
1705
|
})]
|
|
1665
1706
|
})
|
|
1666
1707
|
})]
|
|
@@ -1699,7 +1740,7 @@ const FileManagementScreen = ({
|
|
|
1699
1740
|
style: [_styles.fileManagementStyles.dimensionsLoadingText, {
|
|
1700
1741
|
color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'
|
|
1701
1742
|
}],
|
|
1702
|
-
children:
|
|
1743
|
+
children: t('fileManagement.loadingPhotoLayout')
|
|
1703
1744
|
})]
|
|
1704
1745
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_JustifiedPhotoGrid.default, {
|
|
1705
1746
|
photos: photos,
|
|
@@ -1726,12 +1767,12 @@ const FileManagementScreen = ({
|
|
|
1726
1767
|
style: [_styles.fileManagementStyles.emptyStateTitle, {
|
|
1727
1768
|
color: themeStyles.textColor
|
|
1728
1769
|
}],
|
|
1729
|
-
children:
|
|
1770
|
+
children: t('fileManagement.emptyFiles.title')
|
|
1730
1771
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1731
1772
|
style: [_styles.fileManagementStyles.emptyStateDescription, {
|
|
1732
1773
|
color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'
|
|
1733
1774
|
}],
|
|
1734
|
-
children: user?.id === targetUserId ?
|
|
1775
|
+
children: user?.id === targetUserId ? t('fileManagement.emptyFiles.ownDescription') : t('fileManagement.emptyFiles.otherDescription')
|
|
1735
1776
|
}), user?.id === targetUserId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
1736
1777
|
style: [_styles.fileManagementStyles.emptyStateButton, {
|
|
1737
1778
|
backgroundColor: themeStyles.primaryColor
|
|
@@ -1751,7 +1792,7 @@ const FileManagementScreen = ({
|
|
|
1751
1792
|
color: "#FFFFFF"
|
|
1752
1793
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1753
1794
|
style: _styles.fileManagementStyles.emptyStateButtonText,
|
|
1754
|
-
children:
|
|
1795
|
+
children: t('fileManagement.uploadFiles')
|
|
1755
1796
|
})]
|
|
1756
1797
|
})
|
|
1757
1798
|
})]
|
|
@@ -1997,8 +2038,10 @@ const FileManagementScreen = ({
|
|
|
1997
2038
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
1998
2039
|
style: _styles.fileManagementStyles.container,
|
|
1999
2040
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Header.default, {
|
|
2000
|
-
title:
|
|
2001
|
-
subtitle:
|
|
2041
|
+
title: t('fileManagement.reviewFiles'),
|
|
2042
|
+
subtitle: t('fileManagement.readyToUpload', {
|
|
2043
|
+
count: pendingFiles.length
|
|
2044
|
+
}),
|
|
2002
2045
|
onBack: handleCancelUpload,
|
|
2003
2046
|
showBackButton: true,
|
|
2004
2047
|
variant: "minimal",
|
|
@@ -2018,43 +2061,58 @@ const FileManagementScreen = ({
|
|
|
2018
2061
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
2019
2062
|
style: _styles.fileManagementStyles.container,
|
|
2020
2063
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Header.default, {
|
|
2021
|
-
title: selectMode ? multiSelect ?
|
|
2022
|
-
|
|
2064
|
+
title: selectMode ? multiSelect ? maxSelection ? t('fileManagement.selectedWithMax', {
|
|
2065
|
+
count: selectedIds.size,
|
|
2066
|
+
max: maxSelection
|
|
2067
|
+
}) : t('fileManagement.selected', {
|
|
2068
|
+
count: selectedIds.size
|
|
2069
|
+
}) : t('fileManagement.selectFile') : viewMode === 'photos' ? t('fileManagement.photos') : t('fileManagement.title'),
|
|
2070
|
+
subtitle: selectMode ? multiSelect ? t('fileManagement.available', {
|
|
2071
|
+
count: filteredFiles.length
|
|
2072
|
+
}) : t('fileManagement.tapToSelect') : filteredFiles.length === 1 ? t('fileManagement.itemCount', {
|
|
2073
|
+
count: filteredFiles.length
|
|
2074
|
+
}) : t('fileManagement.itemCount_plural', {
|
|
2075
|
+
count: filteredFiles.length
|
|
2076
|
+
}),
|
|
2023
2077
|
rightActions: selectMode && multiSelect ? [{
|
|
2024
2078
|
key: 'clear',
|
|
2025
|
-
text: '
|
|
2079
|
+
text: t('fileManagement.clear'),
|
|
2026
2080
|
onPress: () => setSelectedIds(new Set()),
|
|
2027
2081
|
disabled: selectedIds.size === 0
|
|
2028
2082
|
}, {
|
|
2029
2083
|
key: 'confirm',
|
|
2030
|
-
text: '
|
|
2084
|
+
text: t('fileManagement.confirm'),
|
|
2031
2085
|
onPress: confirmMultiSelection,
|
|
2032
2086
|
disabled: selectedIds.size === 0
|
|
2033
2087
|
}] : !selectMode && selectedIds.size > 0 ? [{
|
|
2034
2088
|
key: 'clear',
|
|
2035
|
-
text: '
|
|
2089
|
+
text: t('fileManagement.clear'),
|
|
2036
2090
|
onPress: () => setSelectedIds(new Set())
|
|
2037
2091
|
}, {
|
|
2038
2092
|
key: 'delete',
|
|
2039
|
-
text:
|
|
2093
|
+
text: t('fileManagement.delete', {
|
|
2094
|
+
count: selectedIds.size
|
|
2095
|
+
}),
|
|
2040
2096
|
onPress: handleBulkDelete,
|
|
2041
2097
|
icon: 'delete'
|
|
2042
2098
|
}, {
|
|
2043
2099
|
key: 'visibility',
|
|
2044
|
-
text: '
|
|
2100
|
+
text: t('fileManagement.visibility'),
|
|
2045
2101
|
onPress: () => {
|
|
2046
2102
|
// Show visibility options menu
|
|
2047
|
-
_reactNative.Alert.alert('
|
|
2048
|
-
|
|
2103
|
+
_reactNative.Alert.alert(t('fileManagement.changeVisibility'), t('fileManagement.changeVisibilityConfirm', {
|
|
2104
|
+
count: selectedIds.size
|
|
2105
|
+
}), [{
|
|
2106
|
+
text: t('common.cancel'),
|
|
2049
2107
|
style: 'cancel'
|
|
2050
2108
|
}, {
|
|
2051
|
-
text: '
|
|
2109
|
+
text: t('fileManagement.private'),
|
|
2052
2110
|
onPress: () => handleBulkVisibilityChange('private')
|
|
2053
2111
|
}, {
|
|
2054
|
-
text: '
|
|
2112
|
+
text: t('fileManagement.public'),
|
|
2055
2113
|
onPress: () => handleBulkVisibilityChange('public')
|
|
2056
2114
|
}, {
|
|
2057
|
-
text: '
|
|
2115
|
+
text: t('fileManagement.unlisted'),
|
|
2058
2116
|
onPress: () => handleBulkVisibilityChange('unlisted')
|
|
2059
2117
|
}]);
|
|
2060
2118
|
},
|
|
@@ -2172,7 +2230,7 @@ const FileManagementScreen = ({
|
|
|
2172
2230
|
style: [_styles.fileManagementStyles.searchInput, {
|
|
2173
2231
|
color: themeStyles.textColor
|
|
2174
2232
|
}],
|
|
2175
|
-
placeholder: viewMode === 'photos' ? '
|
|
2233
|
+
placeholder: viewMode === 'photos' ? t('fileManagement.searchPhotos') : t('fileManagement.searchFiles'),
|
|
2176
2234
|
placeholderTextColor: themeStyles.colors.secondaryText,
|
|
2177
2235
|
value: searchQuery,
|
|
2178
2236
|
onChangeText: setSearchQuery
|
|
@@ -2200,7 +2258,7 @@ const FileManagementScreen = ({
|
|
|
2200
2258
|
style: [_styles.fileManagementStyles.statLabel, {
|
|
2201
2259
|
color: themeStyles.colors.secondaryText
|
|
2202
2260
|
}],
|
|
2203
|
-
children: searchQuery.length > 0 ? '
|
|
2261
|
+
children: searchQuery.length > 0 ? t('fileManagement.found') : filteredFiles.length === 1 ? viewMode === 'photos' ? t('fileManagement.photo') : t('fileManagement.file') : viewMode === 'photos' ? t('fileManagement.photos_stat') : t('fileManagement.files')
|
|
2204
2262
|
})]
|
|
2205
2263
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
2206
2264
|
style: _styles.fileManagementStyles.statItem,
|
|
@@ -2213,7 +2271,7 @@ const FileManagementScreen = ({
|
|
|
2213
2271
|
style: [_styles.fileManagementStyles.statLabel, {
|
|
2214
2272
|
color: themeStyles.colors.secondaryText
|
|
2215
2273
|
}],
|
|
2216
|
-
children: searchQuery.length > 0 ? '
|
|
2274
|
+
children: searchQuery.length > 0 ? t('fileManagement.size') : t('fileManagement.totalSize')
|
|
2217
2275
|
})]
|
|
2218
2276
|
}), searchQuery.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
2219
2277
|
style: _styles.fileManagementStyles.statItem,
|
|
@@ -2226,7 +2284,7 @@ const FileManagementScreen = ({
|
|
|
2226
2284
|
style: [_styles.fileManagementStyles.statLabel, {
|
|
2227
2285
|
color: themeStyles.colors.secondaryText
|
|
2228
2286
|
}],
|
|
2229
|
-
children:
|
|
2287
|
+
children: t('fileManagement.total')
|
|
2230
2288
|
})]
|
|
2231
2289
|
})]
|
|
2232
2290
|
}), viewMode === 'photos' ? renderPhotoGrid() : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
|
|
@@ -2262,12 +2320,14 @@ const FileManagementScreen = ({
|
|
|
2262
2320
|
style: [_styles.fileManagementStyles.emptyStateTitle, {
|
|
2263
2321
|
color: themeStyles.textColor
|
|
2264
2322
|
}],
|
|
2265
|
-
children:
|
|
2266
|
-
}), /*#__PURE__*/(0, _jsxRuntime.
|
|
2323
|
+
children: t('fileManagement.noResults.title')
|
|
2324
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
2267
2325
|
style: [_styles.fileManagementStyles.emptyStateDescription, {
|
|
2268
2326
|
color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666'
|
|
2269
2327
|
}],
|
|
2270
|
-
children:
|
|
2328
|
+
children: t('fileManagement.noResults.description', {
|
|
2329
|
+
query: searchQuery
|
|
2330
|
+
})
|
|
2271
2331
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
2272
2332
|
style: [_styles.fileManagementStyles.emptyStateButton, {
|
|
2273
2333
|
backgroundColor: themeStyles.primaryColor
|
|
@@ -2279,7 +2339,7 @@ const FileManagementScreen = ({
|
|
|
2279
2339
|
color: "#FFFFFF"
|
|
2280
2340
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
2281
2341
|
style: _styles.fileManagementStyles.emptyStateButtonText,
|
|
2282
|
-
children:
|
|
2342
|
+
children: t('fileManagement.clearSearch')
|
|
2283
2343
|
})]
|
|
2284
2344
|
})]
|
|
2285
2345
|
}) : filteredFiles.length === 0 ? renderEmptyState() : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
@@ -2294,7 +2354,7 @@ const FileManagementScreen = ({
|
|
|
2294
2354
|
style: [_styles.fileManagementStyles.loadingMoreText, {
|
|
2295
2355
|
color: themeStyles.textColor
|
|
2296
2356
|
}],
|
|
2297
|
-
children:
|
|
2357
|
+
children: t('fileManagement.loadingMore')
|
|
2298
2358
|
})]
|
|
2299
2359
|
})]
|
|
2300
2360
|
})
|
|
@@ -2325,7 +2385,7 @@ const FileManagementScreen = ({
|
|
|
2325
2385
|
style: [_styles.fileManagementStyles.uploadBannerText, {
|
|
2326
2386
|
color: themeStyles.textColor
|
|
2327
2387
|
}],
|
|
2328
|
-
children: [
|
|
2388
|
+
children: [t('fileManagement.uploading'), uploadProgress ? ` ${uploadProgress.current}/${uploadProgress.total}` : '...']
|
|
2329
2389
|
}), uploadProgress && uploadProgress.total > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2330
2390
|
style: [_styles.fileManagementStyles.uploadProgressBarContainer, {
|
|
2331
2391
|
backgroundColor: themeStyles.isDarkTheme ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)'
|