'use client' import { useMutation, useQueryClient } from '@tanstack/react-query' import { apiClient, OfflineError } from '../client' import { enqueue } from '../offline-queue' import type { Recipient, ApiOutboxMessage, PaginatedResponse } from '../types' export interface SendMessagePayload { account_id: string to: Recipient[] cc?: Recipient[] bcc?: Recipient[] subject: string body_html: string in_reply_to?: string idempotency_key: string scheduled_at?: string } export type DraftPayload = Omit export function useSendMessage() { const queryClient = useQueryClient() return useMutation({ mutationFn: async (payload: SendMessagePayload) => { try { return await apiClient.post('/mail/send', payload) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: payload.idempotency_key, timestamp: Date.now(), type: 'send_message', payload, retries: 0, }) return null } throw err } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['messages', 'sent'] }) }, }) } export function useCreateDraft() { const queryClient = useQueryClient() return useMutation({ mutationFn: async (payload: DraftPayload) => { try { return await apiClient.post('/mail/drafts', payload) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: `draft-create-${Date.now()}`, timestamp: Date.now(), type: 'create_draft', payload, retries: 0, }) return null } throw err } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['messages', 'drafts'] }) }, }) } export function useUpdateDraft() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id, ...payload }: DraftPayload & { id: string }) => { try { return await apiClient.put(`/mail/drafts/${id}`, payload) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: `draft-update-${id}-${Date.now()}`, timestamp: Date.now(), type: 'update_draft', payload: { draft_id: id, ...payload }, retries: 0, }) return null } throw err } }, onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['messages', 'drafts'] }) queryClient.invalidateQueries({ queryKey: ['message', variables.id] }) }, }) } export function useDeleteDraft() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id }: { id: string }) => { try { await apiClient.delete(`/mail/drafts/${id}`) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: `draft-delete-${id}-${Date.now()}`, timestamp: Date.now(), type: 'delete_draft', payload: { draft_id: id }, retries: 0, }) return } throw err } }, onMutate: async ({ id }) => { await queryClient.cancelQueries({ queryKey: ['messages', 'drafts'] }) const previous = queryClient.getQueriesData>({ queryKey: ['messages', 'drafts'], }) queryClient.setQueriesData>( { queryKey: ['messages', 'drafts'] }, (old) => { if (!old) return old return { ...old, data: old.data.filter((m) => m.id !== id) } } ) return { previous } }, onError: (_err, _vars, context) => { context?.previous?.forEach(([key, data]) => queryClient.setQueryData(key, data)) }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['messages', 'drafts'] }) }, }) } export function useScheduleSend() { const queryClient = useQueryClient() return useMutation({ mutationFn: async (payload: SendMessagePayload & { scheduled_at: string }) => { try { return await apiClient.post('/mail/send', payload) } catch (err) { if (err instanceof OfflineError) { await enqueue({ id: payload.idempotency_key, timestamp: Date.now(), type: 'schedule_send', payload, retries: 0, }) return null } throw err } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['outbox'] }) }, }) } export function useRescheduleSend() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id, scheduled_at }: { id: string; scheduled_at: string }) => { return await apiClient.post(`/mail/outbox/${id}/reschedule`, { scheduled_at, }) }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['outbox'] }) }, }) } export function useCancelScheduled() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id }: { id: string }) => { return await apiClient.post(`/mail/outbox/${id}/cancel`) }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['outbox'] }) }, }) } export function useSendNow() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ id }: { id: string }) => { return await apiClient.post(`/mail/outbox/${id}/send-now`) }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['outbox'] }) queryClient.invalidateQueries({ queryKey: ['messages', 'sent'] }) }, }) }