@oxyhq/services 5.16.4 → 5.16.8

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 (102) hide show
  1. package/lib/commonjs/core/OxyServices.base.js +3 -1
  2. package/lib/commonjs/core/OxyServices.base.js.map +1 -1
  3. package/lib/commonjs/core/mixins/OxyServices.assets.js +20 -330
  4. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -1
  5. package/lib/commonjs/index.js +156 -0
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/ui/context/OxyContext.js +95 -9
  8. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  9. package/lib/commonjs/ui/hooks/mutations/index.js +60 -20
  10. package/lib/commonjs/ui/hooks/mutations/index.js.map +1 -1
  11. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +230 -1
  12. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  13. package/lib/commonjs/ui/hooks/queries/index.js +96 -30
  14. package/lib/commonjs/ui/hooks/queries/index.js.map +1 -1
  15. package/lib/commonjs/ui/hooks/queries/queryKeys.js +5 -0
  16. package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
  17. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +75 -1
  18. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  19. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +50 -2
  20. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  21. package/lib/commonjs/ui/hooks/useAssets.js +8 -29
  22. package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
  23. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +6 -6
  24. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  25. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +3 -3
  26. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  27. package/lib/commonjs/ui/screens/FileManagementScreen.js +14 -10
  28. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  29. package/lib/commonjs/ui/utils/fileManagement.js +88 -0
  30. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  31. package/lib/module/core/OxyServices.base.js +3 -1
  32. package/lib/module/core/OxyServices.base.js.map +1 -1
  33. package/lib/module/core/mixins/OxyServices.assets.js +20 -331
  34. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -1
  35. package/lib/module/index.js +17 -1
  36. package/lib/module/index.js.map +1 -1
  37. package/lib/module/ui/context/OxyContext.js +95 -9
  38. package/lib/module/ui/context/OxyContext.js.map +1 -1
  39. package/lib/module/ui/hooks/mutations/index.js +12 -3
  40. package/lib/module/ui/hooks/mutations/index.js.map +1 -1
  41. package/lib/module/ui/hooks/mutations/useAccountMutations.js +227 -0
  42. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  43. package/lib/module/ui/hooks/queries/index.js +15 -4
  44. package/lib/module/ui/hooks/queries/index.js.map +1 -1
  45. package/lib/module/ui/hooks/queries/queryKeys.js +5 -0
  46. package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
  47. package/lib/module/ui/hooks/queries/useAccountQueries.js +73 -0
  48. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  49. package/lib/module/ui/hooks/queries/useServicesQueries.js +50 -2
  50. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  51. package/lib/module/ui/hooks/useAssets.js +8 -29
  52. package/lib/module/ui/hooks/useAssets.js.map +1 -1
  53. package/lib/module/ui/screens/AccountOverviewScreen.js +6 -6
  54. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  55. package/lib/module/ui/screens/AccountSettingsScreen.js +3 -3
  56. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  57. package/lib/module/ui/screens/FileManagementScreen.js +12 -10
  58. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  59. package/lib/module/ui/utils/fileManagement.js +87 -0
  60. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  61. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
  62. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +1 -70
  63. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
  64. package/lib/typescript/core/mixins/index.d.ts +4 -14
  65. package/lib/typescript/core/mixins/index.d.ts.map +1 -1
  66. package/lib/typescript/index.d.ts +2 -0
  67. package/lib/typescript/index.d.ts.map +1 -1
  68. package/lib/typescript/ui/context/OxyContext.d.ts +1 -0
  69. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  70. package/lib/typescript/ui/hooks/mutations/index.d.ts +8 -2
  71. package/lib/typescript/ui/hooks/mutations/index.d.ts.map +1 -1
  72. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +19 -0
  73. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  74. package/lib/typescript/ui/hooks/queries/index.d.ts +9 -3
  75. package/lib/typescript/ui/hooks/queries/index.d.ts.map +1 -1
  76. package/lib/typescript/ui/hooks/queries/queryKeys.d.ts +4 -0
  77. package/lib/typescript/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  78. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +6 -0
  79. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  80. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  81. package/lib/typescript/ui/hooks/useAssets.d.ts.map +1 -1
  82. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  83. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  84. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
  85. package/lib/typescript/ui/utils/fileManagement.d.ts +48 -0
  86. package/lib/typescript/ui/utils/fileManagement.d.ts.map +1 -1
  87. package/package.json +6 -2
  88. package/src/core/OxyServices.base.ts +5 -1
  89. package/src/core/mixins/OxyServices.assets.ts +21 -338
  90. package/src/index.ts +49 -2
  91. package/src/ui/context/OxyContext.tsx +98 -7
  92. package/src/ui/hooks/mutations/index.ts +24 -3
  93. package/src/ui/hooks/mutations/useAccountMutations.ts +205 -0
  94. package/src/ui/hooks/queries/index.ts +29 -4
  95. package/src/ui/hooks/queries/queryKeys.ts +6 -0
  96. package/src/ui/hooks/queries/useAccountQueries.ts +69 -0
  97. package/src/ui/hooks/queries/useServicesQueries.ts +49 -2
  98. package/src/ui/hooks/useAssets.ts +8 -28
  99. package/src/ui/screens/AccountOverviewScreen.tsx +4 -3
  100. package/src/ui/screens/AccountSettingsScreen.tsx +3 -5
  101. package/src/ui/screens/FileManagementScreen.tsx +10 -11
  102. package/src/ui/utils/fileManagement.ts +105 -0
@@ -75,12 +75,12 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
75
75
  } = useOxy();
76
76
  const { t } = useI18n();
77
77
  const normalizedTheme = normalizeTheme(theme);
78
-
78
+
79
79
  // Use TanStack Query for user data
80
80
  const { data: user, isLoading: userLoading } = useCurrentUser({ enabled: isAuthenticated });
81
81
  const updateProfileMutation = useUpdateProfile();
82
82
  const uploadAvatarMutation = useUploadAvatar();
83
-
83
+
84
84
  // Fallback to store for backward compatibility
85
85
  const userFromStore = useAuthStore((state) => state.user);
86
86
  const finalUser = user || userFromStore;
@@ -503,9 +503,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
503
503
  });
504
504
  };
505
505
 
506
- const openAvatarPicker = useCallback(() => {
507
- toast.info?.(t('editProfile.toasts.avatarPickerUnavailable') || 'Avatar picker is not available in this build.');
508
- }, [t]);
506
+ const { openAvatarPicker } = useOxy();
509
507
 
510
508
  // Handlers to open modals
511
509
  const handleOpenDisplayNameModal = useCallback(() => setShowEditDisplayNameModal(true), []);
@@ -14,6 +14,7 @@ import {
14
14
  Alert,
15
15
  } from 'react-native';
16
16
  import { Image as ExpoImage } from 'expo-image';
17
+ import * as DocumentPicker from 'expo-document-picker';
17
18
  import type { FileManagementScreenProps } from '../types/fileManagement';
18
19
  import { toast } from '../../lib/sonner';
19
20
  import { Ionicons } from '@expo/vector-icons';
@@ -28,13 +29,13 @@ import { useThemeStyles } from '../hooks/useThemeStyles';
28
29
  import { useColorScheme } from '../hooks/use-color-scheme';
29
30
  import { normalizeTheme } from '../utils/themeUtils';
30
31
  import { useOxy } from '../context/OxyContext';
32
+ import { useUploadFile } from '../hooks/mutations/useAccountMutations';
31
33
  import {
32
34
  confirmAction,
33
35
  convertDocumentPickerAssetToFile,
34
36
  formatFileSize,
35
37
  getFileIcon,
36
38
  getSafeDownloadUrl,
37
- uploadFileRaw
38
39
  } from '../utils/fileManagement';
39
40
  import { FileViewer } from '../components/fileManagement/FileViewer';
40
41
  import { FileDetailsModal } from '../components/fileManagement/FileDetailsModal';
@@ -115,6 +116,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
115
116
  }) => {
116
117
  // Use useOxy() hook for OxyContext values
117
118
  const { user, oxyServices } = useOxy();
119
+ const uploadFileMutation = useUploadFile();
118
120
  const files = useFiles();
119
121
  // Ensure containerWidth is a number (TypeScript guard)
120
122
  const safeContainerWidth: number = typeof containerWidth === 'number' ? containerWidth : 400;
@@ -528,7 +530,11 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
528
530
  };
529
531
  useFileStore.getState().addFile(optimisticFile, { prepend: true });
530
532
 
531
- const result = await uploadFileRaw(raw, targetUserId, oxyServices, defaultVisibility);
533
+ // Use the mutation hook with authentication handling
534
+ const result = await uploadFileMutation.mutateAsync({
535
+ file: raw,
536
+ visibility: defaultVisibility,
537
+ });
532
538
 
533
539
  // Attempt to refresh file list incrementally – fetch single file metadata if API allows
534
540
  if (result?.file || result?.files?.[0]) {
@@ -763,16 +769,9 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
763
769
 
764
770
  try {
765
771
  setIsPickingDocument(true);
766
-
767
- // Dynamically import expo-document-picker (Expo 54 supports it on all platforms)
768
- const DocumentPicker = await import('expo-document-picker').catch(() => null);
769
-
770
- if (!DocumentPicker || !DocumentPicker.getDocumentAsync) {
771
- toast.error('File picker not available. Please install expo-document-picker');
772
- return;
773
- }
774
772
 
775
- // Use getDocumentAsync directly - it will handle platform availability
773
+ // Use expo-document-picker (works on all platforms including web)
774
+ // On web, it uses the native file input and provides File objects directly
776
775
  const result = await DocumentPicker.getDocumentAsync({
777
776
  type: '*/*',
778
777
  multiple: true,
@@ -1,6 +1,8 @@
1
1
  import { Alert } from 'react-native';
2
2
  import type { FileMetadata } from '../../models/interfaces';
3
3
  import { File as ExpoFile } from 'expo-file-system';
4
+ import { toast } from '../../lib/sonner';
5
+ import type { RouteName } from '../navigation/routes';
4
6
 
5
7
  /**
6
8
  * Format file size in bytes to human-readable string
@@ -188,3 +190,106 @@ export async function uploadFileRaw(
188
190
  return await oxyServices.uploadRawFile(file, visibility);
189
191
  }
190
192
 
193
+ /**
194
+ * Configuration for creating an avatar picker handler
195
+ */
196
+ export interface AvatarPickerConfig {
197
+ /** Navigation function from BaseScreenProps */
198
+ navigate?: (screen: RouteName, props?: Record<string, unknown>) => void;
199
+ /** OxyServices instance */
200
+ oxyServices: any;
201
+ /** TanStack Query mutation for updating profile */
202
+ updateProfileMutation: {
203
+ mutateAsync: (updates: { avatar: string }) => Promise<any>;
204
+ };
205
+ /** Callback to update local avatar state */
206
+ onAvatarSelected?: (fileId: string) => void;
207
+ /** i18n translation function */
208
+ t: (key: string) => string | undefined;
209
+ /** Optional context name for logging (e.g., 'AccountSettings', 'WelcomeNewUser') */
210
+ contextName?: string;
211
+ }
212
+
213
+ /**
214
+ * Creates a reusable avatar picker handler function.
215
+ *
216
+ * This function navigates to the FileManagement screen and handles:
217
+ * - Image file validation
218
+ * - File visibility update to public
219
+ * - Profile avatar update via mutation
220
+ * - Success/error toast notifications
221
+ *
222
+ * @example
223
+ * ```tsx
224
+ * const openAvatarPicker = createAvatarPickerHandler({
225
+ * navigate,
226
+ * oxyServices,
227
+ * updateProfileMutation,
228
+ * onAvatarSelected: setAvatarFileId,
229
+ * t,
230
+ * contextName: 'AccountSettings'
231
+ * });
232
+ *
233
+ * <TouchableOpacity onPress={openAvatarPicker}>
234
+ * <Text>Change Avatar</Text>
235
+ * </TouchableOpacity>
236
+ * ```
237
+ */
238
+ export function createAvatarPickerHandler(config: AvatarPickerConfig): () => void {
239
+ const {
240
+ navigate,
241
+ oxyServices,
242
+ updateProfileMutation,
243
+ onAvatarSelected,
244
+ t,
245
+ contextName = 'AvatarPicker'
246
+ } = config;
247
+
248
+ return () => {
249
+ if (!navigate) {
250
+ console.warn(`[${contextName}] navigate function is not available`);
251
+ return;
252
+ }
253
+
254
+ navigate('FileManagement', {
255
+ selectMode: true,
256
+ multiSelect: false,
257
+ disabledMimeTypes: ['video/', 'audio/', 'application/pdf'],
258
+ afterSelect: 'none', // Don't navigate away - stay on current screen
259
+ onSelect: async (file: any) => {
260
+ if (!file.contentType.startsWith('image/')) {
261
+ toast.error(t('editProfile.toasts.selectImage') || 'Please select an image file');
262
+ return;
263
+ }
264
+
265
+ try {
266
+ // Update file visibility to public for avatar (skip if temporary asset ID)
267
+ if (file.id && !file.id.startsWith('temp-')) {
268
+ try {
269
+ await oxyServices.assetUpdateVisibility(file.id, 'public');
270
+ console.log(`[${contextName}] Avatar visibility updated to public`);
271
+ } catch (visError: any) {
272
+ // Only log non-404 errors (404 means asset doesn't exist yet, which is OK)
273
+ if (visError?.response?.status !== 404) {
274
+ console.warn(`[${contextName}] Failed to update avatar visibility, continuing anyway:`, visError);
275
+ }
276
+ }
277
+ }
278
+
279
+ // Update local state if callback provided
280
+ if (onAvatarSelected) {
281
+ onAvatarSelected(file.id);
282
+ }
283
+
284
+ // Update user using TanStack Query mutation
285
+ await updateProfileMutation.mutateAsync({ avatar: file.id });
286
+
287
+ toast.success(t('editProfile.toasts.avatarUpdated') || 'Avatar updated');
288
+ } catch (e: any) {
289
+ toast.error(e.message || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
290
+ }
291
+ }
292
+ });
293
+ };
294
+ }
295
+