@oxyhq/services 22.6.2 → 22.6.4

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 (47) hide show
  1. package/lib/commonjs/ui/components/fileManagement/FileViewer.js +99 -77
  2. package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
  3. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +33 -40
  4. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
  5. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js +92 -88
  6. package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  7. package/lib/commonjs/ui/screens/FileManagementScreen.js +175 -360
  8. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  9. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +9 -2
  10. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  11. package/lib/module/ui/components/fileManagement/FileViewer.js +100 -78
  12. package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
  13. package/lib/module/ui/components/fileManagement/UploadPreview.js +34 -41
  14. package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
  15. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js +94 -90
  16. package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
  17. package/lib/module/ui/screens/FileManagementScreen.js +177 -363
  18. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  19. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +9 -2
  20. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  21. package/lib/typescript/commonjs/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts +0 -1
  24. package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  27. package/lib/typescript/module/ui/components/fileManagement/FileViewer.d.ts.map +1 -1
  28. package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  29. package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts +0 -1
  30. package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
  31. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
  32. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/ui/components/fileManagement/FileViewer.tsx +82 -68
  35. package/src/ui/components/fileManagement/UploadPreview.tsx +34 -29
  36. package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +14 -13
  37. package/src/ui/screens/FileManagementScreen.tsx +135 -354
  38. package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +6 -2
  39. package/lib/commonjs/ui/components/fileManagement/styles.js +0 -832
  40. package/lib/commonjs/ui/components/fileManagement/styles.js.map +0 -1
  41. package/lib/module/ui/components/fileManagement/styles.js +0 -828
  42. package/lib/module/ui/components/fileManagement/styles.js.map +0 -1
  43. package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts +0 -825
  44. package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts.map +0 -1
  45. package/lib/typescript/module/ui/components/fileManagement/styles.d.ts +0 -825
  46. package/lib/typescript/module/ui/components/fileManagement/styles.d.ts.map +0 -1
  47. package/src/ui/components/fileManagement/styles.ts +0 -823
@@ -12,6 +12,8 @@ import {
12
12
  Animated,
13
13
  Easing,
14
14
  AccessibilityInfo,
15
+ Platform,
16
+ useWindowDimensions,
15
17
  } from 'react-native';
16
18
  import { Image as ExpoImage } from 'expo-image';
17
19
  import type { FileManagementScreenProps } from '../types/fileManagement';
@@ -33,13 +35,29 @@ import { useResolvedFileUrls, fileThumbSource } from '../hooks/useResolvedFileUr
33
35
  import { FileViewer } from '../components/fileManagement/FileViewer';
34
36
  import { FileDetailsModal } from '../components/fileManagement/FileDetailsModal';
35
37
  import { UploadPreview } from '../components/fileManagement/UploadPreview';
36
- import { fileManagementStyles } from '../components/fileManagement/styles';
37
38
  import { getErrorMessage } from './fileManagement/shared';
38
39
  import { useFileUploadState } from './fileManagement/hooks/useFileUploadState';
39
40
  import PhotoPickerView from './fileManagement/PhotoPickerSection';
40
41
  import FileListSection, { type FileListItem } from './fileManagement/FileListSection';
41
42
  import UploadBar from './fileManagement/UploadBar';
42
43
 
44
+ // Genuinely-inline-only styles: `viewModeButton` is spread into an Animated.View
45
+ // style array (interpolated backgroundColor), and the photo tiles are
46
+ // `expo-image` (no className remap). Everything else uses NativeWind classNames.
47
+ const screenStyles = StyleSheet.create({
48
+ viewModeButton: {
49
+ paddingHorizontal: 10,
50
+ paddingVertical: 6,
51
+ borderRadius: 999,
52
+ minWidth: 36,
53
+ alignItems: 'center',
54
+ justifyContent: 'center',
55
+ marginHorizontal: 1,
56
+ },
57
+ photoImage: { width: '100%', height: '100%', borderRadius: 8 },
58
+ justifiedPhotoImage: { width: '100%', height: '100%', borderRadius: 6 },
59
+ });
60
+
43
61
  // Animated button component for smooth transitions
44
62
  const AnimatedButton: React.FC<{
45
63
  isSelected: boolean;
@@ -100,7 +118,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
100
118
  goBack,
101
119
  navigate,
102
120
  userId,
103
- containerWidth = 400, // Fallback for when not provided by the router
121
+ containerWidth,
104
122
  selectMode = false,
105
123
  multiSelect = false,
106
124
  onSelect,
@@ -126,8 +144,13 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
126
144
  const visibilityChangeDialog = useDialogControl();
127
145
  const [pendingDeleteFile, setPendingDeleteFile] = useState<{ id: string; name: string } | null>(null);
128
146
  const files = useFiles();
129
- // Ensure containerWidth is a number (TypeScript guard)
130
- const safeContainerWidth: number = typeof containerWidth === 'number' ? containerWidth : 400;
147
+ const { width: windowWidth } = useWindowDimensions();
148
+ // Prefer an explicit sheet width from the router; fall back to the window
149
+ // so photo grids never size against a hardcoded 400px default.
150
+ const safeContainerWidth: number =
151
+ typeof containerWidth === 'number' && containerWidth > 0
152
+ ? containerWidth
153
+ : windowWidth;
131
154
  const uploading = useUploadingStore();
132
155
  const uploadProgress = useUploadAggregateProgress();
133
156
  const deleting = useDeletingStore();
@@ -782,79 +805,26 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
782
805
  fileDetailsControl.open();
783
806
  };
784
807
 
785
- const renderSimplePhotoItem = useCallback((photo: FileMetadata, index: number) => {
786
- const downloadUrl = thumbSourceFor(photo);
787
-
788
- // Calculate photo item width based on actual container size from bottom sheet
789
- let itemsPerRow = 3; // Default for mobile
790
- if (safeContainerWidth > 768) itemsPerRow = 4; // Desktop/tablet
791
- else if (safeContainerWidth > 480) itemsPerRow = 3; // Large mobile
792
-
793
- // Account for the photoScrollContainer padding (16px on each side = 32px total)
794
- const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
795
- const gaps = (itemsPerRow - 1) * 4; // Gap between items (4px)
796
- const availableWidth = safeContainerWidth - scrollContainerPadding;
797
- const itemWidth = (availableWidth - gaps) / itemsPerRow;
798
-
799
- return (
800
- <TouchableOpacity
801
- key={photo.id}
802
- style={[
803
- fileManagementStyles.simplePhotoItem,
804
- {
805
- width: itemWidth,
806
- height: itemWidth,
807
- marginRight: (index + 1) % itemsPerRow === 0 ? 0 : 4,
808
- ...(selectMode && selectedIds.has(photo.id) ? { borderWidth: 2, borderColor: colors.primary } : {})
809
- }
810
- ]}
811
- onPress={() => handleFileOpen(photo)}
812
- activeOpacity={0.8}
813
- >
814
- <View style={fileManagementStyles.simplePhotoContainer}>
815
- <ExpoImage
816
- source={{ uri: downloadUrl }}
817
- style={fileManagementStyles.simplePhotoImage}
818
- contentFit="cover"
819
- transition={120}
820
- cachePolicy="memory-disk"
821
- onError={() => {
822
- // Photo failed to load, will show placeholder
823
- }}
824
- accessibilityLabel={photo.filename}
825
- />
826
- {selectMode && (
827
- <View style={fileManagementStyles.selectionBadge}>
828
- <Ionicons name={selectedIds.has(photo.id) ? 'checkmark-circle' : 'ellipse-outline'} size={20} color={selectedIds.has(photo.id) ? colors.primary : colors.text} />
829
- </View>
830
- )}
831
- </View>
832
- </TouchableOpacity>
833
- );
834
- }, [thumbSourceFor, safeContainerWidth, selectMode, selectedIds, colors.primary, colors.text]);
835
-
836
808
  const renderJustifiedPhotoItem = useCallback((photo: FileMetadata, width: number, height: number, isLast: boolean) => {
837
809
  const downloadUrl = thumbSourceFor(photo);
838
810
 
839
811
  return (
840
812
  <TouchableOpacity
841
813
  key={photo.id}
842
- style={[
843
- fileManagementStyles.justifiedPhotoItem,
844
- {
845
- width,
846
- height,
847
- ...(selectMode && selectedIds.has(photo.id) ? { borderWidth: 2, borderColor: colors.primary } : {}),
848
- ...(selectMode && multiSelect && selectedIds.size > 0 && !selectedIds.has(photo.id) ? { opacity: 0.4 } : {}),
849
- },
850
- ]}
814
+ className="rounded-[6px] overflow-hidden relative"
815
+ style={{
816
+ width,
817
+ height,
818
+ ...(selectMode && selectedIds.has(photo.id) ? { borderWidth: 2, borderColor: colors.primary } : {}),
819
+ ...(selectMode && multiSelect && selectedIds.size > 0 && !selectedIds.has(photo.id) ? { opacity: 0.4 } : {}),
820
+ }}
851
821
  onPress={() => handleFileOpen(photo)}
852
822
  activeOpacity={0.8}
853
823
  >
854
- <View style={fileManagementStyles.justifiedPhotoContainer}>
824
+ <View className="w-full h-full relative rounded-[6px] overflow-hidden">
855
825
  <ExpoImage
856
826
  source={{ uri: downloadUrl }}
857
- style={fileManagementStyles.justifiedPhotoImage}
827
+ style={screenStyles.justifiedPhotoImage}
858
828
  contentFit="cover"
859
829
  transition={120}
860
830
  cachePolicy="memory-disk"
@@ -864,7 +834,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
864
834
  accessibilityLabel={photo.filename}
865
835
  />
866
836
  {selectMode && (
867
- <View style={fileManagementStyles.selectionBadge}>
837
+ <View className="absolute top-[4px] right-[4px] rounded-[12px] p-[2px]" style={{ backgroundColor: 'rgba(0,0,0,0.4)' }}>
868
838
  <Ionicons name={selectedIds.has(photo.id) ? 'checkmark-circle' : 'ellipse-outline'} size={20} color={selectedIds.has(photo.id) ? colors.primary : colors.text} />
869
839
  </View>
870
840
  )}
@@ -884,147 +854,6 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
884
854
  // eslint-disable-next-line react-hooks/exhaustive-deps
885
855
  }, [targetUserId]);
886
856
 
887
- const renderFileItem = (file: FileMetadata) => {
888
- const isImage = file.contentType.startsWith('image/');
889
- const isPDF = file.contentType.includes('pdf');
890
- const isVideo = file.contentType.startsWith('video/');
891
- const isAudio = file.contentType.startsWith('audio/');
892
- const hasPreview = isImage || isPDF || isVideo;
893
- const borderColor = colors.border;
894
-
895
- return (
896
- <View
897
- key={file.id}
898
- style={[fileManagementStyles.fileItem, { backgroundColor: colors.backgroundSecondary, borderColor }, selectMode && selectedIds.has(file.id) && { borderColor: colors.primary, borderWidth: 2 }]}
899
- >
900
- <TouchableOpacity
901
- style={fileManagementStyles.fileContent}
902
- onPress={() => handleFileOpen(file)}
903
- >
904
- {/* Preview Thumbnail */}
905
- <View style={fileManagementStyles.filePreviewContainer}>
906
- {hasPreview ? (
907
- <View style={fileManagementStyles.filePreview}>
908
- {isImage && (
909
- <ExpoImage
910
- source={{ uri: thumbSourceFor(file) }}
911
- style={fileManagementStyles.previewImage}
912
- contentFit="cover"
913
- transition={120}
914
- cachePolicy="memory-disk"
915
- onError={() => {
916
- // Image preview failed to load
917
- }}
918
- accessibilityLabel={file.filename}
919
- />
920
- )}
921
- {isPDF && (
922
- <View style={fileManagementStyles.pdfPreview}>
923
- <Ionicons name="document" size={32} color={colors.primary} />
924
- <Text style={[fileManagementStyles.pdfLabel, { color: colors.primary }]}>PDF</Text>
925
- </View>
926
- )}
927
- {isVideo && (
928
- <View style={fileManagementStyles.videoPreviewWrapper}>
929
- <ExpoImage
930
- source={{ uri: thumbSourceFor(file) }}
931
- style={fileManagementStyles.videoPosterImage}
932
- contentFit="cover"
933
- transition={120}
934
- cachePolicy="memory-disk"
935
- onError={(_: unknown) => {
936
- // If thumbnail not available, we still show icon overlay
937
- }}
938
- accessibilityLabel={`${file.filename} video thumbnail`}
939
- />
940
- <View style={fileManagementStyles.videoOverlay}>
941
- <Ionicons name="play" size={24} color="#FFFFFF" />
942
- </View>
943
- </View>
944
- )}
945
- {/* Fallback icon (hidden by default for images) */}
946
- <View
947
- style={[fileManagementStyles.fallbackIcon, { display: isImage ? 'none' : 'flex' }]}
948
- >
949
- <Ionicons
950
- name={getFileIcon(file.contentType)}
951
- size={32}
952
- color={colors.primary}
953
- />
954
- </View>
955
-
956
- {selectMode && (
957
- <View style={fileManagementStyles.selectionBadge}>
958
- <Ionicons name={selectedIds.has(file.id) ? 'checkmark-circle' : 'ellipse-outline'} size={22} color={selectedIds.has(file.id) ? colors.primary : colors.text} />
959
- </View>
960
- )}
961
- </View>
962
- ) : (
963
- <View style={fileManagementStyles.fileIconContainer}>
964
- <Ionicons
965
- name={getFileIcon(file.contentType)}
966
- size={32}
967
- color={colors.primary}
968
- />
969
- </View>
970
- )}
971
- </View>
972
-
973
- <View style={fileManagementStyles.fileInfo}>
974
- <Text style={[fileManagementStyles.fileName, { color: colors.text }]} numberOfLines={1}>
975
- {file.filename}
976
- </Text>
977
- <Text style={[fileManagementStyles.fileDetails, { color: colors.textSecondary }]}>
978
- {formatFileSize(file.length)} • {new Date(file.uploadDate).toLocaleDateString()}
979
- </Text>
980
- {file.metadata?.description && (
981
- <Text
982
- style={[fileManagementStyles.fileDescription, { color: colors.textTertiary }]}
983
- numberOfLines={2}
984
- >
985
- {file.metadata.description}
986
- </Text>
987
- )}
988
- </View>
989
- </TouchableOpacity>
990
-
991
- {!selectMode && (
992
- <View style={fileManagementStyles.fileActions}>
993
- {/* Preview button for supported files */}
994
- {hasPreview && (
995
- <TouchableOpacity
996
- style={[fileManagementStyles.actionButton, { backgroundColor: colors.backgroundSecondary }]}
997
- onPress={() => handleFileOpen(file)}
998
- >
999
- <Ionicons name="eye" size={20} color={colors.primary} />
1000
- </TouchableOpacity>
1001
- )}
1002
-
1003
- <TouchableOpacity
1004
- style={[fileManagementStyles.actionButton, { backgroundColor: colors.backgroundSecondary }]}
1005
- onPress={() => handleFileDownload(file.id, file.filename)}
1006
- >
1007
- <Ionicons name="download" size={20} color={colors.primary} />
1008
- </TouchableOpacity>
1009
-
1010
- {/* Always show delete button for debugging */}
1011
- <TouchableOpacity
1012
- style={[fileManagementStyles.actionButton, { backgroundColor: colors.negativeSubtle }]}
1013
- onPress={() => confirmFileDelete(file.id, file.filename)}
1014
- disabled={deleting === file.id}
1015
- >
1016
- {deleting === file.id ? (
1017
- <ActivityIndicator size="small" color={colors.error} />
1018
- ) : (
1019
- <Ionicons name="trash" size={20} color={colors.error} />
1020
- )}
1021
- </TouchableOpacity>
1022
- </View>
1023
- )}
1024
- </View>
1025
- );
1026
- };
1027
-
1028
857
  // SettingsListItem-based file items (for 'all' view)
1029
858
  const groupedFileItems: FileListItem[] = useMemo(() => {
1030
859
  // filteredFiles is already sorted, so just use it directly
@@ -1100,23 +929,26 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1100
929
  },
1101
930
  // Hide action buttons when selecting (in selectMode or bulk operations mode)
1102
931
  rightElement: (!selectMode && selectedIds.size === 0) ? (
1103
- <View style={fileManagementStyles.groupedActions}>
932
+ <View className="flex-row items-center gap-[6px] ml-[12px]">
1104
933
  {(isImage || isVideo || file.contentType.includes('pdf')) && (
1105
934
  <TouchableOpacity
1106
- style={[fileManagementStyles.groupedActionBtn, { backgroundColor: colors.backgroundSecondary }]}
935
+ className="w-[34px] h-[34px] rounded-[8px] items-center justify-center"
936
+ style={{ backgroundColor: colors.backgroundSecondary }}
1107
937
  onPress={() => handleFileOpen(file)}
1108
938
  >
1109
939
  <Ionicons name="eye" size={18} color={colors.primary} />
1110
940
  </TouchableOpacity>
1111
941
  )}
1112
942
  <TouchableOpacity
1113
- style={[fileManagementStyles.groupedActionBtn, { backgroundColor: colors.backgroundSecondary }]}
943
+ className="w-[34px] h-[34px] rounded-[8px] items-center justify-center"
944
+ style={{ backgroundColor: colors.backgroundSecondary }}
1114
945
  onPress={() => handleFileDownload(file.id, file.filename)}
1115
946
  >
1116
947
  <Ionicons name="download" size={18} color={colors.primary} />
1117
948
  </TouchableOpacity>
1118
949
  <TouchableOpacity
1119
- style={[fileManagementStyles.groupedActionBtn, { backgroundColor: colors.negativeSubtle }]}
950
+ className="w-[34px] h-[34px] rounded-[8px] items-center justify-center"
951
+ style={{ backgroundColor: colors.negativeSubtle }}
1120
952
  onPress={() => confirmFileDelete(file.id, file.filename)}
1121
953
  disabled={deleting === file.id}
1122
954
  >
@@ -1177,21 +1009,12 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1177
1009
  const photoIndex = photos.findIndex(p => p.id === lastSelectedFileId);
1178
1010
 
1179
1011
  if (photoIndex >= 0) {
1180
- // Estimate photo item height based on grid layout
1181
- // Calculate items per row
1182
- let itemsPerRow = 3;
1183
- if (safeContainerWidth > 768) itemsPerRow = 6;
1184
- else if (safeContainerWidth > 480) itemsPerRow = 4;
1185
-
1186
- const scrollContainerPadding = 32;
1187
- const gaps = (itemsPerRow - 1) * 4;
1188
- const availableWidth = safeContainerWidth - scrollContainerPadding;
1189
- const itemWidth = (availableWidth - gaps) / itemsPerRow;
1190
-
1191
- // Calculate row and approximate scroll position
1012
+ // Rough scroll estimate for the justified grid (3 photos/row; variable row heights).
1013
+ const itemsPerRow = 3;
1014
+ const estimatedRowHeight = 150;
1015
+ const rowGap = 4;
1192
1016
  const row = Math.floor(photoIndex / itemsPerRow);
1193
- const headerHeight = 250;
1194
- const finalScrollPosition = headerHeight + (row * (itemWidth + 4)) - 150;
1017
+ const finalScrollPosition = row * (estimatedRowHeight + rowGap) - 100;
1195
1018
 
1196
1019
  // Use requestAnimationFrame to ensure DOM is updated before scrolling
1197
1020
  requestAnimationFrame(() => {
@@ -1218,66 +1041,23 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1218
1041
  }
1219
1042
  }, [lastSelectedFileId]);
1220
1043
 
1221
- const renderPhotoItem = (photo: FileMetadata, index: number) => {
1222
- const downloadUrl = thumbSourceFor(photo);
1223
-
1224
- // Calculate photo item width based on actual container size from bottom sheet
1225
- let itemsPerRow = 3; // Default for mobile
1226
- if (safeContainerWidth > 768) itemsPerRow = 6; // Tablet/Desktop
1227
- else if (safeContainerWidth > 480) itemsPerRow = 4; // Large mobile
1228
-
1229
- // Account for the photoScrollContainer padding (16px on each side = 32px total)
1230
- const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
1231
- const gaps = (itemsPerRow - 1) * 4; // Gap between items
1232
- const availableWidth = safeContainerWidth - scrollContainerPadding;
1233
- const itemWidth = (availableWidth - gaps) / itemsPerRow;
1234
-
1235
- return (
1236
- <TouchableOpacity
1237
- key={photo.id}
1238
- style={[
1239
- fileManagementStyles.photoItem,
1240
- {
1241
- width: itemWidth,
1242
- height: itemWidth,
1243
- }
1244
- ]}
1245
- onPress={() => handleFileOpen(photo)}
1246
- activeOpacity={0.8}
1247
- >
1248
- <View style={fileManagementStyles.photoContainer}>
1249
- <ExpoImage
1250
- source={{ uri: downloadUrl }}
1251
- style={fileManagementStyles.photoImage}
1252
- contentFit="cover"
1253
- transition={120}
1254
- cachePolicy="memory-disk"
1255
- onError={() => {
1256
- // Image preview failed to load
1257
- }}
1258
- accessibilityLabel={photo.filename}
1259
- />
1260
- </View>
1261
- </TouchableOpacity>
1262
- );
1263
- };
1264
-
1265
1044
  const renderPhotoGrid = useCallback(() => {
1266
1045
  const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
1267
1046
 
1268
1047
  if (photos.length === 0) {
1269
1048
  return (
1270
- <View style={fileManagementStyles.emptyState}>
1049
+ <View className="items-center py-[40px] px-[24px]">
1271
1050
  <Ionicons name="images-outline" size={64} color={colors.textTertiary} />
1272
- <Text style={[fileManagementStyles.emptyStateTitle, { color: colors.text }]}>{t('fileManagement.emptyPhotos.title')}</Text>
1273
- <Text style={[fileManagementStyles.emptyStateDescription, { color: colors.textSecondary }]}> {
1051
+ <Text className="text-[24px] font-bold mt-[16px] mb-[8px]" style={{ color: colors.text }}>{t('fileManagement.emptyPhotos.title')}</Text>
1052
+ <Text className="text-[16px] text-center leading-[24px] mb-[32px]" style={{ color: colors.textSecondary }}> {
1274
1053
  user?.id === targetUserId
1275
1054
  ? t('fileManagement.emptyPhotos.ownDescription')
1276
1055
  : t('fileManagement.emptyPhotos.otherDescription')
1277
1056
  } </Text>
1278
1057
  {user?.id === targetUserId && (
1279
1058
  <TouchableOpacity
1280
- style={[fileManagementStyles.emptyStateButton, { backgroundColor: colors.primary }]}
1059
+ className="flex-row items-center px-[24px] py-[12px] rounded-[24px] gap-[8px]"
1060
+ style={{ backgroundColor: colors.primary }}
1281
1061
  onPress={handleFileUpload}
1282
1062
  disabled={uploading || isPickingDocument}
1283
1063
  >
@@ -1288,7 +1068,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1288
1068
  ) : (
1289
1069
  <>
1290
1070
  <Ionicons name="cloud-upload" size={20} color="#FFFFFF" />
1291
- <Text style={fileManagementStyles.emptyStateButtonText}>{t('fileManagement.uploadPhotos')}</Text>
1071
+ <Text className="text-white text-[16px] font-semibold">{t('fileManagement.uploadPhotos')}</Text>
1292
1072
  </>
1293
1073
  )}
1294
1074
  </TouchableOpacity>
@@ -1300,8 +1080,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1300
1080
  return (
1301
1081
  <ScrollView
1302
1082
  ref={photoScrollViewRef}
1303
- style={fileManagementStyles.scrollView}
1304
- contentContainerStyle={fileManagementStyles.photoScrollContainer}
1083
+ className="flex-1"
1084
+ contentContainerClassName="p-[10px]"
1305
1085
  refreshControl={
1306
1086
  <RefreshControl
1307
1087
  refreshing={refreshing}
@@ -1320,9 +1100,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1320
1100
  scrollEventThrottle={250}
1321
1101
  >
1322
1102
  {loadingDimensions && (
1323
- <View style={fileManagementStyles.dimensionsLoadingIndicator}>
1103
+ <View className="flex-row items-center justify-center py-[16px] gap-[8px]">
1324
1104
  <ActivityIndicator size="small" color={colors.primary} />
1325
- <Text style={[fileManagementStyles.dimensionsLoadingText, { color: colors.textSecondary }]}>{t('fileManagement.loadingPhotoLayout')}</Text>
1105
+ <Text className="text-[14px] italic" style={{ color: colors.textSecondary }}>{t('fileManagement.loadingPhotoLayout')}</Text>
1326
1106
  </View>
1327
1107
  )}
1328
1108
 
@@ -1332,9 +1112,12 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1332
1112
  loadPhotoDimensions={loadPhotoDimensions}
1333
1113
  createJustifiedRows={createJustifiedRows}
1334
1114
  renderJustifiedPhotoItem={renderJustifiedPhotoItem}
1335
- renderSimplePhotoItem={renderPhotoItem}
1336
1115
  textColor={colors.text}
1337
- containerWidth={safeContainerWidth}
1116
+ containerWidth={
1117
+ typeof containerWidth === 'number' && containerWidth > 0
1118
+ ? containerWidth
1119
+ : undefined
1120
+ }
1338
1121
  />
1339
1122
  </ScrollView>
1340
1123
  );
@@ -1352,8 +1135,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1352
1135
  loadPhotoDimensions,
1353
1136
  createJustifiedRows,
1354
1137
  renderJustifiedPhotoItem,
1355
- renderPhotoItem,
1356
- safeContainerWidth
1138
+ containerWidth,
1357
1139
  ]);
1358
1140
 
1359
1141
  // Inline justified grid removed (moved to components/photogrid/JustifiedPhotoGrid.tsx)
@@ -1361,10 +1143,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1361
1143
 
1362
1144
 
1363
1145
  const renderEmptyState = () => (
1364
- <View style={fileManagementStyles.emptyState}>
1146
+ <View className="items-center py-[40px] px-[24px]">
1365
1147
  <Ionicons name="folder-open-outline" size={64} color={colors.textTertiary} />
1366
- <Text style={[fileManagementStyles.emptyStateTitle, { color: colors.text }]}>{t('fileManagement.emptyFiles.title')}</Text>
1367
- <Text style={[fileManagementStyles.emptyStateDescription, { color: colors.textSecondary }]}>
1148
+ <Text className="text-[24px] font-bold mt-[16px] mb-[8px]" style={{ color: colors.text }}>{t('fileManagement.emptyFiles.title')}</Text>
1149
+ <Text className="text-[16px] text-center leading-[24px] mb-[32px]" style={{ color: colors.textSecondary }}>
1368
1150
  {user?.id === targetUserId
1369
1151
  ? t('fileManagement.emptyFiles.ownDescription')
1370
1152
  : t('fileManagement.emptyFiles.otherDescription')
@@ -1372,7 +1154,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1372
1154
  </Text>
1373
1155
  {user?.id === targetUserId && (
1374
1156
  <TouchableOpacity
1375
- style={[fileManagementStyles.emptyStateButton, { backgroundColor: colors.primary }]}
1157
+ className="flex-row items-center px-[24px] py-[12px] rounded-[24px] gap-[8px]"
1158
+ style={{ backgroundColor: colors.primary }}
1376
1159
  onPress={handleFileUpload}
1377
1160
  disabled={uploading || isPickingDocument}
1378
1161
  >
@@ -1383,7 +1166,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1383
1166
  ) : (
1384
1167
  <>
1385
1168
  <Ionicons name="cloud-upload" size={20} color="#FFFFFF" />
1386
- <Text style={fileManagementStyles.emptyStateButtonText}>{t('fileManagement.uploadFiles')}</Text>
1169
+ <Text className="text-white text-[16px] font-semibold">{t('fileManagement.uploadFiles')}</Text>
1387
1170
  </>
1388
1171
  )}
1389
1172
  </TouchableOpacity>
@@ -1402,7 +1185,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1402
1185
  toValue: 1,
1403
1186
  duration: 2000,
1404
1187
  easing: Easing.linear,
1405
- useNativeDriver: true,
1188
+ // Web has no native animated module; forcing the native
1189
+ // driver there warns and falls back to JS. The shimmer only
1190
+ // drives a transform, so JS-driving it on web is fine.
1191
+ useNativeDriver: Platform.OS !== 'web',
1406
1192
  })
1407
1193
  );
1408
1194
  shimmer.start();
@@ -1511,11 +1297,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1511
1297
  );
1512
1298
 
1513
1299
  return (
1514
- <View style={[fileManagementStyles.container, { backgroundColor }]}>
1300
+ <View className="flex-1" style={{ backgroundColor }}>
1515
1301
  {/* Header Skeleton */}
1516
- <View style={[fileManagementStyles.header, { borderBottomColor: colors.border, borderBottomWidth: StyleSheet.hairlineWidth }]}>
1302
+ <View
1303
+ className="flex-row items-center justify-between px-[16px] py-[12px] relative"
1304
+ style={{ borderBottomColor: colors.border, borderBottomWidth: StyleSheet.hairlineWidth }}
1305
+ >
1517
1306
  <SkeletonBox width={44} height={44} borderRadius={12} />
1518
- <View style={[fileManagementStyles.headerTitleContainer, { flex: 1 }]}>
1307
+ <View className="flex-1 items-center justify-center mx-[16px]">
1519
1308
  <SkeletonBox width={140} height={20} style={{ marginBottom: 6 }} />
1520
1309
  <SkeletonBox width={100} height={14} />
1521
1310
  </View>
@@ -1523,24 +1312,26 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1523
1312
  </View>
1524
1313
 
1525
1314
  {/* Controls Bar Skeleton */}
1526
- <View style={fileManagementStyles.controlsBar}>
1315
+ <View className="flex-row items-center justify-between px-[12px] py-[12px] gap-[12px]">
1527
1316
  <SkeletonBox width={100} height={36} borderRadius={18} />
1528
1317
  <SkeletonBox width={44} height={44} borderRadius={22} />
1529
1318
  </View>
1530
1319
 
1531
1320
  {/* Search Bar Skeleton */}
1532
- <View style={[fileManagementStyles.searchContainer, {
1533
- backgroundColor: colors.card,
1534
- }]}>
1321
+ <View
1322
+ className="flex-row items-center px-[14px] py-[10px] mx-[12px] mb-[12px] rounded-full gap-[10px]"
1323
+ style={{ backgroundColor: colors.card }}
1324
+ >
1535
1325
  <SkeletonBox width="100%" height={44} borderRadius={12} />
1536
1326
  </View>
1537
1327
 
1538
1328
  {/* Stats Container Skeleton */}
1539
- <View style={[fileManagementStyles.statsContainer, {
1540
- backgroundColor: colors.card,
1541
- }]}>
1329
+ <View
1330
+ className="flex-row px-[14px] py-[10px] mx-[12px] mb-[12px] rounded-[18px]"
1331
+ style={{ backgroundColor: colors.card }}
1332
+ >
1542
1333
  {[1, 2, 3].map((i) => (
1543
- <View key={i} style={fileManagementStyles.statItem}>
1334
+ <View key={i} className="flex-1 items-center py-[4px]">
1544
1335
  <SkeletonBox width={50} height={20} style={{ marginBottom: 4 }} delay={i * 30} />
1545
1336
  <SkeletonBox width={40} height={14} delay={i * 30 + 15} />
1546
1337
  </View>
@@ -1549,8 +1340,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1549
1340
 
1550
1341
  {/* File List Skeleton - Matching SettingsListItem */}
1551
1342
  <ScrollView
1552
- style={fileManagementStyles.scrollView}
1553
- contentContainerStyle={fileManagementStyles.scrollContainer}
1343
+ className="flex-1"
1344
+ contentContainerClassName="px-[12px] pt-0 pb-[12px]"
1554
1345
  showsVerticalScrollIndicator={false}
1555
1346
  >
1556
1347
  <View style={{
@@ -1654,7 +1445,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1654
1445
  // If upload preview is showing, render it inline instead of the file list
1655
1446
  if (showUploadPreview) {
1656
1447
  return (
1657
- <View style={fileManagementStyles.container}>
1448
+ <View className="flex-1">
1658
1449
  <Header
1659
1450
  title={t('fileManagement.reviewFiles')}
1660
1451
  subtitle={t('fileManagement.readyToUpload', { count: pendingFiles.length })}
@@ -1676,7 +1467,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1676
1467
  }
1677
1468
 
1678
1469
  return (
1679
- <View style={fileManagementStyles.container}>
1470
+ <View className="flex-1">
1680
1471
  <Header
1681
1472
  title={selectMode ? (multiSelect ? (maxSelection ? t('fileManagement.selectedWithMax', { count: selectedIds.size, max: maxSelection }) : t('fileManagement.selected', { count: selectedIds.size })) : t('fileManagement.selectFile')) : (viewMode === 'photos' ? t('fileManagement.photos') : t('fileManagement.title'))}
1682
1473
  subtitle={selectMode ? (multiSelect ? t('fileManagement.available', { count: filteredFiles.length }) : t('fileManagement.tapToSelect')) : (filteredFiles.length === 1 ? t('fileManagement.itemCount', { count: filteredFiles.length }) : t('fileManagement.itemCount_plural', { count: filteredFiles.length }))}
@@ -1722,25 +1513,24 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1722
1513
  titleAlignment="left"
1723
1514
  />
1724
1515
 
1725
- <View style={fileManagementStyles.controlsBar}>
1516
+ <View className="flex-row items-center justify-between px-[12px] py-[12px] gap-[12px]">
1726
1517
  <ScrollView
1727
1518
  horizontal
1728
1519
  showsHorizontalScrollIndicator={false}
1729
- style={fileManagementStyles.viewModeScroll}
1520
+ className="flex-1"
1521
+ style={{ maxWidth: '80%' }}
1730
1522
  >
1731
- <View style={[
1732
- fileManagementStyles.viewModeToggle,
1733
- {
1734
- backgroundColor: colors.card,
1735
- }
1736
- ]}>
1523
+ <View
1524
+ className="flex-row rounded-full p-[2px] overflow-hidden"
1525
+ style={{ backgroundColor: colors.card }}
1526
+ >
1737
1527
  <AnimatedButton
1738
1528
  isSelected={viewMode === 'all'}
1739
1529
  onPress={() => setViewMode('all')}
1740
1530
  icon={viewMode === 'all' ? 'folder' : 'folder-outline'}
1741
1531
  primaryColor={colors.primary}
1742
1532
  textColor={colors.text}
1743
- style={fileManagementStyles.viewModeButton}
1533
+ style={screenStyles.viewModeButton}
1744
1534
  accessibilityLabel={t('fileManagement.a11y.viewAll') || 'Show all files'}
1745
1535
  />
1746
1536
  <AnimatedButton
@@ -1749,7 +1539,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1749
1539
  icon={viewMode === 'photos' ? 'image-multiple' : 'image-multiple-outline'}
1750
1540
  primaryColor={colors.primary}
1751
1541
  textColor={colors.text}
1752
- style={fileManagementStyles.viewModeButton}
1542
+ style={screenStyles.viewModeButton}
1753
1543
  accessibilityLabel={t('fileManagement.a11y.viewPhotos') || 'Show photos only'}
1754
1544
  />
1755
1545
  {!isImageOnlyPicker && (
@@ -1760,7 +1550,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1760
1550
  icon={viewMode === 'videos' ? 'video' : 'video-outline'}
1761
1551
  primaryColor={colors.primary}
1762
1552
  textColor={colors.text}
1763
- style={fileManagementStyles.viewModeButton}
1553
+ style={screenStyles.viewModeButton}
1764
1554
  accessibilityLabel={t('fileManagement.a11y.viewVideos') || 'Show videos only'}
1765
1555
  />
1766
1556
  <AnimatedButton
@@ -1769,7 +1559,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1769
1559
  icon={viewMode === 'documents' ? 'file-document' : 'file-document-outline'}
1770
1560
  primaryColor={colors.primary}
1771
1561
  textColor={colors.text}
1772
- style={fileManagementStyles.viewModeButton}
1562
+ style={screenStyles.viewModeButton}
1773
1563
  accessibilityLabel={t('fileManagement.a11y.viewDocuments') || 'Show documents only'}
1774
1564
  />
1775
1565
  <AnimatedButton
@@ -1778,7 +1568,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1778
1568
  icon={viewMode === 'audio' ? 'music-note' : 'music-note-outline'}
1779
1569
  primaryColor={colors.primary}
1780
1570
  textColor={colors.text}
1781
- style={fileManagementStyles.viewModeButton}
1571
+ style={screenStyles.viewModeButton}
1782
1572
  accessibilityLabel={t('fileManagement.a11y.viewAudio') || 'Show audio only'}
1783
1573
  />
1784
1574
  </>
@@ -1786,9 +1576,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1786
1576
  </View>
1787
1577
  </ScrollView>
1788
1578
  <TouchableOpacity
1789
- style={[fileManagementStyles.sortButton, {
1790
- backgroundColor: colors.card,
1791
- }]}
1579
+ className="flex-row items-center justify-center px-[10px] py-[6px] rounded-full min-w-[36px] gap-[4px]"
1580
+ style={{ backgroundColor: colors.card }}
1792
1581
  accessibilityRole="button"
1793
1582
  accessibilityLabel={t('fileManagement.a11y.sortBy', {
1794
1583
  field: sortBy,
@@ -1823,11 +1612,8 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1823
1612
  </TouchableOpacity>
1824
1613
  {user?.id === targetUserId && (!selectMode || (selectMode && allowUploadInSelectMode)) && (
1825
1614
  <TouchableOpacity
1826
- style={[
1827
- fileManagementStyles.uploadButton,
1828
- isImageOnlyPicker && fileManagementStyles.uploadButtonExtended,
1829
- { backgroundColor: colors.primary },
1830
- ]}
1615
+ className={`h-[44px] rounded-[22px] items-center justify-center ${isImageOnlyPicker ? 'w-auto min-w-[44px] px-[14px]' : 'w-[44px]'}`}
1616
+ style={{ backgroundColor: colors.primary }}
1831
1617
  onPress={handleFileUpload}
1832
1618
  disabled={uploading || isPickingDocument}
1833
1619
  accessibilityRole="button"
@@ -1839,10 +1625,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1839
1625
  accessibilityState={{ busy: uploading || isPickingDocument }}
1840
1626
  >
1841
1627
  {uploading ? (
1842
- <View style={fileManagementStyles.uploadProgress}>
1628
+ <View className="items-center justify-center">
1843
1629
  <ActivityIndicator size="small" color="#FFFFFF" />
1844
1630
  {uploadProgress && (
1845
- <Text style={fileManagementStyles.uploadProgressText}>
1631
+ <Text className="text-white text-[10px] font-semibold mt-[2px]">
1846
1632
  {uploadProgress.current}/{uploadProgress.total}
1847
1633
  </Text>
1848
1634
  )}
@@ -1850,9 +1636,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1850
1636
  ) : isPickingDocument ? (
1851
1637
  <ActivityIndicator size="small" color="#FFFFFF" />
1852
1638
  ) : isImageOnlyPicker ? (
1853
- <View style={fileManagementStyles.uploadButtonContent}>
1639
+ <View className="flex-row items-center gap-[6px]">
1854
1640
  <Ionicons name="cloud-upload" size={18} color="#FFFFFF" />
1855
- <Text style={fileManagementStyles.uploadButtonLabel} numberOfLines={1}>
1641
+ <Text className="text-white text-[14px] font-semibold" numberOfLines={1}>
1856
1642
  {t('fileManagement.upload') || 'Upload'}
1857
1643
  </Text>
1858
1644
  </View>
@@ -1865,15 +1651,14 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1865
1651
 
1866
1652
  {/* Search Bar */}
1867
1653
  {files.length > 0 && (viewMode === 'all' || files.some(f => f.contentType.startsWith('image/'))) && (
1868
- <View style={[
1869
- fileManagementStyles.searchContainer,
1870
- {
1871
- backgroundColor: colors.card,
1872
- }
1873
- ]}>
1654
+ <View
1655
+ className="flex-row items-center px-[14px] py-[10px] mx-[12px] mb-[12px] rounded-full gap-[10px]"
1656
+ style={{ backgroundColor: colors.card }}
1657
+ >
1874
1658
  <Ionicons name="search" size={22} color={colors.icon} />
1875
1659
  <TextInput
1876
- style={[fileManagementStyles.searchInput, { color: colors.text }]}
1660
+ className="flex-1 text-[16px] leading-[20px]"
1661
+ style={{ color: colors.text }}
1877
1662
  placeholder={viewMode === 'photos' ? t('fileManagement.searchPhotos') : t('fileManagement.searchFiles')}
1878
1663
  placeholderTextColor={colors.textSecondary}
1879
1664
  value={searchQuery}
@@ -1882,7 +1667,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1882
1667
  {searchQuery.length > 0 && (
1883
1668
  <TouchableOpacity
1884
1669
  onPress={() => setSearchQuery('')}
1885
- style={fileManagementStyles.searchClearButton}
1670
+ className="p-[4px] rounded-[12px] items-center justify-center"
1886
1671
  >
1887
1672
  <Ionicons name="close-circle" size={22} color={colors.icon} />
1888
1673
  </TouchableOpacity>
@@ -1892,30 +1677,28 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1892
1677
 
1893
1678
  {/* File Stats */}
1894
1679
  {files.length > 0 && (
1895
- <View style={[
1896
- fileManagementStyles.statsContainer,
1897
- {
1898
- backgroundColor: colors.card,
1899
- }
1900
- ]}>
1901
- <View style={fileManagementStyles.statItem}>
1902
- <Text style={[fileManagementStyles.statValue, { color: colors.text }]}>{filteredFiles.length}</Text>
1903
- <Text style={[fileManagementStyles.statLabel, { color: colors.textSecondary }]}>
1680
+ <View
1681
+ className="flex-row px-[14px] py-[10px] mx-[12px] mb-[12px] rounded-[18px]"
1682
+ style={{ backgroundColor: colors.card }}
1683
+ >
1684
+ <View className="flex-1 items-center py-[4px]">
1685
+ <Text className="text-[20px] font-extrabold leading-[24px]" style={{ color: colors.text, letterSpacing: -0.5 }}>{filteredFiles.length}</Text>
1686
+ <Text className="text-[12px] font-medium mt-[2px]" style={{ color: colors.textSecondary, letterSpacing: 0.2 }}>
1904
1687
  {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')))}
1905
1688
  </Text>
1906
1689
  </View>
1907
- <View style={fileManagementStyles.statItem}>
1908
- <Text style={[fileManagementStyles.statValue, { color: colors.text }]}>
1690
+ <View className="flex-1 items-center py-[4px]">
1691
+ <Text className="text-[20px] font-extrabold leading-[24px]" style={{ color: colors.text, letterSpacing: -0.5 }}>
1909
1692
  {formatFileSize(filteredFiles.reduce((total, file) => total + file.length, 0))}
1910
1693
  </Text>
1911
- <Text style={[fileManagementStyles.statLabel, { color: colors.textSecondary }]}>
1694
+ <Text className="text-[12px] font-medium mt-[2px]" style={{ color: colors.textSecondary, letterSpacing: 0.2 }}>
1912
1695
  {searchQuery.length > 0 ? t('fileManagement.size') : t('fileManagement.totalSize')}
1913
1696
  </Text>
1914
1697
  </View>
1915
1698
  {searchQuery.length > 0 && (
1916
- <View style={fileManagementStyles.statItem}>
1917
- <Text style={[fileManagementStyles.statValue, { color: colors.text }]}>{files.length}</Text>
1918
- <Text style={[fileManagementStyles.statLabel, { color: colors.textSecondary }]}>
1699
+ <View className="flex-1 items-center py-[4px]">
1700
+ <Text className="text-[20px] font-extrabold leading-[24px]" style={{ color: colors.text, letterSpacing: -0.5 }}>{files.length}</Text>
1701
+ <Text className="text-[12px] font-medium mt-[2px]" style={{ color: colors.textSecondary, letterSpacing: 0.2 }}>
1919
1702
  {t('fileManagement.total')}
1920
1703
  </Text>
1921
1704
  </View>
@@ -1998,6 +1781,4 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
1998
1781
  );
1999
1782
  };
2000
1783
 
2001
- // Styles have been moved to components/fileManagement/styles.ts
2002
-
2003
1784
  export default FileManagementScreen;