feat: implement email archiving options and reorganize functionality
This commit is contained in:
@@ -3,13 +3,18 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import type { ArchiveMode } from '@/stores/settings-store';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
|
||||
import { TrustedSendersModal } from '@/components/trusted-senders-modal';
|
||||
import { ChevronRight, AlertTriangle } from 'lucide-react';
|
||||
import { ChevronRight, AlertTriangle, FolderSync, Loader2 } from 'lucide-react';
|
||||
|
||||
export function EmailSettings() {
|
||||
const t = useTranslations('settings.email_behavior');
|
||||
const [showTrustedModal, setShowTrustedModal] = useState(false);
|
||||
const [isReorganizing, setIsReorganizing] = useState(false);
|
||||
const [reorganizeResult, setReorganizeResult] = useState<string | null>(null);
|
||||
|
||||
const {
|
||||
markAsReadDelay,
|
||||
@@ -20,6 +25,7 @@ export function EmailSettings() {
|
||||
externalContentPolicy,
|
||||
mailAttachmentAction,
|
||||
emailAlwaysLightMode,
|
||||
archiveMode,
|
||||
trustedSenders,
|
||||
updateSetting,
|
||||
} = useSettingsStore();
|
||||
@@ -32,6 +38,69 @@ export function EmailSettings() {
|
||||
return t('trusted_senders.count_other', { count });
|
||||
};
|
||||
|
||||
const handleReorganizeArchive = async () => {
|
||||
const { client } = useAuthStore.getState();
|
||||
const { mailboxes, fetchMailboxes } = useEmailStore.getState();
|
||||
if (!client) return;
|
||||
|
||||
const archiveMailbox = mailboxes.find(m => m.role === 'archive' || m.name.toLowerCase() === 'archive');
|
||||
if (!archiveMailbox) return;
|
||||
|
||||
setIsReorganizing(true);
|
||||
setReorganizeResult(null);
|
||||
|
||||
try {
|
||||
const archiveId = archiveMailbox.originalId || archiveMailbox.id;
|
||||
|
||||
// Fetch all emails in the root archive mailbox
|
||||
const emails = await client.getEmailsInMailbox(archiveId);
|
||||
let movedCount = 0;
|
||||
|
||||
for (const email of emails) {
|
||||
const emailDate = new Date(email.receivedAt);
|
||||
const year = emailDate.getFullYear().toString();
|
||||
const month = (emailDate.getMonth() + 1).toString().padStart(2, '0');
|
||||
|
||||
// Re-read mailboxes from store each iteration in case new ones were created
|
||||
let currentMailboxes = useEmailStore.getState().mailboxes;
|
||||
|
||||
// Find or create year subfolder
|
||||
let yearMailbox = currentMailboxes.find(
|
||||
m => m.name === year && m.parentId === archiveId
|
||||
);
|
||||
if (!yearMailbox) {
|
||||
yearMailbox = await client.createMailbox(year, archiveId);
|
||||
await fetchMailboxes(client);
|
||||
currentMailboxes = useEmailStore.getState().mailboxes;
|
||||
}
|
||||
|
||||
if (archiveMode === 'year') {
|
||||
await client.moveEmail(email.id, yearMailbox.id);
|
||||
movedCount++;
|
||||
} else {
|
||||
// month mode
|
||||
const yearId = yearMailbox.originalId || yearMailbox.id;
|
||||
let monthMailbox = currentMailboxes.find(
|
||||
m => m.name === month && m.parentId === yearId
|
||||
);
|
||||
if (!monthMailbox) {
|
||||
monthMailbox = await client.createMailbox(month, yearId);
|
||||
await fetchMailboxes(client);
|
||||
}
|
||||
await client.moveEmail(email.id, monthMailbox.id);
|
||||
movedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
setReorganizeResult(t('archive_mode.reorganize_success', { count: movedCount }));
|
||||
} catch (error) {
|
||||
console.error('Failed to reorganize archive:', error);
|
||||
setReorganizeResult(t('archive_mode.reorganize_error'));
|
||||
} finally {
|
||||
setIsReorganizing(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsSection title={t('title')} description={t('description')}>
|
||||
{/* Mark as Read */}
|
||||
@@ -68,6 +137,40 @@ export function EmailSettings() {
|
||||
</div>
|
||||
</SettingItem>
|
||||
|
||||
{/* Archive Mode */}
|
||||
<SettingItem label={t('archive_mode.label')} description={t('archive_mode.description')}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Select
|
||||
value={archiveMode}
|
||||
onChange={(value) => updateSetting('archiveMode', value as ArchiveMode)}
|
||||
options={[
|
||||
{ value: 'single', label: t('archive_mode.single') },
|
||||
{ value: 'year', label: t('archive_mode.year') },
|
||||
{ value: 'month', label: t('archive_mode.month') },
|
||||
]}
|
||||
/>
|
||||
{archiveMode !== 'single' && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<button
|
||||
onClick={handleReorganizeArchive}
|
||||
disabled={isReorganizing}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-muted hover:bg-accent rounded-md transition-colors text-sm disabled:opacity-50"
|
||||
>
|
||||
{isReorganizing ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<FolderSync className="w-4 h-4" />
|
||||
)}
|
||||
<span>{t('archive_mode.reorganize')}</span>
|
||||
</button>
|
||||
{reorganizeResult && (
|
||||
<p className="text-xs text-muted-foreground">{reorganizeResult}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SettingItem>
|
||||
|
||||
{/* Permanently Delete Junk */}
|
||||
<SettingItem label={t('permanently_delete_junk.label')} description={t('permanently_delete_junk.description')}>
|
||||
<ToggleSwitch
|
||||
|
||||
Reference in New Issue
Block a user