mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 17:11:13 -05:00
117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
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>(['profile', userId]);
|
|
|
|
// Optimistically update
|
|
queryClient.setQueryData<Profile>(['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.",
|
|
});
|
|
},
|
|
});
|
|
}
|