@oxyhq/services 22.10.0 → 22.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ui/components/AvatarCameraBadge.js +42 -0
- package/lib/commonjs/ui/components/AvatarCameraBadge.js.map +1 -0
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +41 -19
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +640 -168
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileButton.js +14 -0
- package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileSummaryCard.js +3 -23
- package/lib/commonjs/ui/components/ProfileSummaryCard.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +41 -5
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js +78 -26
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +1 -1
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js +83 -23
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +73 -37
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js +18 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useFileQueries.js +129 -0
- package/lib/commonjs/ui/hooks/queries/useFileQueries.js.map +1 -0
- package/lib/commonjs/ui/hooks/useAvatarPicker.js +36 -48
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/commonjs/ui/hooks/useReduceMotion.js +57 -0
- package/lib/commonjs/ui/hooks/useReduceMotion.js.map +1 -0
- package/lib/commonjs/ui/hooks/useSurfaceHeader.js +1 -1
- package/lib/commonjs/ui/hooks/useSurfaceHeader.js.map +1 -1
- package/lib/commonjs/ui/navigation/accountDialogManager.js +22 -0
- package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -1
- package/lib/commonjs/ui/navigation/bottomSheetManager.js +5 -6
- package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +1 -0
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaceNavStack.js +62 -15
- package/lib/commonjs/ui/navigation/surfaceNavStack.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaceRegistry.js +38 -17
- package/lib/commonjs/ui/navigation/surfaceRegistry.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaces.js +98 -21
- package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
- package/lib/commonjs/ui/screens/AvatarCropScreen.js +281 -279
- package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ChangeAvatarScreen.js +301 -0
- package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/FileManagementScreen.js +194 -431
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +16 -48
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/FileLibraryError.js +63 -0
- package/lib/commonjs/ui/screens/fileManagement/FileLibraryError.js.map +1 -0
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +54 -41
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +44 -81
- package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
- package/lib/commonjs/ui/stores/resetSessionScopedStores.js +8 -3
- package/lib/commonjs/ui/stores/resetSessionScopedStores.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +11 -2
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/commonjs/ui/utils/fileManagement.js +0 -79
- package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
- package/lib/module/ui/components/AvatarCameraBadge.js +40 -0
- package/lib/module/ui/components/AvatarCameraBadge.js.map +1 -0
- package/lib/module/ui/components/OxyAccountDialogScreen.js +41 -19
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +642 -171
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/ProfileButton.js +15 -1
- package/lib/module/ui/components/ProfileButton.js.map +1 -1
- package/lib/module/ui/components/ProfileSummaryCard.js +2 -23
- package/lib/module/ui/components/ProfileSummaryCard.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +43 -7
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/oxyAuthChooserStyles.js +78 -26
- package/lib/module/ui/components/oxyAuthChooserStyles.js.map +1 -1
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js +83 -25
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +74 -38
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/queries/queryKeys.js +16 -0
- package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/module/ui/hooks/queries/useFileQueries.js +120 -0
- package/lib/module/ui/hooks/queries/useFileQueries.js.map +1 -0
- package/lib/module/ui/hooks/useAvatarPicker.js +37 -49
- package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/module/ui/hooks/useReduceMotion.js +53 -0
- package/lib/module/ui/hooks/useReduceMotion.js.map +1 -0
- package/lib/module/ui/hooks/useSurfaceHeader.js +1 -1
- package/lib/module/ui/hooks/useSurfaceHeader.js.map +1 -1
- package/lib/module/ui/navigation/accountDialogManager.js +20 -0
- package/lib/module/ui/navigation/accountDialogManager.js.map +1 -1
- package/lib/module/ui/navigation/bottomSheetManager.js +6 -7
- package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +1 -0
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/navigation/surfaceNavStack.js +62 -15
- package/lib/module/ui/navigation/surfaceNavStack.js.map +1 -1
- package/lib/module/ui/navigation/surfaceRegistry.js +38 -17
- package/lib/module/ui/navigation/surfaceRegistry.js.map +1 -1
- package/lib/module/ui/navigation/surfaces.js +97 -22
- package/lib/module/ui/navigation/surfaces.js.map +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js +283 -281
- package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/module/ui/screens/ChangeAvatarScreen.js +298 -0
- package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -0
- package/lib/module/ui/screens/FileManagementScreen.js +196 -433
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +17 -49
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/FileLibraryError.js +59 -0
- package/lib/module/ui/screens/fileManagement/FileLibraryError.js.map +1 -0
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +54 -42
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +45 -81
- package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
- package/lib/module/ui/stores/resetSessionScopedStores.js +8 -3
- package/lib/module/ui/stores/resetSessionScopedStores.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +11 -2
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/ui/utils/fileManagement.js +0 -78
- package/lib/module/ui/utils/fileManagement.js.map +1 -1
- package/lib/typescript/commonjs/ui/components/AvatarCameraBadge.d.ts +4 -0
- package/lib/typescript/commonjs/ui/components/AvatarCameraBadge.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts +2 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileSummaryCard.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts +1 -4
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts +71 -19
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts +10 -8
- package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts +9 -0
- package/lib/typescript/commonjs/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useFileQueries.d.ts +50 -0
- package/lib/typescript/commonjs/ui/hooks/queries/useFileQueries.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts +15 -10
- package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useReduceMotion.d.ts +14 -0
- package/lib/typescript/commonjs/ui/hooks/useReduceMotion.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +13 -0
- package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts +4 -5
- package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaceNavStack.d.ts +21 -0
- package/lib/typescript/commonjs/ui/navigation/surfaceNavStack.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaceRegistry.d.ts +73 -13
- package/lib/typescript/commonjs/ui/navigation/surfaceRegistry.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts +42 -11
- package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts +18 -5
- package/lib/typescript/commonjs/ui/screens/AvatarCropScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts +34 -0
- package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/FileLibraryError.d.ts +23 -0
- package/lib/typescript/commonjs/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts +4 -0
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +10 -2
- package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/resetSessionScopedStores.d.ts +8 -1
- package/lib/typescript/commonjs/ui/stores/resetSessionScopedStores.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +2 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +0 -48
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/AvatarCameraBadge.d.ts +4 -0
- package/lib/typescript/module/ui/components/AvatarCameraBadge.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts +2 -1
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileSummaryCard.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts +1 -4
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts +71 -19
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts +10 -8
- package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts +9 -0
- package/lib/typescript/module/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useFileQueries.d.ts +50 -0
- package/lib/typescript/module/ui/hooks/queries/useFileQueries.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts +15 -10
- package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useReduceMotion.d.ts +14 -0
- package/lib/typescript/module/ui/hooks/useReduceMotion.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +13 -0
- package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts +4 -5
- package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaceNavStack.d.ts +21 -0
- package/lib/typescript/module/ui/navigation/surfaceNavStack.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaceRegistry.d.ts +73 -13
- package/lib/typescript/module/ui/navigation/surfaceRegistry.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaces.d.ts +42 -11
- package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts +18 -5
- package/lib/typescript/module/ui/screens/AvatarCropScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts +34 -0
- package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/FileLibraryError.d.ts +23 -0
- package/lib/typescript/module/ui/screens/fileManagement/FileLibraryError.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts +4 -0
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +10 -2
- package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/resetSessionScopedStores.d.ts +8 -1
- package/lib/typescript/module/ui/stores/resetSessionScopedStores.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +2 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/fileManagement.d.ts +0 -48
- package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/ui/components/AvatarCameraBadge.tsx +35 -0
- package/src/ui/components/OxyAccountDialogScreen.tsx +38 -15
- package/src/ui/components/OxyAuthChooser.tsx +722 -185
- package/src/ui/components/ProfileButton.tsx +16 -1
- package/src/ui/components/ProfileSummaryCard.tsx +2 -25
- package/src/ui/components/SurfaceScreen.tsx +48 -14
- package/src/ui/components/oxyAuthChooserStyles.ts +75 -23
- package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +158 -84
- package/src/ui/components/photogrid/__tests__/chunkPhotos.test.ts +51 -0
- package/src/ui/context/OxyContext.tsx +74 -34
- package/src/ui/hooks/queries/queryKeys.ts +15 -0
- package/src/ui/hooks/queries/useFileQueries.ts +162 -0
- package/src/ui/hooks/useAvatarPicker.ts +38 -53
- package/src/ui/hooks/useReduceMotion.ts +58 -0
- package/src/ui/hooks/useSurfaceHeader.ts +2 -1
- package/src/ui/navigation/__tests__/getSurfaceConfig.test.ts +55 -7
- package/src/ui/navigation/__tests__/surfaceNavStack.test.ts +81 -0
- package/src/ui/navigation/accountDialogManager.ts +28 -0
- package/src/ui/navigation/bottomSheetManager.ts +6 -7
- package/src/ui/navigation/routes.ts +3 -1
- package/src/ui/navigation/surfaceNavStack.ts +72 -9
- package/src/ui/navigation/surfaceRegistry.ts +91 -17
- package/src/ui/navigation/surfaces.ts +108 -29
- package/src/ui/screens/AvatarCropScreen.tsx +336 -298
- package/src/ui/screens/ChangeAvatarScreen.tsx +359 -0
- package/src/ui/screens/FileManagementScreen.tsx +189 -432
- package/src/ui/screens/WelcomeNewUserScreen.tsx +18 -48
- package/src/ui/screens/fileManagement/FileLibraryError.tsx +56 -0
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +56 -46
- package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +55 -71
- package/src/ui/stores/resetSessionScopedStores.ts +8 -3
- package/src/ui/types/navigation.ts +2 -0
- package/src/ui/utils/avatarUtils.ts +11 -2
- package/src/ui/utils/fileManagement.ts +1 -101
- package/lib/commonjs/ui/stores/fileStore.js +0 -153
- package/lib/commonjs/ui/stores/fileStore.js.map +0 -1
- package/lib/module/ui/stores/fileStore.js +0 -145
- package/lib/module/ui/stores/fileStore.js.map +0 -1
- package/lib/typescript/commonjs/ui/stores/fileStore.d.ts +0 -31
- package/lib/typescript/commonjs/ui/stores/fileStore.d.ts.map +0 -1
- package/lib/typescript/module/ui/stores/fileStore.d.ts +0 -31
- package/lib/typescript/module/ui/stores/fileStore.d.ts.map +0 -1
- package/src/ui/stores/fileStore.ts +0 -118
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useState,
|
|
4
|
-
import { View, Text, TouchableOpacity, StyleSheet, ScrollView, ActivityIndicator, RefreshControl, TextInput,
|
|
3
|
+
import React, { useState, useCallback, useMemo, useRef } from 'react';
|
|
4
|
+
import { View, Text, TouchableOpacity, StyleSheet, ScrollView, ActivityIndicator, RefreshControl, TextInput, useWindowDimensions } from 'react-native';
|
|
5
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
5
6
|
import { Image as ExpoImage } from 'expo-image';
|
|
6
7
|
import { toast } from '@oxyhq/bloom';
|
|
7
8
|
import { surfaces } from '@oxyhq/bloom/surfaces';
|
|
8
9
|
import * as Skeleton from '@oxyhq/bloom/skeleton';
|
|
9
10
|
import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';
|
|
10
11
|
import { Pressable } from 'react-native';
|
|
11
|
-
import {
|
|
12
|
+
import { queryKeys } from "../hooks/queries/queryKeys.js";
|
|
13
|
+
import { useUserFilesInfinite, removeFileFromCache, patchFileMetadataInCache } from "../hooks/queries/useFileQueries.js";
|
|
12
14
|
import { useSurfaceHeader } from "../hooks/useSurfaceHeader.js";
|
|
13
15
|
import { SurfaceHeaderAction } from "../components/SurfaceHeaderAction.js";
|
|
14
16
|
import JustifiedPhotoGrid from "../components/photogrid/JustifiedPhotoGrid.js";
|
|
15
17
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
16
18
|
import { useOxy } from "../context/OxyContext.js";
|
|
17
19
|
import { useI18n } from "../hooks/useI18n.js";
|
|
20
|
+
import { useReduceMotion } from "../hooks/useReduceMotion.js";
|
|
18
21
|
import { useUploadFile } from "../hooks/mutations/useAccountMutations.js";
|
|
19
22
|
import { formatFileSize, getFileIcon } from "../utils/fileManagement.js";
|
|
20
23
|
import { useResolvedFileUrls, fileThumbSource } from "../hooks/useResolvedFileUrls.js";
|
|
@@ -26,6 +29,7 @@ import { getErrorMessage } from "./fileManagement/shared.js";
|
|
|
26
29
|
import { useFileUploadState } from "./fileManagement/hooks/useFileUploadState.js";
|
|
27
30
|
import PhotoPickerView from "./fileManagement/PhotoPickerSection.js";
|
|
28
31
|
import FileListSection from "./fileManagement/FileListSection.js";
|
|
32
|
+
import FileLibraryError from "./fileManagement/FileLibraryError.js";
|
|
29
33
|
import UploadBar from "./fileManagement/UploadBar.js";
|
|
30
34
|
import { AnimatedButton } from "../components/fileManagement/AnimatedButton.js";
|
|
31
35
|
|
|
@@ -57,11 +61,6 @@ const screenStyles = StyleSheet.create({
|
|
|
57
61
|
justifyContent: 'center',
|
|
58
62
|
marginHorizontal: 1
|
|
59
63
|
},
|
|
60
|
-
photoImage: {
|
|
61
|
-
width: '100%',
|
|
62
|
-
height: '100%',
|
|
63
|
-
borderRadius: 8
|
|
64
|
-
},
|
|
65
64
|
justifiedPhotoImage: {
|
|
66
65
|
width: '100%',
|
|
67
66
|
height: '100%',
|
|
@@ -70,7 +69,6 @@ const screenStyles = StyleSheet.create({
|
|
|
70
69
|
});
|
|
71
70
|
const FileManagementScreen = ({
|
|
72
71
|
onClose,
|
|
73
|
-
theme,
|
|
74
72
|
goBack,
|
|
75
73
|
navigate,
|
|
76
74
|
dismiss,
|
|
@@ -99,50 +97,49 @@ const FileManagementScreen = ({
|
|
|
99
97
|
const {
|
|
100
98
|
t
|
|
101
99
|
} = useI18n();
|
|
100
|
+
const queryClient = useQueryClient();
|
|
102
101
|
const uploadFileMutation = useUploadFile();
|
|
103
|
-
|
|
102
|
+
|
|
103
|
+
// Files are owned by the ACTIVE account. `targetUserId` scopes the query key,
|
|
104
|
+
// so switching accounts (or an explicit `userId` prop) shows that account's
|
|
105
|
+
// library with no manual reset.
|
|
106
|
+
const targetUserId = userId || user?.id;
|
|
107
|
+
|
|
108
|
+
// The file list is React Query's, NOT a store: infinite-paginated, fetched on
|
|
109
|
+
// mount + on owner change, edited optimistically via cache helpers. The
|
|
110
|
+
// rendered list is the flattened pages.
|
|
111
|
+
const filesQuery = useUserFilesInfinite(targetUserId);
|
|
112
|
+
const files = useMemo(() => filesQuery.data?.pages.flatMap(page => page.files) ?? [], [filesQuery.data]);
|
|
113
|
+
// No owner resolved yet reads as "loading" (matches the old skeleton-until-
|
|
114
|
+
// first-load behaviour); `isLoading` is the first-page fetch only.
|
|
115
|
+
const isLoadingFiles = !targetUserId || filesQuery.isLoading;
|
|
116
|
+
const isRefreshingFiles = filesQuery.isRefetching;
|
|
117
|
+
const isFetchingMore = filesQuery.isFetchingNextPage;
|
|
118
|
+
const hasMoreFiles = filesQuery.hasNextPage ?? false;
|
|
119
|
+
// Terminal load failure with nothing cached — render a DISTINCT error state
|
|
120
|
+
// (not the empty state) so a failed load never looks like an empty library.
|
|
121
|
+
const hasLoadError = filesQuery.isError && files.length === 0;
|
|
122
|
+
const invalidateFiles = useCallback(() => {
|
|
123
|
+
queryClient.invalidateQueries({
|
|
124
|
+
queryKey: queryKeys.files.list(targetUserId)
|
|
125
|
+
});
|
|
126
|
+
}, [queryClient, targetUserId]);
|
|
104
127
|
const {
|
|
105
128
|
width: windowWidth
|
|
106
129
|
} = useWindowDimensions();
|
|
107
130
|
// Prefer an explicit sheet width from the router; fall back to the window
|
|
108
131
|
// so photo grids never size against a hardcoded 400px default.
|
|
109
132
|
const safeContainerWidth = typeof containerWidth === 'number' && containerWidth > 0 ? containerWidth : windowWidth;
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const deleting = useDeletingStore();
|
|
113
|
-
const [loading, setLoading] = useState(true);
|
|
114
|
-
const [refreshing, setRefreshing] = useState(false);
|
|
115
|
-
const [paging, setPaging] = useState({
|
|
116
|
-
offset: 0,
|
|
117
|
-
limit: 40,
|
|
118
|
-
total: 0,
|
|
119
|
-
hasMore: true,
|
|
120
|
-
loadingMore: false
|
|
121
|
-
});
|
|
133
|
+
// In-flight delete target (local UI state — was in the file store).
|
|
134
|
+
const [deletingId, setDeletingId] = useState(null);
|
|
122
135
|
// In selectMode we never open the detailed viewer
|
|
123
136
|
const [openedFile, setOpenedFile] = useState(null);
|
|
124
137
|
const [fileContent, setFileContent] = useState(null);
|
|
125
138
|
const [loadingFileContent, setLoadingFileContent] = useState(false);
|
|
126
139
|
const [showFileDetailsInViewer, setShowFileDetailsInViewer] = useState(false);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// Used by `PhotoPickerView` to skip cell stagger animations.
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
let cancelled = false;
|
|
133
|
-
AccessibilityInfo.isReduceMotionEnabled().then(enabled => {
|
|
134
|
-
if (!cancelled) setReduceMotion(enabled);
|
|
135
|
-
}).catch(() => {
|
|
136
|
-
// Defaults to false; no action needed.
|
|
137
|
-
});
|
|
138
|
-
const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', enabled => {
|
|
139
|
-
setReduceMotion(enabled);
|
|
140
|
-
});
|
|
141
|
-
return () => {
|
|
142
|
-
cancelled = true;
|
|
143
|
-
sub.remove();
|
|
144
|
-
};
|
|
145
|
-
}, []);
|
|
140
|
+
// Reduce-motion preference, read as an external store (no effect). Passed to
|
|
141
|
+
// `PhotoPickerView` to skip cell stagger animations.
|
|
142
|
+
const reduceMotion = useReduceMotion();
|
|
146
143
|
|
|
147
144
|
// Image-only picker mode: when the consumer restricts to image MIME types
|
|
148
145
|
// (e.g. avatar picker), photos grid is the more useful default view.
|
|
@@ -194,24 +191,59 @@ const FileManagementScreen = ({
|
|
|
194
191
|
});
|
|
195
192
|
return sorted;
|
|
196
193
|
}, [files, searchQuery, viewMode, sortBy, sortOrder]);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const [
|
|
201
|
-
// Selection state
|
|
202
|
-
const [selectedIds, setSelectedIds] = useState(new Set(initialSelectedIds));
|
|
203
|
-
const [lastSelectedFileId, setLastSelectedFileId] = useState(null);
|
|
194
|
+
// Selection state. `initialSelectedIds` is the INITIAL selection only (lazy
|
|
195
|
+
// init) — there is no prop→state sync effect, since no caller mutates it
|
|
196
|
+
// after mount.
|
|
197
|
+
const [selectedIds, setSelectedIds] = useState(() => new Set(initialSelectedIds));
|
|
204
198
|
const scrollViewRef = useRef(null);
|
|
205
199
|
const photoScrollViewRef = useRef(null);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
200
|
+
|
|
201
|
+
// Scroll the browse list/grid to a just-selected file. Driven directly from
|
|
202
|
+
// the selection event handler (double-rAF so the selection re-render has
|
|
203
|
+
// committed first) — not an effect. No-ops in the image-only picker, whose
|
|
204
|
+
// own FlatList is not either ScrollView ref.
|
|
205
|
+
const scrollToSelectedFile = useCallback(fileId => {
|
|
206
|
+
if (viewMode === 'all' && scrollViewRef.current) {
|
|
207
|
+
const itemIndex = filteredFiles.findIndex(file => file.id === fileId);
|
|
208
|
+
if (itemIndex < 0) return;
|
|
209
|
+
// GroupedItem dense rows are ~65px, +30px when a description wraps.
|
|
210
|
+
const baseItemHeight = 65;
|
|
211
|
+
const descriptionHeight = 30;
|
|
212
|
+
let scrollPosition = 0;
|
|
213
|
+
for (let i = 0; i <= itemIndex && i < filteredFiles.length; i++) {
|
|
214
|
+
scrollPosition += baseItemHeight;
|
|
215
|
+
if (filteredFiles[i].metadata?.description) scrollPosition += descriptionHeight;
|
|
216
|
+
}
|
|
217
|
+
// Offset for the header/controls/search/stats chrome (~250px) so the
|
|
218
|
+
// item lands near the top, not under the chrome.
|
|
219
|
+
const finalScrollPosition = 250 + scrollPosition - 150;
|
|
220
|
+
requestAnimationFrame(() => {
|
|
221
|
+
requestAnimationFrame(() => {
|
|
222
|
+
scrollViewRef.current?.scrollTo({
|
|
223
|
+
y: Math.max(0, finalScrollPosition),
|
|
224
|
+
animated: true
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
} else if (viewMode === 'photos' && photoScrollViewRef.current) {
|
|
229
|
+
const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
|
|
230
|
+
const photoIndex = photos.findIndex(p => p.id === fileId);
|
|
231
|
+
if (photoIndex < 0) return;
|
|
232
|
+
// Rough estimate for the justified grid (3 photos/row, variable heights).
|
|
233
|
+
const row = Math.floor(photoIndex / 3);
|
|
234
|
+
const finalScrollPosition = row * (150 + 4) - 100;
|
|
235
|
+
requestAnimationFrame(() => {
|
|
236
|
+
requestAnimationFrame(() => {
|
|
237
|
+
photoScrollViewRef.current?.scrollTo({
|
|
238
|
+
y: Math.max(0, finalScrollPosition),
|
|
239
|
+
animated: true
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
210
243
|
}
|
|
211
|
-
}, [
|
|
244
|
+
}, [viewMode, filteredFiles]);
|
|
212
245
|
const toggleSelect = useCallback(async file => {
|
|
213
|
-
//
|
|
214
|
-
// if (!selectMode) return;
|
|
246
|
+
// Selection is allowed in regular mode too (for bulk operations).
|
|
215
247
|
if (disabledMimeTypes.length) {
|
|
216
248
|
const blocked = disabledMimeTypes.some(mt => file.contentType === mt || file.contentType.startsWith(mt.endsWith('/') ? mt : `${mt}/`));
|
|
217
249
|
if (blocked) {
|
|
@@ -230,9 +262,6 @@ const FileManagementScreen = ({
|
|
|
230
262
|
}
|
|
231
263
|
}
|
|
232
264
|
|
|
233
|
-
// Track the selected file for scrolling
|
|
234
|
-
setLastSelectedFileId(file.id);
|
|
235
|
-
|
|
236
265
|
// Link file to entity if linkContext is provided
|
|
237
266
|
if (linkContext) {
|
|
238
267
|
try {
|
|
@@ -274,13 +303,14 @@ const FileManagementScreen = ({
|
|
|
274
303
|
}
|
|
275
304
|
return next;
|
|
276
305
|
});
|
|
277
|
-
|
|
306
|
+
scrollToSelectedFile(file.id);
|
|
307
|
+
}, [multiSelect, onSelect, onClose, goBack, dismiss, disabledMimeTypes, maxSelection, afterSelect, defaultVisibility, oxyServices, linkContext, t, scrollToSelectedFile]);
|
|
278
308
|
const confirmMultiSelection = useCallback(async () => {
|
|
279
309
|
if (!selectMode || !multiSelect) return;
|
|
280
310
|
const map = {};
|
|
281
|
-
|
|
311
|
+
for (const f of files) {
|
|
282
312
|
map[f.id] = f;
|
|
283
|
-
}
|
|
313
|
+
}
|
|
284
314
|
const chosen = Array.from(selectedIds).map(id => map[id]).filter(Boolean);
|
|
285
315
|
|
|
286
316
|
// Update visibility and link files if needed
|
|
@@ -334,81 +364,16 @@ const FileManagementScreen = ({
|
|
|
334
364
|
// FileManagementScreen uses a slightly elevated page background.
|
|
335
365
|
const backgroundColor = colors.backgroundSecondary;
|
|
336
366
|
const borderColor = colors.border;
|
|
337
|
-
const targetUserId = userId || user?.id;
|
|
338
|
-
const storeSetDeleting = useFileStore(s => s.setDeleting);
|
|
339
|
-
const loadFiles = useCallback(async (mode = 'initial') => {
|
|
340
|
-
if (!targetUserId) return;
|
|
341
|
-
try {
|
|
342
|
-
if (mode === 'refresh') {
|
|
343
|
-
setRefreshing(true);
|
|
344
|
-
} else if (mode === 'initial') {
|
|
345
|
-
setLoading(true);
|
|
346
|
-
setPaging(p => ({
|
|
347
|
-
...p,
|
|
348
|
-
offset: 0,
|
|
349
|
-
hasMore: true
|
|
350
|
-
}));
|
|
351
|
-
} else if (mode === 'more') {
|
|
352
|
-
// Prevent duplicate fetches
|
|
353
|
-
setPaging(p => ({
|
|
354
|
-
...p,
|
|
355
|
-
loadingMore: true
|
|
356
|
-
}));
|
|
357
|
-
}
|
|
358
|
-
const currentPaging = mode === 'more' ? prevPagingRef.current ?? paging : paging;
|
|
359
|
-
const effectiveOffset = mode === 'more' ? currentPaging.offset + currentPaging.limit : 0;
|
|
360
|
-
const response = await oxyServices.listUserFiles(currentPaging.limit, effectiveOffset);
|
|
361
|
-
const assets = (response.files || []).map(f => ({
|
|
362
|
-
id: f.id,
|
|
363
|
-
filename: f.originalName ?? f.sha256 ?? '',
|
|
364
|
-
contentType: f.mime ?? '',
|
|
365
|
-
length: f.size ?? 0,
|
|
366
|
-
chunkSize: 0,
|
|
367
|
-
uploadDate: f.createdAt ?? '',
|
|
368
|
-
metadata: f.metadata ?? {},
|
|
369
|
-
variants: f.variants ?? []
|
|
370
|
-
}));
|
|
371
|
-
if (mode === 'more') {
|
|
372
|
-
// append
|
|
373
|
-
useFileStore.getState().setFiles(assets, {
|
|
374
|
-
merge: true
|
|
375
|
-
});
|
|
376
|
-
setPaging(p => ({
|
|
377
|
-
...p,
|
|
378
|
-
offset: effectiveOffset,
|
|
379
|
-
total: response.total || effectiveOffset + assets.length,
|
|
380
|
-
hasMore: response.hasMore,
|
|
381
|
-
loadingMore: false
|
|
382
|
-
}));
|
|
383
|
-
} else {
|
|
384
|
-
useFileStore.getState().setFiles(assets, {
|
|
385
|
-
merge: false
|
|
386
|
-
});
|
|
387
|
-
setPaging(p => ({
|
|
388
|
-
...p,
|
|
389
|
-
offset: 0,
|
|
390
|
-
total: response.total || assets.length,
|
|
391
|
-
hasMore: response.hasMore,
|
|
392
|
-
loadingMore: false
|
|
393
|
-
}));
|
|
394
|
-
}
|
|
395
|
-
} catch (error) {
|
|
396
|
-
toast.error(getErrorMessage(error) || t('fileManagement.toasts.loadFailed'));
|
|
397
|
-
} finally {
|
|
398
|
-
setLoading(false);
|
|
399
|
-
setRefreshing(false);
|
|
400
|
-
setPaging(p => ({
|
|
401
|
-
...p,
|
|
402
|
-
loadingMore: false
|
|
403
|
-
}));
|
|
404
|
-
}
|
|
405
|
-
}, [targetUserId, oxyServices, paging]);
|
|
406
367
|
|
|
407
368
|
// Self-contained document-picking + upload-preview state and handlers.
|
|
369
|
+
// Optimistic uploads edit the file query cache directly (see
|
|
370
|
+
// useFileQueries), so no loader is threaded in.
|
|
408
371
|
const {
|
|
409
372
|
isPickingDocument,
|
|
410
373
|
pendingFiles,
|
|
411
374
|
showUploadPreview,
|
|
375
|
+
uploading,
|
|
376
|
+
uploadProgress,
|
|
412
377
|
handleFileUpload,
|
|
413
378
|
handleConfirmUpload,
|
|
414
379
|
handleCancelUpload,
|
|
@@ -425,105 +390,8 @@ const FileManagementScreen = ({
|
|
|
425
390
|
onClose,
|
|
426
391
|
selectedIds,
|
|
427
392
|
setSelectedIds,
|
|
428
|
-
loadFiles,
|
|
429
393
|
t
|
|
430
394
|
});
|
|
431
|
-
|
|
432
|
-
// Keep a ref to avoid stale closure when calculating next offset
|
|
433
|
-
const prevPagingRef = useRef(paging);
|
|
434
|
-
useEffect(() => {
|
|
435
|
-
prevPagingRef.current = paging;
|
|
436
|
-
}, [paging]);
|
|
437
|
-
|
|
438
|
-
// (removed effect; filteredFiles is memoized)
|
|
439
|
-
|
|
440
|
-
// Load photo dimensions for justified grid - unified approach using Image.getSize
|
|
441
|
-
const loadPhotoDimensions = useCallback(async photos => {
|
|
442
|
-
if (photos.length === 0) return;
|
|
443
|
-
setLoadingDimensions(true);
|
|
444
|
-
|
|
445
|
-
// Snapshot ids missing from the cache at kick-off; concurrent runs may
|
|
446
|
-
// overlap but each merge is applied via a functional updater.
|
|
447
|
-
const photosToLoad = photos.filter(photo => !photoDimensionsRef.current[photo.id]);
|
|
448
|
-
if (photosToLoad.length === 0) {
|
|
449
|
-
setLoadingDimensions(false);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
try {
|
|
453
|
-
const measured = {};
|
|
454
|
-
await Promise.all(photosToLoad.map(async photo => {
|
|
455
|
-
try {
|
|
456
|
-
const downloadUrl = thumbSourceFor(photo);
|
|
457
|
-
// URL not resolved yet — skip (do NOT record fallback
|
|
458
|
-
// dims) so this photo is re-measured once its private-safe
|
|
459
|
-
// URL resolves. Measuring the broken public URL is exactly
|
|
460
|
-
// the bug we are fixing.
|
|
461
|
-
if (!downloadUrl) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Unified approach using Image.getSize (works on all platforms)
|
|
466
|
-
await new Promise(resolve => {
|
|
467
|
-
Image.getSize(downloadUrl, (width, height) => {
|
|
468
|
-
measured[photo.id] = {
|
|
469
|
-
width,
|
|
470
|
-
height
|
|
471
|
-
};
|
|
472
|
-
resolve();
|
|
473
|
-
}, () => {
|
|
474
|
-
// Fallback dimensions
|
|
475
|
-
measured[photo.id] = {
|
|
476
|
-
width: 1,
|
|
477
|
-
height: 1
|
|
478
|
-
};
|
|
479
|
-
resolve();
|
|
480
|
-
});
|
|
481
|
-
});
|
|
482
|
-
} catch (error) {
|
|
483
|
-
// Fallback dimensions for any errors
|
|
484
|
-
measured[photo.id] = {
|
|
485
|
-
width: 1,
|
|
486
|
-
height: 1
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
}));
|
|
490
|
-
if (Object.keys(measured).length > 0) {
|
|
491
|
-
setPhotoDimensions(prev => ({
|
|
492
|
-
...prev,
|
|
493
|
-
...measured
|
|
494
|
-
}));
|
|
495
|
-
}
|
|
496
|
-
} catch (error) {
|
|
497
|
-
// Photo dimensions loading failed, continue without dimensions
|
|
498
|
-
} finally {
|
|
499
|
-
setLoadingDimensions(false);
|
|
500
|
-
}
|
|
501
|
-
}, [thumbSourceFor]);
|
|
502
|
-
|
|
503
|
-
// Re-measure photo dimensions when their private-safe URLs resolve. The
|
|
504
|
-
// justified grid's own trigger fires on the photo SET, not on URL
|
|
505
|
-
// availability, so newly-resolved private tiles would otherwise keep their
|
|
506
|
-
// skipped (unmeasured) state and render with fallback geometry.
|
|
507
|
-
useEffect(() => {
|
|
508
|
-
if (viewMode !== 'photos') return;
|
|
509
|
-
const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
|
|
510
|
-
if (photos.length > 0) {
|
|
511
|
-
loadPhotoDimensions(photos);
|
|
512
|
-
}
|
|
513
|
-
}, [resolvedThumbUrls, viewMode, filteredFiles, loadPhotoDimensions]);
|
|
514
|
-
|
|
515
|
-
// Create justified rows from photos with responsive algorithm
|
|
516
|
-
const createJustifiedRows = useCallback((photos, containerWidth) => {
|
|
517
|
-
if (photos.length === 0) return [];
|
|
518
|
-
const rows = [];
|
|
519
|
-
const photosPerRow = 3; // Fixed 3 photos per row for consistency
|
|
520
|
-
|
|
521
|
-
for (let i = 0; i < photos.length; i += photosPerRow) {
|
|
522
|
-
const rowPhotos = photos.slice(i, i + photosPerRow);
|
|
523
|
-
rows.push(rowPhotos);
|
|
524
|
-
}
|
|
525
|
-
return rows;
|
|
526
|
-
}, []);
|
|
527
395
|
const confirmFileDelete = useCallback(async (fileId, filename) => {
|
|
528
396
|
const confirmed = await surfaces.confirm({
|
|
529
397
|
title: t('fileManagement.deleteFile') || 'Delete File',
|
|
@@ -536,30 +404,28 @@ const FileManagementScreen = ({
|
|
|
536
404
|
});
|
|
537
405
|
if (!confirmed) return;
|
|
538
406
|
try {
|
|
539
|
-
|
|
407
|
+
setDeletingId(fileId);
|
|
540
408
|
await oxyServices.deleteFile(fileId);
|
|
541
409
|
toast.success(t('fileManagement.toasts.deleteSuccess'));
|
|
542
410
|
|
|
543
|
-
//
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
// Silent background reconcile
|
|
547
|
-
setTimeout(() => loadFiles('silent'), 800);
|
|
411
|
+
// Remove from the query cache, then reconcile against the server.
|
|
412
|
+
removeFileFromCache(queryClient, targetUserId, fileId);
|
|
413
|
+
invalidateFiles();
|
|
548
414
|
} catch (error) {
|
|
549
415
|
// Provide specific error messages
|
|
550
416
|
if (getErrorMessage(error)?.includes('File not found') || getErrorMessage(error)?.includes('404')) {
|
|
551
417
|
toast.error(t('fileManagement.toasts.fileNotFound'));
|
|
552
|
-
//
|
|
553
|
-
|
|
418
|
+
// It's already gone server-side — reconcile the list.
|
|
419
|
+
invalidateFiles();
|
|
554
420
|
} else if (getErrorMessage(error)?.includes('permission') || getErrorMessage(error)?.includes('403')) {
|
|
555
421
|
toast.error(t('fileManagement.toasts.noPermission'));
|
|
556
422
|
} else {
|
|
557
423
|
toast.error(getErrorMessage(error) || t('fileManagement.toasts.deleteFailed'));
|
|
558
424
|
}
|
|
559
425
|
} finally {
|
|
560
|
-
|
|
426
|
+
setDeletingId(null);
|
|
561
427
|
}
|
|
562
|
-
}, [
|
|
428
|
+
}, [oxyServices, queryClient, targetUserId, invalidateFiles, t]);
|
|
563
429
|
const confirmBulkDelete = useCallback(async () => {
|
|
564
430
|
if (selectedIds.size === 0) return;
|
|
565
431
|
const confirmed = await surfaces.confirm({
|
|
@@ -576,7 +442,7 @@ const FileManagementScreen = ({
|
|
|
576
442
|
const deletePromises = Array.from(selectedIds).map(async fileId => {
|
|
577
443
|
try {
|
|
578
444
|
await oxyServices.deleteFile(fileId);
|
|
579
|
-
|
|
445
|
+
removeFileFromCache(queryClient, targetUserId, fileId);
|
|
580
446
|
return {
|
|
581
447
|
success: true,
|
|
582
448
|
fileId
|
|
@@ -603,11 +469,11 @@ const FileManagementScreen = ({
|
|
|
603
469
|
}));
|
|
604
470
|
}
|
|
605
471
|
setSelectedIds(new Set());
|
|
606
|
-
|
|
472
|
+
invalidateFiles();
|
|
607
473
|
} catch (error) {
|
|
608
474
|
toast.error(getErrorMessage(error) || t('fileManagement.toasts.bulkDeleteError'));
|
|
609
475
|
}
|
|
610
|
-
}, [selectedIds, oxyServices,
|
|
476
|
+
}, [selectedIds, oxyServices, queryClient, targetUserId, invalidateFiles, t]);
|
|
611
477
|
const handleBulkVisibilityChange = useCallback(async visibility => {
|
|
612
478
|
if (selectedIds.size === 0) return;
|
|
613
479
|
try {
|
|
@@ -634,26 +500,23 @@ const FileManagementScreen = ({
|
|
|
634
500
|
count: successful,
|
|
635
501
|
visibility
|
|
636
502
|
}));
|
|
637
|
-
//
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
...files.find(f => f.id === fileId)?.metadata,
|
|
642
|
-
visibility
|
|
643
|
-
}
|
|
503
|
+
// Reflect the new visibility in the cached files.
|
|
504
|
+
for (const fileId of selectedIds) {
|
|
505
|
+
patchFileMetadataInCache(queryClient, targetUserId, fileId, {
|
|
506
|
+
visibility
|
|
644
507
|
});
|
|
645
|
-
}
|
|
508
|
+
}
|
|
646
509
|
}
|
|
647
510
|
if (failed > 0) {
|
|
648
511
|
toast.error(t('fileManagement.toasts.visibilityFailed', {
|
|
649
512
|
count: failed
|
|
650
513
|
}));
|
|
651
514
|
}
|
|
652
|
-
|
|
515
|
+
invalidateFiles();
|
|
653
516
|
} catch (error) {
|
|
654
517
|
toast.error(getErrorMessage(error) || t('fileManagement.toasts.visibilityError'));
|
|
655
518
|
}
|
|
656
|
-
}, [selectedIds, oxyServices,
|
|
519
|
+
}, [selectedIds, oxyServices, queryClient, targetUserId, invalidateFiles, t]);
|
|
657
520
|
const handleVisibilityChange = useCallback(async () => {
|
|
658
521
|
if (selectedIds.size === 0) return;
|
|
659
522
|
const visibility = await presentActionSheet({
|
|
@@ -677,7 +540,7 @@ const FileManagementScreen = ({
|
|
|
677
540
|
}, [selectedIds.size, handleBulkVisibilityChange, t]);
|
|
678
541
|
|
|
679
542
|
// Unified download function - works on all platforms
|
|
680
|
-
const handleFileDownload = async (fileId, filename) => {
|
|
543
|
+
const handleFileDownload = useCallback(async (fileId, filename) => {
|
|
681
544
|
try {
|
|
682
545
|
// Resolve an authenticated, private-safe URL. The synchronous
|
|
683
546
|
// `getFileDownloadUrl` yields the public CDN origin, which 404s for
|
|
@@ -720,8 +583,8 @@ const FileManagementScreen = ({
|
|
|
720
583
|
} catch (error) {
|
|
721
584
|
toast.error(getErrorMessage(error) || t('fileManagement.toasts.downloadFailed'));
|
|
722
585
|
}
|
|
723
|
-
};
|
|
724
|
-
const handleFileOpen = async file => {
|
|
586
|
+
}, [oxyServices, t]);
|
|
587
|
+
const handleFileOpen = useCallback(async file => {
|
|
725
588
|
if (selectMode) {
|
|
726
589
|
toggleSelect(file);
|
|
727
590
|
return;
|
|
@@ -761,21 +624,21 @@ const FileManagementScreen = ({
|
|
|
761
624
|
} finally {
|
|
762
625
|
setLoadingFileContent(false);
|
|
763
626
|
}
|
|
764
|
-
};
|
|
765
|
-
const handleCloseFile = () => {
|
|
627
|
+
}, [selectMode, toggleSelect, oxyServices, t]);
|
|
628
|
+
const handleCloseFile = useCallback(() => {
|
|
766
629
|
setOpenedFile(null);
|
|
767
630
|
setFileContent(null);
|
|
768
631
|
setShowFileDetailsInViewer(false);
|
|
769
632
|
// Don't reset view mode when closing a file
|
|
770
|
-
};
|
|
771
|
-
const showFileDetailsModal = file => {
|
|
633
|
+
}, []);
|
|
634
|
+
const showFileDetailsModal = useCallback(file => {
|
|
772
635
|
presentFileDetails({
|
|
773
636
|
file,
|
|
774
637
|
onDownload: handleFileDownload,
|
|
775
638
|
onDelete: confirmFileDelete,
|
|
776
639
|
isOwner: user?.id === targetUserId
|
|
777
640
|
});
|
|
778
|
-
};
|
|
641
|
+
}, [handleFileDownload, confirmFileDelete, user?.id, targetUserId]);
|
|
779
642
|
const renderJustifiedPhotoItem = useCallback((photo, width, height, isLast) => {
|
|
780
643
|
const downloadUrl = thumbSourceFor(photo);
|
|
781
644
|
return /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
@@ -820,18 +683,7 @@ const FileManagementScreen = ({
|
|
|
820
683
|
})]
|
|
821
684
|
})
|
|
822
685
|
}, photo.id);
|
|
823
|
-
}, [thumbSourceFor, selectMode, selectedIds, multiSelect, colors.primary, colors.text]);
|
|
824
|
-
|
|
825
|
-
// Run initial load once per targetUserId change to avoid accidental loops
|
|
826
|
-
const lastLoadedFor = useRef(undefined);
|
|
827
|
-
useEffect(() => {
|
|
828
|
-
const key = targetUserId || 'anonymous';
|
|
829
|
-
if (lastLoadedFor.current !== key) {
|
|
830
|
-
lastLoadedFor.current = key;
|
|
831
|
-
loadFiles('initial');
|
|
832
|
-
}
|
|
833
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
834
|
-
}, [targetUserId]);
|
|
686
|
+
}, [thumbSourceFor, selectMode, selectedIds, multiSelect, colors.primary, colors.text, handleFileOpen]);
|
|
835
687
|
|
|
836
688
|
// SettingsListItem-based file items (for 'all' view)
|
|
837
689
|
const groupedFileItems = useMemo(() => {
|
|
@@ -982,8 +834,8 @@ const FileManagementScreen = ({
|
|
|
982
834
|
backgroundColor: colors.negativeSubtle
|
|
983
835
|
},
|
|
984
836
|
onPress: () => confirmFileDelete(file.id, file.filename),
|
|
985
|
-
disabled:
|
|
986
|
-
children:
|
|
837
|
+
disabled: deletingId === file.id,
|
|
838
|
+
children: deletingId === file.id ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
987
839
|
size: "small",
|
|
988
840
|
color: colors.error
|
|
989
841
|
}) : /*#__PURE__*/_jsx(Ionicons, {
|
|
@@ -995,85 +847,27 @@ const FileManagementScreen = ({
|
|
|
995
847
|
}) : undefined
|
|
996
848
|
};
|
|
997
849
|
});
|
|
998
|
-
}, [filteredFiles,
|
|
999
|
-
|
|
1000
|
-
// Scroll to selected file after selection
|
|
1001
|
-
useEffect(() => {
|
|
1002
|
-
if (lastSelectedFileId && selectMode) {
|
|
1003
|
-
if (viewMode === 'all' && scrollViewRef.current) {
|
|
1004
|
-
// Find the index of the selected file (filteredFiles is already sorted)
|
|
1005
|
-
const itemIndex = filteredFiles.findIndex(file => file.id === lastSelectedFileId);
|
|
1006
|
-
if (itemIndex >= 0) {
|
|
1007
|
-
// Estimate item height (GroupedItem with dense mode is approximately 60-70px)
|
|
1008
|
-
// Account for description rows which add extra height
|
|
1009
|
-
const baseItemHeight = 65;
|
|
1010
|
-
const descriptionHeight = 30; // Approximate height for description
|
|
1011
|
-
// Use filteredFiles which is already sorted according to user's selection
|
|
1012
|
-
const sortedFiles = filteredFiles;
|
|
1013
|
-
|
|
1014
|
-
// Calculate total height up to this item
|
|
1015
|
-
let scrollPosition = 0;
|
|
1016
|
-
for (let i = 0; i <= itemIndex && i < sortedFiles.length; i++) {
|
|
1017
|
-
const file = sortedFiles[i];
|
|
1018
|
-
scrollPosition += baseItemHeight;
|
|
1019
|
-
if (file.metadata?.description) {
|
|
1020
|
-
scrollPosition += descriptionHeight;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// Add header, controls, search, and stats height (approximately 250px)
|
|
1025
|
-
const headerHeight = 250;
|
|
1026
|
-
const finalScrollPosition = headerHeight + scrollPosition - 150; // Offset to show item near top
|
|
1027
|
-
|
|
1028
|
-
// Use requestAnimationFrame to ensure DOM is updated before scrolling
|
|
1029
|
-
requestAnimationFrame(() => {
|
|
1030
|
-
requestAnimationFrame(() => {
|
|
1031
|
-
scrollViewRef.current?.scrollTo({
|
|
1032
|
-
y: Math.max(0, finalScrollPosition),
|
|
1033
|
-
animated: true
|
|
1034
|
-
});
|
|
1035
|
-
});
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
} else if (viewMode === 'photos' && photoScrollViewRef.current) {
|
|
1039
|
-
// For photo grid, find the photo index
|
|
1040
|
-
const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
|
|
1041
|
-
const photoIndex = photos.findIndex(p => p.id === lastSelectedFileId);
|
|
1042
|
-
if (photoIndex >= 0) {
|
|
1043
|
-
// Rough scroll estimate for the justified grid (3 photos/row; variable row heights).
|
|
1044
|
-
const itemsPerRow = 3;
|
|
1045
|
-
const estimatedRowHeight = 150;
|
|
1046
|
-
const rowGap = 4;
|
|
1047
|
-
const row = Math.floor(photoIndex / itemsPerRow);
|
|
1048
|
-
const finalScrollPosition = row * (estimatedRowHeight + rowGap) - 100;
|
|
850
|
+
}, [filteredFiles, colors, deletingId, toggleSelect, handleFileDownload, confirmFileDelete, handleFileOpen, thumbSourceFor, selectMode, selectedIds]);
|
|
1049
851
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
}
|
|
1062
|
-
}, [lastSelectedFileId, selectMode, viewMode, filteredFiles, safeContainerWidth]);
|
|
1063
|
-
|
|
1064
|
-
// Clear selected file ID after scroll animation completes
|
|
1065
|
-
useEffect(() => {
|
|
1066
|
-
if (lastSelectedFileId && scrollViewRef.current) {
|
|
1067
|
-
const timeoutId = setTimeout(() => {
|
|
1068
|
-
setLastSelectedFileId(null);
|
|
1069
|
-
}, 600); // Allow time for scroll animation to complete
|
|
852
|
+
// Browse-themed terminal load-failure state — DISTINCT from the empty state.
|
|
853
|
+
const renderLoadError = () => /*#__PURE__*/_jsx(FileLibraryError, {
|
|
854
|
+
title: t('fileManagement.loadError.title'),
|
|
855
|
+
description: t('fileManagement.loadError.description'),
|
|
856
|
+
retryLabel: t('fileManagement.retry'),
|
|
857
|
+
onRetry: () => filesQuery.refetch(),
|
|
858
|
+
iconColor: colors.error,
|
|
859
|
+
titleColor: colors.text,
|
|
860
|
+
descriptionColor: colors.textSecondary,
|
|
861
|
+
buttonColor: colors.primary
|
|
862
|
+
});
|
|
1070
863
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
const renderPhotoGrid =
|
|
864
|
+
// Browse "photos" view. The justified grid owns its own dimension
|
|
865
|
+
// measurement + reflow (see JustifiedPhotoGrid); this only supplies the
|
|
866
|
+
// photo set, the private-safe URL resolver, and the tile renderer.
|
|
867
|
+
const renderPhotoGrid = () => {
|
|
1075
868
|
const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
|
|
1076
869
|
if (photos.length === 0) {
|
|
870
|
+
if (hasLoadError) return renderLoadError();
|
|
1077
871
|
return /*#__PURE__*/_jsxs(View, {
|
|
1078
872
|
className: "items-center py-[40px] px-[24px]",
|
|
1079
873
|
children: [/*#__PURE__*/_jsx(Ionicons, {
|
|
@@ -1099,10 +893,7 @@ const FileManagementScreen = ({
|
|
|
1099
893
|
},
|
|
1100
894
|
onPress: handleFileUpload,
|
|
1101
895
|
disabled: uploading || isPickingDocument,
|
|
1102
|
-
children: uploading ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
1103
|
-
size: "small",
|
|
1104
|
-
color: "#FFFFFF"
|
|
1105
|
-
}) : isPickingDocument ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
896
|
+
children: uploading || isPickingDocument ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
1106
897
|
size: "small",
|
|
1107
898
|
color: "#FFFFFF"
|
|
1108
899
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
@@ -1118,13 +909,13 @@ const FileManagementScreen = ({
|
|
|
1118
909
|
})]
|
|
1119
910
|
});
|
|
1120
911
|
}
|
|
1121
|
-
return /*#__PURE__*/
|
|
912
|
+
return /*#__PURE__*/_jsx(ScrollView, {
|
|
1122
913
|
ref: photoScrollViewRef,
|
|
1123
914
|
className: "flex-1",
|
|
1124
915
|
contentContainerClassName: "p-[10px]",
|
|
1125
916
|
refreshControl: /*#__PURE__*/_jsx(RefreshControl, {
|
|
1126
|
-
refreshing:
|
|
1127
|
-
onRefresh: () =>
|
|
917
|
+
refreshing: isRefreshingFiles,
|
|
918
|
+
onRefresh: () => filesQuery.refetch(),
|
|
1128
919
|
tintColor: colors.primary
|
|
1129
920
|
}),
|
|
1130
921
|
showsVerticalScrollIndicator: false,
|
|
@@ -1137,38 +928,21 @@ const FileManagementScreen = ({
|
|
|
1137
928
|
contentSize
|
|
1138
929
|
} = nativeEvent;
|
|
1139
930
|
const distanceFromBottom = contentSize.height - (contentOffset.y + layoutMeasurement.height);
|
|
1140
|
-
if (distanceFromBottom < 200 && !
|
|
1141
|
-
|
|
931
|
+
if (distanceFromBottom < 200 && !isFetchingMore && hasMoreFiles) {
|
|
932
|
+
filesQuery.fetchNextPage();
|
|
1142
933
|
}
|
|
1143
934
|
},
|
|
1144
935
|
scrollEventThrottle: 250,
|
|
1145
|
-
children:
|
|
1146
|
-
className: "flex-row items-center justify-center py-[16px] gap-[8px]",
|
|
1147
|
-
children: [/*#__PURE__*/_jsx(ActivityIndicator, {
|
|
1148
|
-
size: "small",
|
|
1149
|
-
color: colors.primary
|
|
1150
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
1151
|
-
className: "text-[14px] italic",
|
|
1152
|
-
style: {
|
|
1153
|
-
color: colors.textSecondary
|
|
1154
|
-
},
|
|
1155
|
-
children: t('fileManagement.loadingPhotoLayout')
|
|
1156
|
-
})]
|
|
1157
|
-
}), /*#__PURE__*/_jsx(JustifiedPhotoGrid, {
|
|
936
|
+
children: /*#__PURE__*/_jsx(JustifiedPhotoGrid, {
|
|
1158
937
|
photos: photos,
|
|
1159
|
-
|
|
1160
|
-
loadPhotoDimensions: loadPhotoDimensions,
|
|
1161
|
-
createJustifiedRows: createJustifiedRows,
|
|
938
|
+
getThumbUrl: thumbSourceFor,
|
|
1162
939
|
renderJustifiedPhotoItem: renderJustifiedPhotoItem,
|
|
1163
940
|
textColor: colors.text,
|
|
1164
941
|
containerWidth: typeof containerWidth === 'number' && containerWidth > 0 ? containerWidth : undefined
|
|
1165
|
-
})
|
|
942
|
+
})
|
|
1166
943
|
});
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// Inline justified grid removed (moved to components/photogrid/JustifiedPhotoGrid.tsx)
|
|
1170
|
-
|
|
1171
|
-
const renderEmptyState = () => /*#__PURE__*/_jsxs(View, {
|
|
944
|
+
};
|
|
945
|
+
const renderEmptyState = () => hasLoadError ? renderLoadError() : /*#__PURE__*/_jsxs(View, {
|
|
1172
946
|
className: "items-center py-[40px] px-[24px]",
|
|
1173
947
|
children: [/*#__PURE__*/_jsx(Ionicons, {
|
|
1174
948
|
name: "folder-open-outline",
|
|
@@ -1193,10 +967,7 @@ const FileManagementScreen = ({
|
|
|
1193
967
|
},
|
|
1194
968
|
onPress: handleFileUpload,
|
|
1195
969
|
disabled: uploading || isPickingDocument,
|
|
1196
|
-
children: uploading ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
1197
|
-
size: "small",
|
|
1198
|
-
color: "#FFFFFF"
|
|
1199
|
-
}) : isPickingDocument ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
970
|
+
children: uploading || isPickingDocument ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
1200
971
|
size: "small",
|
|
1201
972
|
color: "#FFFFFF"
|
|
1202
973
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
@@ -1300,7 +1071,7 @@ const FileManagementScreen = ({
|
|
|
1300
1071
|
right: fmHeaderRight,
|
|
1301
1072
|
largeTitle: false
|
|
1302
1073
|
});
|
|
1303
|
-
if (
|
|
1074
|
+
if (isLoadingFiles && !isImageOnlyPicker) {
|
|
1304
1075
|
const GRID_PADDING = 10;
|
|
1305
1076
|
const TILE_GAP = 4;
|
|
1306
1077
|
const GRID_COLUMNS = 3;
|
|
@@ -1399,13 +1170,15 @@ const FileManagementScreen = ({
|
|
|
1399
1170
|
multiSelect: multiSelect,
|
|
1400
1171
|
maxSelection: maxSelection,
|
|
1401
1172
|
allowUpload: allowUpload,
|
|
1402
|
-
refreshing:
|
|
1173
|
+
refreshing: isRefreshingFiles,
|
|
1403
1174
|
uploading: uploading,
|
|
1404
1175
|
isPickingDocument: isPickingDocument,
|
|
1405
1176
|
uploadProgress: uploadProgress,
|
|
1406
|
-
hasMore:
|
|
1407
|
-
loadingMore:
|
|
1408
|
-
loading:
|
|
1177
|
+
hasMore: hasMoreFiles,
|
|
1178
|
+
loadingMore: isFetchingMore,
|
|
1179
|
+
loading: isLoadingFiles,
|
|
1180
|
+
loadError: hasLoadError,
|
|
1181
|
+
onRetry: () => filesQuery.refetch(),
|
|
1409
1182
|
reduceMotion: reduceMotion,
|
|
1410
1183
|
getThumbUrl: thumbSourceFor,
|
|
1411
1184
|
primaryColor: colors.primary,
|
|
@@ -1415,8 +1188,8 @@ const FileManagementScreen = ({
|
|
|
1415
1188
|
,
|
|
1416
1189
|
onPreviewPhoto: file => showFileDetailsModal(file),
|
|
1417
1190
|
onUpload: handleFileUpload,
|
|
1418
|
-
onRefresh: () =>
|
|
1419
|
-
onLoadMore: () =>
|
|
1191
|
+
onRefresh: () => filesQuery.refetch(),
|
|
1192
|
+
onLoadMore: () => filesQuery.fetchNextPage(),
|
|
1420
1193
|
onCancel: () => {
|
|
1421
1194
|
if (onSelect || onConfirmSelection) {
|
|
1422
1195
|
// Legacy callback caller: preserve close/back behaviour.
|
|
@@ -1491,32 +1264,30 @@ const FileManagementScreen = ({
|
|
|
1491
1264
|
textColor: colors.text,
|
|
1492
1265
|
style: screenStyles.viewModeButton,
|
|
1493
1266
|
accessibilityLabel: t('fileManagement.a11y.viewPhotos') || 'Show photos only'
|
|
1494
|
-
}),
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
accessibilityLabel: t('fileManagement.a11y.viewAudio') || 'Show audio only'
|
|
1519
|
-
})]
|
|
1267
|
+
}), /*#__PURE__*/_jsx(AnimatedButton, {
|
|
1268
|
+
isSelected: viewMode === 'videos',
|
|
1269
|
+
onPress: () => setViewMode('videos'),
|
|
1270
|
+
icon: viewMode === 'videos' ? 'video' : 'video-outline',
|
|
1271
|
+
primaryColor: colors.primary,
|
|
1272
|
+
textColor: colors.text,
|
|
1273
|
+
style: screenStyles.viewModeButton,
|
|
1274
|
+
accessibilityLabel: t('fileManagement.a11y.viewVideos') || 'Show videos only'
|
|
1275
|
+
}), /*#__PURE__*/_jsx(AnimatedButton, {
|
|
1276
|
+
isSelected: viewMode === 'documents',
|
|
1277
|
+
onPress: () => setViewMode('documents'),
|
|
1278
|
+
icon: viewMode === 'documents' ? 'file-document' : 'file-document-outline',
|
|
1279
|
+
primaryColor: colors.primary,
|
|
1280
|
+
textColor: colors.text,
|
|
1281
|
+
style: screenStyles.viewModeButton,
|
|
1282
|
+
accessibilityLabel: t('fileManagement.a11y.viewDocuments') || 'Show documents only'
|
|
1283
|
+
}), /*#__PURE__*/_jsx(AnimatedButton, {
|
|
1284
|
+
isSelected: viewMode === 'audio',
|
|
1285
|
+
onPress: () => setViewMode('audio'),
|
|
1286
|
+
icon: viewMode === 'audio' ? 'music-note' : 'music-note-outline',
|
|
1287
|
+
primaryColor: colors.primary,
|
|
1288
|
+
textColor: colors.text,
|
|
1289
|
+
style: screenStyles.viewModeButton,
|
|
1290
|
+
accessibilityLabel: t('fileManagement.a11y.viewAudio') || 'Show audio only'
|
|
1520
1291
|
})]
|
|
1521
1292
|
})
|
|
1522
1293
|
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
@@ -1549,15 +1320,15 @@ const FileManagementScreen = ({
|
|
|
1549
1320
|
size: 14,
|
|
1550
1321
|
color: colors.textSecondary
|
|
1551
1322
|
})]
|
|
1552
|
-
}), user?.id === targetUserId && (!selectMode ||
|
|
1553
|
-
className:
|
|
1323
|
+
}), user?.id === targetUserId && (!selectMode || allowUploadInSelectMode) && /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
1324
|
+
className: "h-[44px] w-[44px] rounded-[22px] items-center justify-center",
|
|
1554
1325
|
style: {
|
|
1555
1326
|
backgroundColor: colors.primary
|
|
1556
1327
|
},
|
|
1557
1328
|
onPress: handleFileUpload,
|
|
1558
1329
|
disabled: uploading || isPickingDocument,
|
|
1559
1330
|
accessibilityRole: "button",
|
|
1560
|
-
accessibilityLabel:
|
|
1331
|
+
accessibilityLabel: t('fileManagement.a11y.uploadFile') || 'Upload file',
|
|
1561
1332
|
accessibilityState: {
|
|
1562
1333
|
busy: uploading || isPickingDocument
|
|
1563
1334
|
},
|
|
@@ -1573,17 +1344,6 @@ const FileManagementScreen = ({
|
|
|
1573
1344
|
}) : isPickingDocument ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
1574
1345
|
size: "small",
|
|
1575
1346
|
color: "#FFFFFF"
|
|
1576
|
-
}) : isImageOnlyPicker ? /*#__PURE__*/_jsxs(View, {
|
|
1577
|
-
className: "flex-row items-center gap-[6px]",
|
|
1578
|
-
children: [/*#__PURE__*/_jsx(Ionicons, {
|
|
1579
|
-
name: "cloud-upload",
|
|
1580
|
-
size: 18,
|
|
1581
|
-
color: "#FFFFFF"
|
|
1582
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
1583
|
-
className: "text-white text-[14px] font-semibold",
|
|
1584
|
-
numberOfLines: 1,
|
|
1585
|
-
children: t('fileManagement.upload') || 'Upload'
|
|
1586
|
-
})]
|
|
1587
1347
|
}) : /*#__PURE__*/_jsx(Ionicons, {
|
|
1588
1348
|
name: "add",
|
|
1589
1349
|
size: 22,
|
|
@@ -1679,12 +1439,15 @@ const FileManagementScreen = ({
|
|
|
1679
1439
|
filteredFiles: filteredFiles,
|
|
1680
1440
|
searchQuery: searchQuery,
|
|
1681
1441
|
items: groupedFileItems,
|
|
1682
|
-
paging:
|
|
1683
|
-
|
|
1442
|
+
paging: {
|
|
1443
|
+
loadingMore: isFetchingMore,
|
|
1444
|
+
hasMore: hasMoreFiles
|
|
1445
|
+
},
|
|
1446
|
+
refreshing: isRefreshingFiles,
|
|
1684
1447
|
colors: colors,
|
|
1685
1448
|
t: t,
|
|
1686
|
-
onRefresh: () =>
|
|
1687
|
-
onLoadMore: () =>
|
|
1449
|
+
onRefresh: () => filesQuery.refetch(),
|
|
1450
|
+
onLoadMore: () => filesQuery.fetchNextPage(),
|
|
1688
1451
|
onClearSearch: () => setSearchQuery(''),
|
|
1689
1452
|
renderEmptyState: renderEmptyState
|
|
1690
1453
|
}), !selectMode && uploading && /*#__PURE__*/_jsx(UploadBar, {
|