'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' export function useUpdateFlags() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id, flags }: { id: string; flags: string[] }) => { 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 }) => { await queryClient.cancelQueries({ queryKey: ['messages'] }) await queryClient.cancelQueries({ queryKey: ['message', id] }) const previousMessages = queryClient.getQueriesData>({ queryKey: ['messages'], }) queryClient.setQueriesData>( { queryKey: ['messages'] }, (old) => { if (!old) return old return { ...old, data: old.data.map((m) => (m.id === id ? { ...m, flags } : m)) } } ) queryClient.setQueryData(['message', id], (old) => old ? { ...old, flags } : old ) return { previousMessages } }, onError: (_err, _vars, context) => { context?.previousMessages?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['messages'] }) }, }) } export function useUpdateLabels() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id, labels }: { id: string; labels: string[] }) => { 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 }) => { await queryClient.cancelQueries({ queryKey: ['messages'] }) await queryClient.cancelQueries({ queryKey: ['message', id] }) const previousMessages = queryClient.getQueriesData>({ queryKey: ['messages'], }) queryClient.setQueriesData>( { queryKey: ['messages'] }, (old) => { if (!old) return old return { ...old, data: old.data.map((m) => (m.id === id ? { ...m, labels } : m)) } } ) queryClient.setQueryData(['message', id], (old) => old ? { ...old, labels } : old ) return { previousMessages } }, onError: (_err, _vars, context) => { context?.previousMessages?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['messages'] }) }, }) } export function useDeleteMessage() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id }: { id: string }) => { 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 }) => { await queryClient.cancelQueries({ queryKey: ['messages'] }) const previousMessages = queryClient.getQueriesData>({ queryKey: ['messages'], }) queryClient.setQueriesData>( { queryKey: ['messages'] }, (old) => { if (!old) return old return { ...old, data: old.data.filter((m) => m.id !== id) } } ) queryClient.removeQueries({ queryKey: ['message', id] }) return { previousMessages } }, onError: (_err, _vars, context) => { context?.previousMessages?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['messages'] }) }, }) } export function useToggleStar() { const updateFlags = useUpdateFlags() return useMutation({ mutationFn: async ({ id, flags, starred }: { id: string; flags: string[]; starred: boolean }) => { const newFlags = starred ? flags.filter((f) => f !== 'starred') : [...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 = important ? flags.filter((f) => f !== 'important') : [...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 (flags.includes('read')) return return updateFlags.mutateAsync({ id, flags: [...flags, 'read'] }) }, }) }