'use client' import { useMutation, useQueryClient } from '@tanstack/react-query' import { apiClient, OfflineError } from '../client' import { enqueue } from '../offline-queue' import type { PaginatedResponse, ApiMessageSummary, ApiMessageFull } from '../types' import { mailFlagIsRead, mailFlagsWithRead, mailFlagsWithStarred, mailFlagsWithImportant, } from '@/lib/mail-flags' import { useDemoMail } from '@/lib/demo/demo-mail-context' import { useDemoMailStore } from '@/lib/demo/demo-mail-store' import { mailMessagesQueryRoot } from '@/lib/api/hooks/use-mail-queries' function messageDetailQueryRoot(isDemo: boolean) { return isDemo ? (['demo', 'message'] as const) : (['message'] as const) } export function useUpdateFlags() { const queryClient = useQueryClient() const demoMail = useDemoMail() return useMutation({ mutationFn: async ({ id, flags }: { id: string; flags: string[] }) => { if (demoMail) { const updated = useDemoMailStore.getState().updateFlags(id, flags) if (!updated) throw new Error('Message introuvable') return updated } try { return await apiClient.put(`/mail/messages/${id}/flags`, { flags }) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: `flags-${id}-${Date.now()}`, timestamp: Date.now(), type: 'update_flags', payload: { message_id: id, flags }, retries: 0, }) return undefined } throw err } }, onMutate: async ({ id, flags }) => { const messagesRoot = mailMessagesQueryRoot(!!demoMail) const messageRoot = messageDetailQueryRoot(!!demoMail) await queryClient.cancelQueries({ queryKey: messagesRoot }) await queryClient.cancelQueries({ queryKey: [...messageRoot, id] }) const previousMessages = queryClient.getQueriesData>({ queryKey: messagesRoot, }) queryClient.setQueriesData>( { queryKey: messagesRoot }, (old) => { if (!old) return old return { ...old, data: old.data.map((m) => (m.id === id ? { ...m, flags } : m)) } } ) queryClient.setQueriesData({ queryKey: [...messageRoot, id] }, (old) => old ? { ...old, flags } : old ) return { previousMessages } }, onError: (_err, _vars, context) => { context?.previousMessages?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { if (demoMail) { useDemoMailStore.getState().bump() } queryClient.invalidateQueries({ queryKey: mailMessagesQueryRoot(!!demoMail) }) }, }) } export function useUpdateLabels() { const queryClient = useQueryClient() const demoMail = useDemoMail() return useMutation({ mutationFn: async ({ id, labels }: { id: string; labels: string[] }) => { if (demoMail) { const updated = useDemoMailStore.getState().updateLabels(id, labels) if (!updated) throw new Error('Message introuvable') return updated } try { return await apiClient.put(`/mail/messages/${id}/labels`, { labels }) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: `labels-${id}-${Date.now()}`, timestamp: Date.now(), type: 'update_labels', payload: { message_id: id, labels }, retries: 0, }) return undefined } throw err } }, onMutate: async ({ id, labels }) => { const messagesRoot = mailMessagesQueryRoot(!!demoMail) const messageRoot = messageDetailQueryRoot(!!demoMail) await queryClient.cancelQueries({ queryKey: messagesRoot }) await queryClient.cancelQueries({ queryKey: [...messageRoot, id] }) const previousMessages = queryClient.getQueriesData>({ queryKey: messagesRoot, }) queryClient.setQueriesData>( { queryKey: messagesRoot }, (old) => { if (!old) return old return { ...old, data: old.data.map((m) => (m.id === id ? { ...m, labels } : m)) } } ) queryClient.setQueriesData({ queryKey: [...messageRoot, id] }, (old) => old ? { ...old, labels } : old ) return { previousMessages } }, onError: (_err, _vars, context) => { context?.previousMessages?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { if (demoMail) { useDemoMailStore.getState().bump() } queryClient.invalidateQueries({ queryKey: mailMessagesQueryRoot(!!demoMail) }) }, }) } export function useDeleteMessage() { const queryClient = useQueryClient() const demoMail = useDemoMail() return useMutation({ mutationFn: async ({ id }: { id: string }) => { if (demoMail) { const updated = useDemoMailStore.getState().deleteMessage(id) if (!updated) throw new Error('Message introuvable') demoMail.notify('Message placé dans la corbeille') return } try { await apiClient.delete(`/mail/messages/${id}`) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: `delete-${id}-${Date.now()}`, timestamp: Date.now(), type: 'delete_message', payload: { message_id: id }, retries: 0, }) return } throw err } }, onMutate: async ({ id }) => { const messagesRoot = mailMessagesQueryRoot(!!demoMail) const messageRoot = messageDetailQueryRoot(!!demoMail) await queryClient.cancelQueries({ queryKey: messagesRoot }) const previousMessages = queryClient.getQueriesData>({ queryKey: messagesRoot, }) queryClient.setQueriesData>( { queryKey: messagesRoot }, (old) => { if (!old) return old return { ...old, data: old.data.filter((m) => m.id !== id) } } ) queryClient.removeQueries({ queryKey: [...messageRoot, id] }) return { previousMessages } }, onError: (_err, _vars, context) => { context?.previousMessages?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { if (demoMail) { useDemoMailStore.getState().bump() } queryClient.invalidateQueries({ queryKey: mailMessagesQueryRoot(!!demoMail) }) }, }) } export function useToggleStar() { const updateFlags = useUpdateFlags() return useMutation({ mutationFn: async ({ id, flags, starred }: { id: string; flags: string[]; starred: boolean }) => { const newFlags = mailFlagsWithStarred(flags, !starred) return updateFlags.mutateAsync({ id, flags: newFlags }) }, }) } export function useToggleImportant() { const updateFlags = useUpdateFlags() return useMutation({ mutationFn: async ({ id, flags, important, }: { id: string flags: string[] important: boolean }) => { const newFlags = mailFlagsWithImportant(flags, !important) return updateFlags.mutateAsync({ id, flags: newFlags }) }, }) } export function useMarkRead() { const updateFlags = useUpdateFlags() return useMutation({ mutationFn: async ({ id, flags }: { id: string; flags: string[] }) => { if (mailFlagIsRead(flags)) return return updateFlags.mutateAsync({ id, flags: mailFlagsWithRead(flags, true) }) }, }) }