import { useMutation, useQueryClient } from '@tanstack/react-query'; import { supabase } from '@/integrations/supabase/client'; import { toast } from 'sonner'; import { getErrorMessage } from '@/lib/errorHandler'; import { useQueryInvalidation } from '@/lib/queryInvalidation'; interface ProfileUpdateParams { userId: string; updates: { display_name?: string; bio?: string; location_id?: string | null; website?: string | null; [key: string]: any; }; } /** * Hook for profile update mutations * * Features: * - Optimistic updates for instant UI feedback * - Automatic rollback on error * - Smart cache invalidation (profile, stats, activity) * - Conditional search invalidation when name changes * - Comprehensive error handling with toast notifications * * Modifies: * - `profiles` table * * Cache Invalidation: * - User profile data (`invalidateUserProfile`) * - Profile stats (`invalidateProfileStats`) * - Profile activity feed (`invalidateProfileActivity`) * - User search results if name changed (`invalidateUserSearch`) * * @example * ```tsx * const mutation = useProfileUpdateMutation(); * * mutation.mutate({ * userId: user.id, * updates: { * display_name: 'New Name', * bio: 'Updated bio', * website: 'https://example.com' * } * }); * ``` */ export function useProfileUpdateMutation() { const queryClient = useQueryClient(); const { invalidateUserProfile, invalidateProfileStats, invalidateProfileActivity, invalidateUserSearch } = useQueryInvalidation(); return useMutation({ mutationFn: async ({ userId, updates }: ProfileUpdateParams) => { const { error } = await supabase .from('profiles') .update(updates) .eq('user_id', userId); if (error) throw error; }, onMutate: async ({ userId, updates }) => { // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: ['profile', userId] }); interface Profile { display_name?: string; bio?: string; location_id?: string; website?: string; } // Snapshot previous value const previousProfile = queryClient.getQueryData(['profile', userId]); // Optimistically update queryClient.setQueryData(['profile', userId], (old) => old ? { ...old, ...updates } : old ); return { previousProfile, userId }; }, onError: (error: unknown, _variables, context) => { // Rollback on error if (context?.previousProfile) { queryClient.setQueryData(['profile', context.userId], context.previousProfile); } toast.error("Update Failed", { description: getErrorMessage(error), }); }, onSuccess: (_data, { userId, updates }) => { // Invalidate all related caches invalidateUserProfile(userId); invalidateProfileStats(userId); invalidateProfileActivity(userId); // If display name or username changed, invalidate user search results if (updates.display_name || updates.username) { invalidateUserSearch(); } toast.success("Profile Updated", { description: "Your changes have been saved.", }); }, }); }