UNPKG

@oxyhq/services

Version:

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

173 lines (160 loc) 5.11 kB
"use strict"; /** * Mutation Factory - Creates standardized mutations with optimistic updates * * This factory reduces boilerplate code for mutations that follow the common pattern: * 1. Cancel outgoing queries * 2. Snapshot previous data * 3. Apply optimistic update * 4. On error: rollback and show toast * 5. On success: update cache, stores, and invalidate queries */ import { queryKeys, invalidateAccountQueries, invalidateUserQueries } from "../queries/queryKeys.js"; import { toast } from '../../../lib/sonner'; import { useAuthStore } from "../../stores/authStore.js"; /** * Configuration for creating a standard profile mutation */ /** * Creates a standard profile mutation with optimistic updates * * @example * ```ts * const updateProfile = createProfileMutation({ * mutationFn: (updates) => oxyServices.updateProfile(updates), * optimisticUpdate: (user, updates) => updates, * errorMessage: 'Failed to update profile', * }); * ``` */ export function createProfileMutation(config, queryClient, activeSessionId) { const { mutationFn, cancelQueryKeys = [], optimisticUpdate, errorMessage = 'Operation failed', successMessage, updateAuthStore = true, invalidateUserQueries: shouldInvalidateUserQueries = true, invalidateAccountQueries: shouldInvalidateAccountQueries = true, onSuccess: customOnSuccess } = config; return { mutationFn, onMutate: async variables => { // Cancel queries that might conflict await queryClient.cancelQueries({ queryKey: queryKeys.accounts.current() }); for (const key of cancelQueryKeys) { await queryClient.cancelQueries({ queryKey: key }); } // Snapshot previous user data const previousUser = queryClient.getQueryData(queryKeys.accounts.current()); // Apply optimistic update if provided if (previousUser && optimisticUpdate) { const updates = optimisticUpdate(previousUser, variables); const optimisticUser = { ...previousUser, ...updates }; queryClient.setQueryData(queryKeys.accounts.current(), optimisticUser); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), optimisticUser); } } return { previousUser }; }, onError: (error, _variables, context) => { // Rollback optimistic update if (context?.previousUser) { queryClient.setQueryData(queryKeys.accounts.current(), context.previousUser); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), context.previousUser); } } // Show error toast const message = typeof errorMessage === 'function' ? errorMessage(error) : error instanceof Error ? error.message : errorMessage; toast.error(message); }, onSuccess: (data, variables) => { // Update cache with server response queryClient.setQueryData(queryKeys.accounts.current(), data); if (activeSessionId) { queryClient.setQueryData(queryKeys.users.profile(activeSessionId), data); } // Update authStore for immediate UI updates if (updateAuthStore) { useAuthStore.getState().setUser(data); } // Invalidate related queries if (shouldInvalidateUserQueries) { invalidateUserQueries(queryClient); } if (shouldInvalidateAccountQueries) { invalidateAccountQueries(queryClient); } // Show success toast if configured if (successMessage) { toast.success(successMessage); } // Call custom onSuccess handler if (customOnSuccess) { customOnSuccess(data, variables, queryClient); } } }; } /** * Configuration for creating a generic mutation (non-profile) */ /** * Creates a generic mutation with optimistic updates */ export function createGenericMutation(config, queryClient) { const { mutationFn, queryKey, optimisticData, errorMessage = 'Operation failed', successMessage, invalidateQueries = [] } = config; return { mutationFn, onMutate: async variables => { await queryClient.cancelQueries({ queryKey }); const previous = queryClient.getQueryData(queryKey); if (optimisticData) { queryClient.setQueryData(queryKey, optimisticData(previous, variables)); } return { previous }; }, onError: (error, _variables, context) => { if (context?.previous !== undefined) { queryClient.setQueryData(queryKey, context.previous); } toast.error(error instanceof Error ? error.message : errorMessage); }, onSuccess: data => { queryClient.setQueryData(queryKey, data); for (const key of invalidateQueries) { queryClient.invalidateQueries({ queryKey: key }); } if (successMessage) { toast.success(successMessage); } } }; } //# sourceMappingURL=mutationFactory.js.map