diff --git a/apps/mail/components/mail/empty-folder-button.tsx b/apps/mail/components/mail/empty-folder-button.tsx new file mode 100644 index 0000000000..82629228d3 --- /dev/null +++ b/apps/mail/components/mail/empty-folder-button.tsx @@ -0,0 +1,78 @@ +import { useState, useCallback } from 'react'; +import { toast } from 'sonner'; +import { Button } from '@/components/ui/button'; +import { Trash } from '@/components/icons/icons'; +import { trpcClient } from '@/providers/query-provider'; +import { useOptimisticActions } from '@/hooks/use-optimistic-actions'; + +interface Props { + folder: string; +} + +// A small utility component to permanently delete all emails in the currently opened folder (spam / bin) +export default function EmptyFolderButton({ folder }: Props) { + const { optimisticDeleteThreads, optimisticMoveThreadsTo } = useOptimisticActions(); + const [isLoading, setIsLoading] = useState(false); + + const handleEmptyFolder = useCallback(async () => { + if (isLoading) return; + + const confirmText = + folder === 'spam' + ? 'This will delete all emails in the Spam folder and move them to Bin. Continue?' + : 'This will permanently delete all emails in the Bin folder. Continue?'; + + if (!window.confirm(confirmText)) return; + + setIsLoading(true); + try { + const ids: string[] = []; + let cursor = ''; + const MAX_PER_PAGE = 100; + while (true) { + // Fetch mails in pages of 100 until there is no nextPageToken + const page = await trpcClient.mail.listThreads.query({ + folder, + q: '', + max: MAX_PER_PAGE, + cursor, + } as any); + if (page?.threads?.length) { + ids.push(...page.threads.map((t: { id: string }) => t.id)); + } + if (!page?.nextPageToken) break; + cursor = page.nextPageToken; + } + + if (!ids.length) { + toast.success('Folder already empty'); + return; + } + + if (folder === 'spam') { + // Move all spam to bin + optimisticMoveThreadsTo(ids, folder, 'bin'); + } else { + // Permanently delete everything from bin + optimisticDeleteThreads(ids, folder); + } + } catch (error: any) { + console.error('Failed to empty folder', error); + toast.error(error?.message ?? 'Failed to empty folder'); + } finally { + setIsLoading(false); + } + }, [folder, isLoading, optimisticDeleteThreads, optimisticMoveThreadsTo]); + + return ( + + ); +} \ No newline at end of file diff --git a/apps/mail/components/mail/mail-list.tsx b/apps/mail/components/mail/mail-list.tsx index 97a6190be9..75ed614494 100644 --- a/apps/mail/components/mail/mail-list.tsx +++ b/apps/mail/components/mail/mail-list.tsx @@ -58,6 +58,7 @@ import { useQueryState } from 'nuqs'; import { Categories } from './mail'; import { useAtom } from 'jotai'; import { useThreadNotes } from '@/hooks/use-notes'; +import { Checkbox } from '@/components/ui/checkbox'; const Thread = memo( function Thread({ @@ -420,6 +421,26 @@ const Thread = memo(