import { generateId } from '@sim/utils/id' import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { requestJson } from '@/lib/api/client/request' import { addInboxSenderContract, getInboxConfigContract, type InboxConfig, type InboxSendersResponseBody, type InboxTask, type InboxTaskStatus, type InboxTasksResponseBody, listInboxSendersContract, listInboxTasksContract, removeInboxSenderContract, updateInboxConfigContract, } from '@/lib/api/contracts' export type { InboxConfig, InboxSendersResponseBody } export type InboxTaskItem = InboxTask export type InboxTasksResponse = InboxTasksResponseBody export const INBOX_CONFIG_STALE_TIME = 30 * 1000 export const INBOX_SENDER_LIST_STALE_TIME = 60 * 1000 export const INBOX_TASK_LIST_STALE_TIME = 15 * 1000 export const inboxKeys = { all: ['inbox'] as const, configs: () => [...inboxKeys.all, 'config'] as const, config: (workspaceId: string) => [...inboxKeys.configs(), workspaceId] as const, senders: () => [...inboxKeys.all, 'sender'] as const, senderList: (workspaceId: string) => [...inboxKeys.senders(), workspaceId] as const, tasks: () => [...inboxKeys.all, 'task'] as const, taskList: (workspaceId: string, status?: string, cursor?: string, limit?: number) => [...inboxKeys.tasks(), workspaceId, status ?? 'all', cursor ?? '', limit ?? ''] as const, } type InboxTaskStatusFilter = InboxTaskStatus async function fetchInboxConfig(workspaceId: string, signal?: AbortSignal): Promise { return requestJson(getInboxConfigContract, { params: { id: workspaceId }, signal, }) } async function fetchInboxSenders( workspaceId: string, signal?: AbortSignal ): Promise { return requestJson(listInboxSendersContract, { params: { id: workspaceId }, signal, }) } async function fetchInboxTasks( workspaceId: string, opts: { status?: InboxTaskStatusFilter; cursor?: string; limit?: number }, signal?: AbortSignal ): Promise { return requestJson(listInboxTasksContract, { params: { id: workspaceId }, query: { status: opts.status && opts.status !== 'all' ? opts.status : undefined, cursor: opts.cursor, limit: opts.limit, }, signal, }) } export function useInboxConfig(workspaceId: string) { return useQuery({ queryKey: inboxKeys.config(workspaceId), queryFn: ({ signal }) => fetchInboxConfig(workspaceId, signal), enabled: Boolean(workspaceId), staleTime: INBOX_CONFIG_STALE_TIME, }) } export function useInboxSenders(workspaceId: string) { return useQuery({ queryKey: inboxKeys.senderList(workspaceId), queryFn: ({ signal }) => fetchInboxSenders(workspaceId, signal), enabled: Boolean(workspaceId), staleTime: INBOX_SENDER_LIST_STALE_TIME, }) } export function useInboxTasks( workspaceId: string, opts: { status?: InboxTaskStatusFilter; cursor?: string; limit?: number } = {} ) { return useQuery({ queryKey: inboxKeys.taskList(workspaceId, opts.status, opts.cursor, opts.limit), queryFn: ({ signal }) => fetchInboxTasks(workspaceId, opts, signal), enabled: Boolean(workspaceId), staleTime: INBOX_TASK_LIST_STALE_TIME, placeholderData: keepPreviousData, }) } export function useToggleInbox() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ workspaceId, enabled, username, }: { workspaceId: string enabled: boolean username?: string }) => { return requestJson(updateInboxConfigContract, { params: { id: workspaceId }, body: { enabled, username }, }) }, onSettled: (_data, _error, variables) => { return queryClient.invalidateQueries({ queryKey: inboxKeys.config(variables.workspaceId) }) }, }) } export function useUpdateInboxAddress() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ workspaceId, username }: { workspaceId: string; username: string }) => { return requestJson(updateInboxConfigContract, { params: { id: workspaceId }, body: { username }, }) }, onSettled: (_data, _error, variables) => { return queryClient.invalidateQueries({ queryKey: inboxKeys.config(variables.workspaceId) }) }, }) } export function useAddInboxSender() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ workspaceId, email, label, }: { workspaceId: string email: string label?: string }) => { return requestJson(addInboxSenderContract, { params: { id: workspaceId }, body: { email, label }, }) }, onMutate: async ({ workspaceId, email, label }) => { await queryClient.cancelQueries({ queryKey: inboxKeys.senderList(workspaceId) }) const previous = queryClient.getQueryData( inboxKeys.senderList(workspaceId) ) if (previous) { queryClient.setQueryData(inboxKeys.senderList(workspaceId), { ...previous, senders: [ ...previous.senders, { id: `optimistic-${generateId()}`, email, label: label || null, createdAt: new Date().toISOString(), }, ], }) } return { previous } }, onError: (_err, variables, context) => { if (context?.previous) { queryClient.setQueryData(inboxKeys.senderList(variables.workspaceId), context.previous) } }, onSettled: (_data, _error, variables) => { return queryClient.invalidateQueries({ queryKey: inboxKeys.senderList(variables.workspaceId), }) }, }) } export function useRemoveInboxSender() { const queryClient = useQueryClient() return useMutation({ mutationFn: async ({ workspaceId, senderId }: { workspaceId: string; senderId: string }) => { return requestJson(removeInboxSenderContract, { params: { id: workspaceId }, body: { senderId }, }) }, onMutate: async ({ workspaceId, senderId }) => { await queryClient.cancelQueries({ queryKey: inboxKeys.senderList(workspaceId) }) const previous = queryClient.getQueryData( inboxKeys.senderList(workspaceId) ) if (previous) { queryClient.setQueryData(inboxKeys.senderList(workspaceId), { ...previous, senders: previous.senders.filter((s) => s.id !== senderId), }) } return { previous } }, onError: (_err, variables, context) => { if (context?.previous) { queryClient.setQueryData(inboxKeys.senderList(variables.workspaceId), context.previous) } }, onSettled: (_data, _error, variables) => { return queryClient.invalidateQueries({ queryKey: inboxKeys.senderList(variables.workspaceId), }) }, }) }