'use client'; import { useState, useRef } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { getSignedUrl, resizeImage, uploadFile } from '@/lib/queries'; import { useAuth, QueryErrorCodes } from '@/lib/hooks/context'; import type { SupabaseClient, Result, User, Profile } from '@/utils/supabase'; import { toast } from 'sonner'; type UploadToStorageProps = { client: SupabaseClient; file: File; bucket: string; resize?: false | { maxWidth?: number; maxHeight?: number; quality?: number; }, replace?: false | string, }; const useFileUpload = () => { const [isUploading, setIsUploading] = useState(false); const fileInputRef = useRef(null); const { profile, isAuthenticated } = useAuth(); const queryClient = useQueryClient(); const uploadToStorage = async ({ client, file, bucket, resize = false, replace = false, }: UploadToStorageProps) => { try { if (!isAuthenticated) throw new Error('Error: User is not authenticated!'); setIsUploading(true); let fileToUpload = file; if (resize && file.type.startsWith('image/')) fileToUpload = await resizeImage({file, options: resize}); const path = replace || `${Date.now()}-${profile?.id}.${file.name.split('.').pop()}`; const { data, error} = await uploadFile({ client, bucket, path, file: fileToUpload, options: { contentType: file.type, ...(replace && {upsert: true}) }, }); if (error) throw new Error(`Error uploading file: ${error.message}`); const { data: urlData, error: urlError } = await getSignedUrl({ client, bucket, path: data.path, }); if (urlError) throw new Error(`Error getting signed URL: ${urlError.message}`); return {urlData, error: null}; } catch (error) { return { data: null, error }; } finally { setIsUploading(false); if (fileInputRef.current) fileInputRef.current.value = ''; } }; const uploadMutation = useMutation({ mutationFn: uploadToStorage, onSuccess: (result) => { if (result.error) { toast.error(`Upload failed: ${result.error as string}`) } else { toast.success(`File uploaded successfully!`); } }, onError: (error) => { toast.error(`Upload failed: ${error instanceof Error ? error.message : error}`); }, meta: { errCode: QueryErrorCodes.UPLOAD_PHOTO_FAILED }, }); const uploadAvatarMutation = useMutation({ mutationFn: async (props: UploadToStorageProps) => { const { data, error } = await uploadToStorage(props); if (error) throw new Error(`Error uploading avatar: ${error as string}`); return data; }, onSuccess: (avatarUrl) => { queryClient.invalidateQueries({ queryKey: ['auth'] }); queryClient.setQueryData(['auth, user'], (oldUser: User) => oldUser); if (profile?.id) { queryClient.setQueryData(['profiles', profile.id], (oldProfile: Profile) => ({ ...oldProfile, avatar_url: avatarUrl, updated_at: new Date().toISOString(), })); } toast.success('Avatar uploaded sucessfully!'); }, onError: (error) => { toast.error(`Avatar upload failed: ${error instanceof Error ? error.message : error}`); }, meta: { errCode: QueryErrorCodes.UPLOAD_PHOTO_FAILED }, }) return { isUploading: isUploading || uploadMutation.isPending || uploadAvatarMutation.isPending, fileInputRef, uploadToStorage, uploadMutation, uploadAvatarMutation, }; }; export { useFileUpload };