UNPKG

@oxyhq/services

Version:

OxyHQ Expo/React Native SDK — UI components, screens, and native features

358 lines (333 loc) 11.5 kB
"use strict"; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { authenticatedApiCall } from '@oxyhq/core'; import { queryKeys, invalidateAccountQueries, invalidateUserQueries } from "../queries/queryKeys.js"; import { useOxy } from "../../context/OxyContext.js"; import { toast } from '../../../lib/sonner'; import { refreshAvatarInStore } from "../../utils/avatarUtils.js"; import { useAuthStore } from "../../stores/authStore.js"; /** * Update user profile with optimistic updates and offline queue support */ export const useUpdateProfile = () => { const { oxyServices, activeSessionId, user } = useOxy(); const queryClient = useQueryClient(); return useMutation({ mutationFn: async updates => { return authenticatedApiCall(oxyServices, activeSessionId, () => oxyServices.updateProfile(updates)); }, // Optimistic update onMutate: async updates => { // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: queryKeys.accounts.current() }); // Snapshot previous value const previousUser = queryClient.getQueryData(queryKeys.accounts.current()); // Optimistically update if (previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), { ...previousUser, ...updates }); // Also update profile query if sessionId is available if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), { ...previousUser, ...updates }); } } return { previousUser }; }, // On error, rollback onError: (error, updates, context) => { if (context?.previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), context.previousUser); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), context.previousUser); } } toast.error(error instanceof Error ? error.message : 'Failed to update profile'); }, // On success, invalidate and refetch onSuccess: (data, updates) => { // Update cache with server response queryClient.setQueryData(queryKeys.accounts.current(), data); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), data); } // Update authStore so frontend components see the changes immediately useAuthStore.getState().setUser(data); // If avatar was updated, refresh accountStore with cache-busted URL if (updates.avatar && activeSessionId && oxyServices) { refreshAvatarInStore(activeSessionId, updates.avatar, oxyServices); } // Invalidate all related queries to refresh everywhere invalidateUserQueries(queryClient); invalidateAccountQueries(queryClient); } }); }; /** * Upload avatar with progress tracking and offline queue support */ export const useUploadAvatar = () => { const { oxyServices, activeSessionId } = useOxy(); const queryClient = useQueryClient(); return useMutation({ mutationFn: async file => { return authenticatedApiCall(oxyServices, activeSessionId, async () => { // Upload file first const uploadResult = await oxyServices.assetUpload(file, 'public'); const fileId = uploadResult?.file?.id || uploadResult?.id || uploadResult; if (!fileId || typeof fileId !== 'string') { throw new Error('Failed to get file ID from upload result'); } // Update profile with file ID return await oxyServices.updateProfile({ avatar: fileId }); }); }, onMutate: async file => { await queryClient.cancelQueries({ queryKey: queryKeys.accounts.current() }); const previousUser = queryClient.getQueryData(queryKeys.accounts.current()); // Optimistically set a temporary avatar (using file URI as placeholder) if (previousUser) { const optimisticUser = { ...previousUser, avatar: file.uri // Temporary, will be replaced with fileId }; queryClient.setQueryData(queryKeys.accounts.current(), optimisticUser); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), optimisticUser); } } return { previousUser }; }, onError: (error, file, context) => { if (context?.previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), context.previousUser); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), context.previousUser); } } toast.error(error instanceof Error ? error.message : 'Failed to upload avatar'); }, onSuccess: data => { queryClient.setQueryData(queryKeys.accounts.current(), data); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), data); } // Update authStore so frontend components see the changes immediately useAuthStore.getState().setUser(data); // Refresh accountStore with cache-busted URL if avatar was updated if (data?.avatar && activeSessionId && oxyServices) { refreshAvatarInStore(activeSessionId, data.avatar, oxyServices); } // Invalidate all related queries to refresh everywhere invalidateUserQueries(queryClient); invalidateAccountQueries(queryClient); toast.success('Avatar updated successfully'); } }); }; /** * Update account settings */ export const useUpdateAccountSettings = () => { const { oxyServices, activeSessionId } = useOxy(); const queryClient = useQueryClient(); return useMutation({ mutationFn: async settings => { return await oxyServices.updateProfile({ privacySettings: settings }); }, onMutate: async settings => { await queryClient.cancelQueries({ queryKey: queryKeys.accounts.settings() }); const previousUser = queryClient.getQueryData(queryKeys.accounts.current()); if (previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), { ...previousUser, privacySettings: { ...previousUser.privacySettings, ...settings } }); } return { previousUser }; }, onError: (error, settings, context) => { if (context?.previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), context.previousUser); } toast.error(error instanceof Error ? error.message : 'Failed to update settings'); }, onSuccess: data => { queryClient.setQueryData(queryKeys.accounts.current(), data); // Update authStore so frontend components see the changes immediately useAuthStore.getState().setUser(data); invalidateAccountQueries(queryClient); toast.success('Settings updated successfully'); }, onSettled: () => { queryClient.invalidateQueries({ queryKey: queryKeys.accounts.settings() }); } }); }; /** * Update privacy settings with optimistic updates and authentication handling */ export const useUpdatePrivacySettings = () => { const { oxyServices, activeSessionId, user } = useOxy(); const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ settings, userId }) => { const targetUserId = userId || user?.id; if (!targetUserId) { throw new Error('User ID is required'); } return authenticatedApiCall(oxyServices, activeSessionId, () => oxyServices.updatePrivacySettings(settings, targetUserId)); }, // Optimistic update onMutate: async ({ settings, userId }) => { const targetUserId = userId || user?.id; if (!targetUserId) return; // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: queryKeys.privacy.settings(targetUserId) }); await queryClient.cancelQueries({ queryKey: queryKeys.accounts.current() }); // Snapshot previous values const previousPrivacySettings = queryClient.getQueryData(queryKeys.privacy.settings(targetUserId)); const previousUser = queryClient.getQueryData(queryKeys.accounts.current()); // Optimistically update privacy settings if (previousPrivacySettings) { queryClient.setQueryData(queryKeys.privacy.settings(targetUserId), { ...previousPrivacySettings, ...settings }); } // Also update user query if available if (previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), { ...previousUser, privacySettings: { ...previousUser.privacySettings, ...settings } }); } return { previousPrivacySettings, previousUser }; }, // On error, rollback onError: (error, { userId }, context) => { const targetUserId = userId || user?.id; if (context?.previousPrivacySettings && targetUserId) { queryClient.setQueryData(queryKeys.privacy.settings(targetUserId), context.previousPrivacySettings); } if (context?.previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), context.previousUser); } toast.error(error instanceof Error ? error.message : 'Failed to update privacy settings'); }, // On success, invalidate and refetch onSuccess: (data, { userId }) => { const targetUserId = userId || user?.id; if (targetUserId) { queryClient.setQueryData(queryKeys.privacy.settings(targetUserId), data); } // Also update account query if it contains privacy settings const currentUser = queryClient.getQueryData(queryKeys.accounts.current()); if (currentUser) { const updatedUser = { ...currentUser, privacySettings: data }; queryClient.setQueryData(queryKeys.accounts.current(), updatedUser); // Update authStore so frontend components see the changes immediately useAuthStore.getState().setUser(updatedUser); } invalidateAccountQueries(queryClient); }, // Always refetch after error or success onSettled: (data, error, { userId }) => { const targetUserId = userId || user?.id; if (targetUserId) { queryClient.invalidateQueries({ queryKey: queryKeys.privacy.settings(targetUserId) }); } queryClient.invalidateQueries({ queryKey: queryKeys.accounts.current() }); } }); }; /** Uploaded file data structure from API */ /** Upload result type that supports both single file and batch responses */ /** * Upload file with authentication handling and progress tracking */ export const useUploadFile = () => { const { oxyServices, activeSessionId } = useOxy(); return useMutation({ mutationFn: async ({ file, visibility, metadata, onProgress }) => { return authenticatedApiCall(oxyServices, activeSessionId, () => oxyServices.assetUpload(file, visibility, metadata, onProgress)); } }); }; //# sourceMappingURL=useAccountMutations.js.map