feat: warn on send when attachment keyword found but no file attached #172

This commit is contained in:
Linus Rath
2026-04-10 18:24:52 +02:00
parent 4f54f768e8
commit 5f150f039d
16 changed files with 365 additions and 17 deletions
+58 -3
View File
@@ -103,6 +103,8 @@ export function EmailComposer({
const timeFormat = useSettingsStore((state) => state.timeFormat);
const plainTextMode = useSettingsStore((state) => state.plainTextMode);
const autoSelectReplyIdentity = useSettingsStore((state) => state.autoSelectReplyIdentity);
const attachmentReminderEnabled = useSettingsStore((state) => state.attachmentReminderEnabled);
const attachmentReminderKeywords = useSettingsStore((state) => state.attachmentReminderKeywords);
// Initialize with reply/forward data if provided
const getInitialTo = () => {
@@ -212,6 +214,8 @@ export function EmailComposer({
const [smimePassphrasePrompt, setSmimePassphrasePrompt] = useState<{ keyId: string; resolve: (passphrase: string) => void; reject: () => void } | null>(null);
const [smimePassphraseInput, setSmimePassphraseInput] = useState('');
const [smimePassphraseError, setSmimePassphraseError] = useState('');
const [showAttachmentWarning, setShowAttachmentWarning] = useState(false);
const [attachmentWarningKeyword, setAttachmentWarningKeyword] = useState('');
const saveTemplateModalRef = useFocusTrap({
isActive: showSaveAsTemplate,
@@ -225,6 +229,12 @@ export function EmailComposer({
restoreFocus: true,
});
const attachmentWarningRef = useFocusTrap({
isActive: showAttachmentWarning,
onEscape: () => setShowAttachmentWarning(false),
restoreFocus: true,
});
const { client } = useAuthStore();
const identities = useIdentityStore((s) => s.identities);
const primaryIdentity = identities[0] ?? null;
@@ -723,7 +733,7 @@ export function EmailComposer({
return undefined;
};
const handleSend = async () => {
const handleSend = async (skipAttachmentCheck = false) => {
const ccAddresses = cc.split(",").map(e => e.trim()).filter(Boolean);
const bccAddresses = bcc.split(",").map(e => e.trim()).filter(Boolean);
@@ -742,6 +752,21 @@ export function EmailComposer({
return;
}
// Attachment reminder check
if (!skipAttachmentCheck && attachmentReminderEnabled) {
const hasAttachments = attachments.some(att => att.blobId && !att.uploading && !att.error);
if (!hasAttachments) {
const bodyText = htmlToPlainText(body);
const searchText = `${subject} ${bodyText}`.toLowerCase();
const matched = attachmentReminderKeywords.find(kw => searchText.includes(kw.toLowerCase()));
if (matched) {
setAttachmentWarningKeyword(matched);
setShowAttachmentWarning(true);
return;
}
}
}
let finalDraftId = draftId;
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current);
@@ -1002,7 +1027,7 @@ export function EmailComposer({
</div>
{/* Mobile: send button in header */}
<Button
onClick={handleSend}
onClick={() => handleSend()}
disabled={!canSend}
title={getSendTooltip()}
size="sm"
@@ -1367,7 +1392,7 @@ export function EmailComposer({
{t('discard')}
</button>
<Button
onClick={handleSend}
onClick={() => handleSend()}
disabled={!canSend}
title={getSendTooltip()}
className="hidden md:inline-flex"
@@ -1467,6 +1492,36 @@ export function EmailComposer({
</div>
)}
{showAttachmentWarning && (
<div
className="fixed inset-0 bg-black/50 backdrop-blur-[1px] flex items-center justify-center z-[60] p-4 animate-in fade-in duration-150"
onClick={() => setShowAttachmentWarning(false)}
>
<div
ref={attachmentWarningRef}
role="alertdialog"
aria-modal="true"
onClick={(e) => e.stopPropagation()}
className="bg-background border border-border rounded-lg shadow-xl w-full max-w-md animate-in zoom-in-95 duration-200"
>
<div className="p-6">
<h2 className="text-lg font-semibold text-foreground">{t('forgot_attachment.title')}</h2>
<p className="mt-2 text-sm text-muted-foreground">
{t('forgot_attachment.message', { keyword: attachmentWarningKeyword })}
</p>
</div>
<div className="flex items-center justify-end gap-3 px-6 pb-6">
<Button variant="outline" onClick={() => setShowAttachmentWarning(false)}>
{t('forgot_attachment.back')}
</Button>
<Button onClick={() => { setShowAttachmentWarning(false); handleSend(true); }}>
{t('forgot_attachment.send_anyway')}
</Button>
</div>
</div>
</div>
)}
{showCloseDialog && (
<div
className="fixed inset-0 bg-black/50 backdrop-blur-[1px] flex items-center justify-center z-[60] p-4 animate-in fade-in duration-150"
+62 -1
View File
@@ -11,7 +11,7 @@ import { useEmailStore } from '@/stores/email-store';
import { cn } from '@/lib/utils';
import { RadioGroup, SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section';
import { TrustedSendersModal } from '@/components/trusted-senders-modal';
import { ChevronRight, AlertTriangle, FolderSync, Loader2, Mail } from 'lucide-react';
import { ChevronRight, AlertTriangle, FolderSync, Loader2, Mail, X } from 'lucide-react';
import { usePolicyStore } from '@/stores/policy-store';
const MAIL_LAYOUT_PREVIEW_ROWS = [
@@ -110,6 +110,8 @@ export function EmailSettings() {
}
}, []);
const [newKeyword, setNewKeyword] = useState('');
const {
markAsReadDelay,
deleteAction,
@@ -129,6 +131,8 @@ export function EmailSettings() {
hoverActionsMode,
hoverActionsCorner,
trustedSenders,
attachmentReminderEnabled,
attachmentReminderKeywords,
updateSetting,
} = useSettingsStore();
@@ -337,6 +341,63 @@ export function EmailSettings() {
/>
</SettingItem>
{/* Attachment Reminder */}
<SettingItem label={t('attachment_reminder.label')} description={t('attachment_reminder.description')}>
<ToggleSwitch
checked={attachmentReminderEnabled}
onChange={(checked) => updateSetting('attachmentReminderEnabled', checked)}
/>
</SettingItem>
{attachmentReminderEnabled && (
<div className="py-3 border-b border-border space-y-2">
<div>
<label className="text-sm font-medium text-foreground">{t('attachment_reminder.keywords_label')}</label>
<p className="text-xs text-muted-foreground mt-1">{t('attachment_reminder.keywords_description')}</p>
</div>
<div className="flex flex-wrap gap-1.5">
{attachmentReminderKeywords.map((kw) => (
<span key={kw} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs bg-muted text-foreground">
{kw}
<button
type="button"
aria-label={t('attachment_reminder.remove')}
onClick={() => updateSetting('attachmentReminderKeywords', attachmentReminderKeywords.filter(k => k !== kw))}
className="text-muted-foreground hover:text-foreground"
>
<X className="w-3 h-3" />
</button>
</span>
))}
</div>
<form
className="flex gap-2"
onSubmit={(e) => {
e.preventDefault();
const trimmed = newKeyword.trim().toLowerCase();
if (trimmed && !attachmentReminderKeywords.includes(trimmed)) {
updateSetting('attachmentReminderKeywords', [...attachmentReminderKeywords, trimmed]);
}
setNewKeyword('');
}}
>
<input
type="text"
value={newKeyword}
onChange={(e) => setNewKeyword(e.target.value)}
placeholder={t('attachment_reminder.add_placeholder')}
className="flex-1 min-w-0 px-2 py-1 text-sm bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-ring"
/>
<button
type="submit"
disabled={!newKeyword.trim()}
className="px-3 py-1 text-sm bg-muted hover:bg-accent rounded-md disabled:opacity-50 disabled:cursor-not-allowed"
>
{t('attachment_reminder.add')}
</button>
</form>
</div>
)}
{/* Quick Hover Actions */}
{isFeatureEnabled('hoverActionsConfigEnabled') && (
<div className="py-3 border-b border-border space-y-3">
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "Sie haben ungespeicherte Änderungen. Möchten Sie diese als Entwurf speichern oder verwerfen?",
"save_draft": "Entwurf speichern",
"drop_files": "Dateien zum Anhängen ablegen",
"show_less": "Weniger anzeigen"
"show_less": "Weniger anzeigen",
"forgot_attachment": {
"title": "Haben Sie den Anhang vergessen?",
"message": "Ihre Nachricht enthält \"{keyword}\", aber es ist keine Datei angehängt. Trotzdem senden?",
"send_anyway": "Trotzdem senden",
"back": "Zurück zur Bearbeitung"
}
},
"confirm_dialog": {
"confirm": "Bestätigen",
@@ -914,6 +920,15 @@
"button": "Als Standard festlegen",
"success": "Browser wurde aufgefordert, als Standard festzulegen",
"error": "Ihr Browser unterstützt diese Funktion nicht"
},
"attachment_reminder": {
"label": "Erinnerung an Anhang",
"description": "Warnung anzeigen, wenn die Nachricht Anhänge erwähnt, aber keine angehängt sind",
"keywords_label": "Schlüsselwörter",
"keywords_description": "Wörter oder Phrasen, die die Erinnerung auslösen",
"add_placeholder": "Schlüsselwort hinzufügen...",
"add": "Hinzufügen",
"remove": "Entfernen"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"smime_unlock_title": "Unlock S/MIME Key",
"smime_unlock_message": "Enter the passphrase to unlock your S/MIME signing key.",
"smime_unlock_button": "Unlock",
"smime_passphrase_placeholder": "Passphrase"
"smime_passphrase_placeholder": "Passphrase",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Confirm",
@@ -914,6 +920,15 @@
"button": "Set as Default",
"success": "Browser prompted to set as default",
"error": "Your browser does not support this feature"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "Tiene cambios sin guardar. ¿Desea guardar esto como borrador o descartarlo?",
"save_draft": "Guardar borrador",
"drop_files": "Suelta archivos para adjuntar",
"show_less": "Mostrar menos"
"show_less": "Mostrar menos",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Confirmar",
@@ -914,6 +920,15 @@
"button": "Establecer como predeterminado",
"success": "El navegador solicitó establecer como predeterminado",
"error": "Su navegador no admite esta función"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer comme brouillon ou supprimer ?",
"save_draft": "Enregistrer le brouillon",
"drop_files": "Déposez les fichiers à joindre",
"show_less": "Afficher moins"
"show_less": "Afficher moins",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Confirmer",
@@ -914,6 +920,15 @@
"button": "Définir par défaut",
"success": "Le navigateur a été invité à définir par défaut",
"error": "Votre navigateur ne prend pas en charge cette fonctionnalité"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "Hai modifiche non salvate. Vuoi salvare come bozza o eliminare?",
"save_draft": "Salva bozza",
"drop_files": "Trascina i file per allegarli",
"show_less": "Mostra meno"
"show_less": "Mostra meno",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Conferma",
@@ -914,6 +920,15 @@
"button": "Imposta come predefinito",
"success": "Il browser ha chiesto di impostare come predefinito",
"error": "Il tuo browser non supporta questa funzionalità"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "未保存の変更があります。下書きとして保存しますか、それとも破棄しますか?",
"save_draft": "下書きを保存",
"drop_files": "ファイルをドロップして添付",
"show_less": "折りたたむ"
"show_less": "折りたたむ",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "確認",
@@ -914,6 +920,15 @@
"button": "既定に設定",
"success": "ブラウザに既定として設定するよう要求しました",
"error": "お使いのブラウザはこの機能をサポートしていません"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"smime_unlock_title": "S/MIME 키 잠금 해제",
"smime_unlock_message": "S/MIME 서명 키의 잠금을 해제하려면 비밀번호를 입력해 주세요.",
"smime_unlock_button": "잠금 해제",
"smime_passphrase_placeholder": "비밀번호"
"smime_passphrase_placeholder": "비밀번호",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "확인",
@@ -914,6 +920,15 @@
"button": "기본값으로 설정",
"success": "브라우저에서 기본 설정 팝업이 뜰 거예요",
"error": "이 브라우저에서는 이 기능을 지원하지 않아요"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -493,7 +493,13 @@
"smime_unlock_title": "Atbloķēt S/MIME atslēgu",
"smime_unlock_message": "Ievadiet paroli, lai atbloķētu savu S/MIME parakstīšanas atslēgu.",
"smime_unlock_button": "Atbloķēt",
"smime_passphrase_placeholder": "Parole"
"smime_passphrase_placeholder": "Parole",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Apstiprināt",
@@ -913,6 +919,15 @@
"button": "Iestatīt kā noklusējumu",
"success": "Pārlūkam nosūtīts pieprasījums iestatīt kā noklusējumu",
"error": "Jūsu pārlūks neatbalsta šo funkciju"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "U heeft niet-opgeslagen wijzigingen. Wilt u dit als concept opslaan of verwijderen?",
"save_draft": "Concept opslaan",
"drop_files": "Sleep bestanden om bij te voegen",
"show_less": "Minder tonen"
"show_less": "Minder tonen",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Bevestigen",
@@ -914,6 +920,15 @@
"button": "Instellen als standaard",
"success": "Browser gevraagd om als standaard in te stellen",
"error": "Uw browser ondersteunt deze functie niet"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"smime_unlock_title": "Odblokuj klucz S/MIME",
"smime_unlock_message": "Wprowadź hasło, aby odblokować klucz podpisywania S/MIME.",
"smime_unlock_button": "Odblokuj",
"smime_passphrase_placeholder": "Hasło"
"smime_passphrase_placeholder": "Hasło",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Potwierdź",
@@ -916,6 +922,15 @@
"button": "Ustaw jako domyślny",
"success": "Przeglądarka poprosiła o ustawienie jako domyślnego",
"error": "Twoja przeglądarka nie obsługuje tej funkcji"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"close_draft_message": "Você tem alterações não salvas. Deseja salvar como rascunho ou descartar?",
"save_draft": "Salvar rascunho",
"drop_files": "Solte arquivos para anexar",
"show_less": "Mostrar menos"
"show_less": "Mostrar menos",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Confirmar",
@@ -914,6 +920,15 @@
"button": "Definir como padrão",
"success": "O navegador solicitou definir como padrão",
"error": "Seu navegador não suporta esta funcionalidade"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"smime_unlock_title": "Разблокировать ключ S/MIME",
"smime_unlock_message": "Введите парольную фразу для разблокировки вашего ключа подписи S/MIME.",
"smime_unlock_button": "Разблокировать",
"smime_passphrase_placeholder": "Парольная фраза"
"smime_passphrase_placeholder": "Парольная фраза",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "Подтвердить",
@@ -914,6 +920,15 @@
"button": "Установить по умолчанию",
"success": "Браузер запрошен для установки по умолчанию",
"error": "Ваш браузер не поддерживает эту функцию"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+16 -1
View File
@@ -494,7 +494,13 @@
"smime_unlock_title": "解锁 S/MIME 密钥",
"smime_unlock_message": "输入密码以解锁您的 S/MIME 签名密钥。",
"smime_unlock_button": "解锁",
"smime_passphrase_placeholder": "输入密码"
"smime_passphrase_placeholder": "输入密码",
"forgot_attachment": {
"title": "Did you forget an attachment?",
"message": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?",
"send_anyway": "Send anyway",
"back": "Back to editing"
}
},
"confirm_dialog": {
"confirm": "确认",
@@ -914,6 +920,15 @@
"button": "设为默认",
"success": "浏览器已提示设置为默认",
"error": "您的浏览器不支持此功能"
},
"attachment_reminder": {
"label": "Attachment Reminder",
"description": "Warn before sending when your message mentions attachments but none are attached",
"keywords_label": "Trigger keywords",
"keywords_description": "Words or phrases that trigger the reminder when found in your message",
"add_placeholder": "Add keyword...",
"add": "Add",
"remove": "Remove"
}
},
"composer": {
+37
View File
@@ -185,6 +185,10 @@ interface SettingsState {
// Keywords (labels/tags)
emailKeywords: KeywordDefinition[];
// Attachment Reminder
attachmentReminderEnabled: boolean;
attachmentReminderKeywords: string[];
// Sidebar Apps
sidebarApps: SidebarApp[];
keepAppsLoaded: boolean;
@@ -314,6 +318,37 @@ const DEFAULT_SETTINGS = {
// Keywords
emailKeywords: DEFAULT_KEYWORDS,
// Attachment Reminder
attachmentReminderEnabled: true,
attachmentReminderKeywords: [
// English
'attached', 'attachment', 'attachments', 'see attached', 'find attached', 'please find attached',
// German
'angehängt', 'anhang', 'anbei', 'im anhang',
// French
'ci-joint', 'pièce jointe',
// Spanish
'adjunto', 'adjunta', 'en adjunto',
// Italian
'allegato', 'in allegato',
// Dutch
'bijgevoegd', 'bijlage',
// Portuguese
'em anexo', 'anexo',
// Polish
'w załączniku',
// Russian
'во вложении',
// Japanese
'添付',
// Chinese
'附件',
// Korean
'첨부',
// Latvian
'pielikumā',
] as string[],
// Sidebar Apps
sidebarApps: [] as SidebarApp[],
keepAppsLoaded: false,
@@ -412,6 +447,8 @@ export const useSettingsStore = create<SettingsState>()(
senderFavicons: state.senderFavicons,
folderIcons: state.folderIcons,
emailKeywords: state.emailKeywords,
attachmentReminderEnabled: state.attachmentReminderEnabled,
attachmentReminderKeywords: state.attachmentReminderKeywords,
sidebarApps: state.sidebarApps,
keepAppsLoaded: state.keepAppsLoaded,
debugMode: state.debugMode,