fix(mail): sync Object error, MIME subject decoding, sticky compact pagination

- Fix sync error: backend returns {synced, error?} not {success, synced_count}; frontend now checks error field
- Decode MIME encoded-words (=?UTF-8?Q?...?=) in backend during IMAP sync for subject/from/to/cc headers
- Add frontend decodeMimeHeader() utility for already-stored encoded subjects (MailList + MailDetail)
- Make pagination sticky at bottom of mail list (flex-col h-full, ul scrolls, pagination flex-shrink-0)
- Make pagination compact: smaller padding (px-1.5 py-0.5), text-xs, smaller icons, tighter spacing
This commit is contained in:
Agent Zero
2026-07-16 09:40:59 +02:00
parent 560e4ac69d
commit 2a80aeb0af
6 changed files with 92 additions and 39 deletions
+34 -2
View File
@@ -381,8 +381,40 @@ export function testConnection(accountId: string): Promise<{ success: boolean; m
return apiPost<{ success: boolean; message: string }>(`/mail/accounts/${accountId}/test-connection`);
}
export function triggerSync(accountId: string): Promise<{ success: boolean; synced_count: number }> {
return apiPost<{ success: boolean; synced_count: number }>(`/mail/accounts/${accountId}/sync`);
export function triggerSync(accountId: string): Promise<{ synced: number; error?: string }> {
return apiPost<{ synced: number; error?: string }>(`/mail/accounts/${accountId}/sync`);
}
/**
* Decode MIME encoded-words (=?UTF-8?Q?...?=) to readable text.
* Used for mail subjects/from names that were stored before backend decoding was added.
*/
export function decodeMimeHeader(value: string | undefined | null): string {
if (!value) return '';
// Check if value contains MIME encoded-words
if (!value.includes('=?')) return value;
try {
// Decode all encoded-words in the string
return value.replace(/=\?([^?]+)\?([BQ])\?([^?]*)\?=/gi, (_match, _charset, encoding, encoded) => {
try {
if (encoding.toUpperCase() === 'B') {
// Base64 decode
const decoded = atob(encoded);
return decodeURIComponent(escape(decoded));
} else {
// Q encoding: replace _ with space, then decode =XX hex sequences
const qDecoded = encoded.replace(/_/g, ' ').replace(/=([0-9A-F]{2})/gi, (_m, hex) => {
return String.fromCharCode(parseInt(hex, 16));
});
return decodeURIComponent(escape(qDecoded));
}
} catch {
return _match;
}
});
} catch {
return value;
}
}
// ─── Folders ────────────────────────────────────────────────────────────────