feat: mail phase 4 - IMAP folder create/delete + auto-sync
- Add imap_create_folder() and imap_delete_folder() service functions - Call IMAP CREATE/DELETE in folder create/delete endpoints - Add auto_sync_all_accounts() background task (every 5 min) - Start auto-sync loop on plugin activation via asyncio.create_task - Frontend: working sync button with isSyncing state and toast feedback - i18n: syncSuccess, syncFailed, syncing, autoSyncEnabled keys (de/en)
This commit is contained in:
@@ -559,6 +559,10 @@
|
||||
"sortFrom": "Absender",
|
||||
"sortSubject": "Betreff",
|
||||
"sortAsc": "Aufsteigend",
|
||||
"sortDesc": "Absteigend"
|
||||
"sortDesc": "Absteigend",
|
||||
"syncSuccess": "Synchronisierung erfolgreich",
|
||||
"syncFailed": "Synchronisierung fehlgeschlagen",
|
||||
"syncing": "Synchronisiere...",
|
||||
"autoSyncEnabled": "Auto-Sync aktiv"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +559,10 @@
|
||||
"sortFrom": "From",
|
||||
"sortSubject": "Subject",
|
||||
"sortAsc": "Ascending",
|
||||
"sortDesc": "Descending"
|
||||
"sortDesc": "Descending",
|
||||
"syncSuccess": "Sync successful",
|
||||
"syncFailed": "Sync failed",
|
||||
"syncing": "Syncing...",
|
||||
"autoSyncEnabled": "Auto-sync enabled"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
moveMail,
|
||||
saveDraft,
|
||||
updateDraft,
|
||||
triggerSync,
|
||||
type MailAccount,
|
||||
type MailFolder,
|
||||
type Mail,
|
||||
@@ -80,6 +81,7 @@ export function MailPage() {
|
||||
const [showMoveDropdown, setShowMoveDropdown] = useState(false);
|
||||
const [sortBy, setSortBy] = useState<'date' | 'from' | 'subject'>('date');
|
||||
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
|
||||
// Load accounts
|
||||
const loadAccounts = useCallback(async () => {
|
||||
@@ -585,14 +587,27 @@ export function MailPage() {
|
||||
{
|
||||
id: 'sync',
|
||||
plugin: 'mail',
|
||||
label: 'Sync',
|
||||
label: isSyncing ? t('mail.syncing') : 'Sync',
|
||||
group: 'tools',
|
||||
disabled: isSyncing || !selectedAccountId,
|
||||
icon: (
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg className={`w-3.5 h-3.5 ${isSyncing ? 'animate-spin' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
),
|
||||
onClick: () => { /* sync trigger */ },
|
||||
onClick: async () => {
|
||||
if (!selectedAccountId) return;
|
||||
setIsSyncing(true);
|
||||
try {
|
||||
await triggerSync(selectedAccountId);
|
||||
await loadMails();
|
||||
toast.success(t('mail.syncSuccess'));
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
setIsSyncing(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
if (hasBulkSelection) {
|
||||
|
||||
Reference in New Issue
Block a user