feat: add plain text only setting for email composer #105
This commit is contained in:
@@ -32,6 +32,7 @@ const RESTRICTABLE_SETTINGS = [
|
|||||||
{ key: 'externalContentPolicy', label: 'External Content Policy', category: 'Email', type: 'enum', allowedValues: ['allow', 'block', 'ask'] },
|
{ key: 'externalContentPolicy', label: 'External Content Policy', category: 'Email', type: 'enum', allowedValues: ['allow', 'block', 'ask'] },
|
||||||
{ key: 'sendConfirmation', label: 'Send Confirmation', category: 'Composer', type: 'boolean' },
|
{ key: 'sendConfirmation', label: 'Send Confirmation', category: 'Composer', type: 'boolean' },
|
||||||
{ key: 'defaultReplyMode', label: 'Default Reply Mode', category: 'Composer', type: 'enum', allowedValues: ['reply', 'reply-all'] },
|
{ key: 'defaultReplyMode', label: 'Default Reply Mode', category: 'Composer', type: 'enum', allowedValues: ['reply', 'reply-all'] },
|
||||||
|
{ key: 'plainTextMode', label: 'Plain Text Only', category: 'Composer', type: 'boolean' },
|
||||||
{ key: 'sessionTimeout', label: 'Session Timeout', category: 'Privacy', type: 'number' },
|
{ key: 'sessionTimeout', label: 'Session Timeout', category: 'Privacy', type: 'number' },
|
||||||
{ key: 'emailNotificationsEnabled', label: 'Email Notifications', category: 'Notifications', type: 'boolean' },
|
{ key: 'emailNotificationsEnabled', label: 'Email Notifications', category: 'Notifications', type: 'boolean' },
|
||||||
{ key: 'calendarNotificationsEnabled', label: 'Calendar Notifications', category: 'Notifications', type: 'boolean' },
|
{ key: 'calendarNotificationsEnabled', label: 'Calendar Notifications', category: 'Notifications', type: 'boolean' },
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export function EmailComposer({
|
|||||||
const t = useTranslations('email_composer');
|
const t = useTranslations('email_composer');
|
||||||
const tCommon = useTranslations('common');
|
const tCommon = useTranslations('common');
|
||||||
const timeFormat = useSettingsStore((state) => state.timeFormat);
|
const timeFormat = useSettingsStore((state) => state.timeFormat);
|
||||||
|
const plainTextMode = useSettingsStore((state) => state.plainTextMode);
|
||||||
|
|
||||||
// Initialize with reply/forward data if provided
|
// Initialize with reply/forward data if provided
|
||||||
const getInitialTo = () => {
|
const getInitialTo = () => {
|
||||||
@@ -134,6 +135,26 @@ export function EmailComposer({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getInitialBody = () => {
|
const getInitialBody = () => {
|
||||||
|
if (plainTextMode) {
|
||||||
|
// Plain text mode: produce plain text body with no HTML
|
||||||
|
const prefix = initialDraftText || "";
|
||||||
|
if (!replyTo?.body && !replyTo?.htmlBody) return prefix;
|
||||||
|
|
||||||
|
const date = replyTo.receivedAt ? formatDateTime(replyTo.receivedAt, timeFormat, { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' }) : "";
|
||||||
|
const from = replyTo.from?.[0];
|
||||||
|
const fromStr = from ? `${from.name || from.email}` : tCommon('unknown');
|
||||||
|
|
||||||
|
const originalText = replyTo.body || (replyTo.htmlBody ? htmlToPlainText(replyTo.htmlBody) : '');
|
||||||
|
const quotedText = originalText.split('\n').map(line => `> ${line}`).join('\n');
|
||||||
|
|
||||||
|
if (mode === 'forward') {
|
||||||
|
return `${prefix}\n\n---------- Forwarded message ----------\nFrom: ${fromStr}\nDate: ${date}\nSubject: ${replyTo.subject || ''}\n\n${originalText}`;
|
||||||
|
} else if (mode === 'reply' || mode === 'replyAll') {
|
||||||
|
return `${prefix}\n\nOn ${date}, ${fromStr} wrote:\n${quotedText}`;
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
const prefix = initialDraftText ? `<p>${initialDraftText.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')}</p>` : "";
|
const prefix = initialDraftText ? `<p>${initialDraftText.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')}</p>` : "";
|
||||||
if (!replyTo?.body && !replyTo?.htmlBody) return prefix;
|
if (!replyTo?.body && !replyTo?.htmlBody) return prefix;
|
||||||
|
|
||||||
@@ -369,12 +390,14 @@ export function EmailComposer({
|
|||||||
? substitutePlaceholders(template.body, filledValues)
|
? substitutePlaceholders(template.body, filledValues)
|
||||||
: template.body;
|
: template.body;
|
||||||
|
|
||||||
// Convert template plain text body to HTML for the rich text editor
|
// In plain text mode, use template body as-is; otherwise convert to HTML
|
||||||
const htmlBody = `<p>${filledBody.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')}</p>`;
|
const bodyContent = plainTextMode
|
||||||
|
? filledBody
|
||||||
|
: `<p>${filledBody.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')}</p>`;
|
||||||
|
|
||||||
if (mode === 'compose') {
|
if (mode === 'compose') {
|
||||||
setSubject(filledSubject);
|
setSubject(filledSubject);
|
||||||
setBody(htmlBody);
|
setBody(bodyContent);
|
||||||
if (template.defaultRecipients?.to?.length) {
|
if (template.defaultRecipients?.to?.length) {
|
||||||
setTo(template.defaultRecipients.to.join(', ') + ', ');
|
setTo(template.defaultRecipients.to.join(', ') + ', ');
|
||||||
}
|
}
|
||||||
@@ -387,7 +410,7 @@ export function EmailComposer({
|
|||||||
setShowBcc(true);
|
setShowBcc(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setBody((prev) => htmlBody + prev);
|
setBody((prev) => bodyContent + (plainTextMode ? '\n' : '') + prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.identityId) {
|
if (template.identityId) {
|
||||||
@@ -395,7 +418,7 @@ export function EmailComposer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setShowTemplatePicker(false);
|
setShowTemplatePicker(false);
|
||||||
}, [mode]);
|
}, [mode, plainTextMode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleTemplateKey = (e: KeyboardEvent) => {
|
const handleTemplateKey = (e: KeyboardEvent) => {
|
||||||
@@ -530,7 +553,7 @@ export function EmailComposer({
|
|||||||
const ccAddresses = cc.split(",").map(e => e.trim()).filter(Boolean);
|
const ccAddresses = cc.split(",").map(e => e.trim()).filter(Boolean);
|
||||||
const bccAddresses = bcc.split(",").map(e => e.trim()).filter(Boolean);
|
const bccAddresses = bcc.split(",").map(e => e.trim()).filter(Boolean);
|
||||||
|
|
||||||
if (!toAddresses.length && !subject && !htmlToPlainText(body).trim()) {
|
if (!toAddresses.length && !subject && !(plainTextMode ? body.trim() : htmlToPlainText(body).trim())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +589,7 @@ export function EmailComposer({
|
|||||||
const savedDraftId = await client.createDraft(
|
const savedDraftId = await client.createDraft(
|
||||||
toAddresses,
|
toAddresses,
|
||||||
subject || t('no_subject'),
|
subject || t('no_subject'),
|
||||||
htmlToPlainText(body),
|
plainTextMode ? body : htmlToPlainText(body),
|
||||||
ccAddresses,
|
ccAddresses,
|
||||||
bccAddresses,
|
bccAddresses,
|
||||||
currentIdentity?.id,
|
currentIdentity?.id,
|
||||||
@@ -630,7 +653,7 @@ export function EmailComposer({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toAddresses = to.split(",").map(e => e.trim()).filter(Boolean);
|
const toAddresses = to.split(",").map(e => e.trim()).filter(Boolean);
|
||||||
const bodyPlainText = htmlToPlainText(body).trim();
|
const bodyPlainText = plainTextMode ? body.trim() : htmlToPlainText(body).trim();
|
||||||
const hasContent = bodyPlainText || attachments.some(att => att.blobId && !att.uploading);
|
const hasContent = bodyPlainText || attachments.some(att => att.blobId && !att.uploading);
|
||||||
const canSend = toAddresses.length > 0 && !!subject && hasContent;
|
const canSend = toAddresses.length > 0 && !!subject && hasContent;
|
||||||
|
|
||||||
@@ -680,8 +703,8 @@ export function EmailComposer({
|
|||||||
: currentIdentity.email
|
: currentIdentity.email
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// Body is already HTML from the rich text editor.
|
// Body is already HTML from the rich text editor (or plain text in plain text mode).
|
||||||
// Build HTML signature block
|
// Build HTML signature block (used only in rich text mode)
|
||||||
const buildSignatureHtml = (): string => {
|
const buildSignatureHtml = (): string => {
|
||||||
if (currentIdentity?.htmlSignature) {
|
if (currentIdentity?.htmlSignature) {
|
||||||
return `<br><br>-- <br>${sanitizeEmailHtml(currentIdentity.htmlSignature)}`;
|
return `<br><br>-- <br>${sanitizeEmailHtml(currentIdentity.htmlSignature)}`;
|
||||||
@@ -692,13 +715,14 @@ export function EmailComposer({
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const signatureHtml = buildSignatureHtml();
|
// In plain text mode, send text/plain only (no HTML body)
|
||||||
|
const finalBody = plainTextMode
|
||||||
|
? appendPlainTextSignature(body, currentIdentity)
|
||||||
|
: appendPlainTextSignature(htmlToPlainText(body), currentIdentity);
|
||||||
|
|
||||||
// Build final HTML body: editor content + signature
|
const finalHtmlBody = plainTextMode
|
||||||
const finalHtmlBody = `<div>${body}</div>${signatureHtml}`;
|
? undefined
|
||||||
|
: `<div>${body}</div>${buildSignatureHtml()}`;
|
||||||
// Generate plain text version from the HTML body for multipart/alternative
|
|
||||||
const finalBody = appendPlainTextSignature(htmlToPlainText(body), currentIdentity);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// S/MIME send pipeline: build raw MIME → sign → encrypt → sendRawEmail
|
// S/MIME send pipeline: build raw MIME → sign → encrypt → sendRawEmail
|
||||||
@@ -1101,24 +1125,47 @@ export function EmailComposer({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Body - Rich Text Editor */}
|
{/* Body */}
|
||||||
<RichTextEditor
|
{plainTextMode ? (
|
||||||
content={body}
|
<textarea
|
||||||
onChange={(html) => {
|
value={body}
|
||||||
setBody(html);
|
onChange={(e) => {
|
||||||
if (validationErrors.body) setValidationErrors(prev => ({ ...prev, body: false }));
|
setBody(e.target.value);
|
||||||
}}
|
if (validationErrors.body) setValidationErrors(prev => ({ ...prev, body: false }));
|
||||||
onImageUpload={handleImageUpload}
|
}}
|
||||||
placeholder={t('body_placeholder')}
|
placeholder={t('body_placeholder')}
|
||||||
hasError={validationErrors.body}
|
className={cn(
|
||||||
/>
|
"w-full min-h-[300px] px-4 py-3 text-sm text-foreground bg-transparent resize-y focus:outline-none font-mono",
|
||||||
|
validationErrors.body && "ring-2 ring-red-500 dark:ring-red-400 rounded"
|
||||||
|
)}
|
||||||
|
style={{ height: 'calc(100vh - 350px)' }}
|
||||||
|
aria-invalid={validationErrors.body || undefined}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<RichTextEditor
|
||||||
|
content={body}
|
||||||
|
onChange={(html) => {
|
||||||
|
setBody(html);
|
||||||
|
if (validationErrors.body) setValidationErrors(prev => ({ ...prev, body: false }));
|
||||||
|
}}
|
||||||
|
onImageUpload={handleImageUpload}
|
||||||
|
placeholder={t('body_placeholder')}
|
||||||
|
hasError={validationErrors.body}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{composerSignatureHtml && (
|
{plainTextMode ? (
|
||||||
|
getPlainTextSignature(currentIdentity) ? (
|
||||||
|
<div className="px-4 pb-3 text-sm leading-6 text-muted-foreground break-words whitespace-pre-wrap font-mono">
|
||||||
|
{'-- \n'}{getPlainTextSignature(currentIdentity)}
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
) : composerSignatureHtml ? (
|
||||||
<div
|
<div
|
||||||
className="px-4 pb-3 text-sm leading-6 text-foreground break-words [&_a]:text-primary [&_a]:underline-offset-2 [&_a:hover]:underline"
|
className="px-4 pb-3 text-sm leading-6 text-foreground break-words [&_a]:text-primary [&_a]:underline-offset-2 [&_a:hover]:underline"
|
||||||
dangerouslySetInnerHTML={{ __html: `<div>-- </div>${composerSignatureHtml}` }}
|
dangerouslySetInnerHTML={{ __html: `<div>-- </div>${composerSignatureHtml}` }}
|
||||||
/>
|
/>
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Attachments */}
|
{/* Attachments */}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export function EmailSettings() {
|
|||||||
permanentlyDeleteJunk,
|
permanentlyDeleteJunk,
|
||||||
showPreview,
|
showPreview,
|
||||||
disableThreading,
|
disableThreading,
|
||||||
|
plainTextMode,
|
||||||
emailsPerPage,
|
emailsPerPage,
|
||||||
externalContentPolicy,
|
externalContentPolicy,
|
||||||
mailAttachmentAction,
|
mailAttachmentAction,
|
||||||
@@ -221,6 +222,14 @@ export function EmailSettings() {
|
|||||||
/>
|
/>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
|
{/* Plain Text Mode */}
|
||||||
|
<SettingItem label={t('plain_text_mode.label')} description={t('plain_text_mode.description')}>
|
||||||
|
<ToggleSwitch
|
||||||
|
checked={plainTextMode}
|
||||||
|
onChange={(checked) => updateSetting('plainTextMode', checked)}
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
|
||||||
{/* Quick Hover Actions */}
|
{/* Quick Hover Actions */}
|
||||||
{isFeatureEnabled('hoverActionsConfigEnabled') && (
|
{isFeatureEnabled('hoverActionsConfigEnabled') && (
|
||||||
<div className="py-3 border-b border-border space-y-3">
|
<div className="py-3 border-b border-border space-y-3">
|
||||||
|
|||||||
@@ -807,6 +807,10 @@
|
|||||||
"label": "Konversationsgruppierung deaktivieren",
|
"label": "Konversationsgruppierung deaktivieren",
|
||||||
"description": "E-Mails als einzelne Nachrichten statt nach Konversation gruppiert anzeigen"
|
"description": "E-Mails als einzelne Nachrichten statt nach Konversation gruppiert anzeigen"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Nur Klartext",
|
||||||
|
"description": "Rich-Text-Editor deaktivieren und alle E-Mails nur als Klartext senden, einschließlich Antworten und Weiterleitungen"
|
||||||
|
},
|
||||||
"attachment_click_action": {
|
"attachment_click_action": {
|
||||||
"label": "Aktion beim Klick auf Anhänge",
|
"label": "Aktion beim Klick auf Anhänge",
|
||||||
"description": "Festlegen, ob ein Dateianhang beim Anklicken in der Vorschau geöffnet oder sofort heruntergeladen wird",
|
"description": "Festlegen, ob ein Dateianhang beim Anklicken in der Vorschau geöffnet oder sofort heruntergeladen wird",
|
||||||
|
|||||||
@@ -807,6 +807,10 @@
|
|||||||
"label": "Disable Conversation Grouping",
|
"label": "Disable Conversation Grouping",
|
||||||
"description": "Show emails as individual messages instead of grouped by conversation"
|
"description": "Show emails as individual messages instead of grouped by conversation"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Plain Text Only",
|
||||||
|
"description": "Disable the rich text editor and send all emails as plain text only, including replies and forwards"
|
||||||
|
},
|
||||||
"attachment_click_action": {
|
"attachment_click_action": {
|
||||||
"label": "Attachment Click Action",
|
"label": "Attachment Click Action",
|
||||||
"description": "Choose whether clicking a file attachment previews it or downloads it immediately",
|
"description": "Choose whether clicking a file attachment previews it or downloads it immediately",
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "Desactivar agrupación de conversaciones",
|
"label": "Desactivar agrupación de conversaciones",
|
||||||
"description": "Mostrar correos como mensajes individuales en lugar de agruparlos por conversación"
|
"description": "Mostrar correos como mensajes individuales en lugar de agruparlos por conversación"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Solo texto sin formato",
|
||||||
|
"description": "Desactivar el editor de texto enriquecido y enviar todos los correos solo como texto sin formato, incluyendo respuestas y reenvíos"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "Mostrar Vista Previa",
|
"label": "Mostrar Vista Previa",
|
||||||
"description": "Mostrar vista previa del correo en la lista"
|
"description": "Mostrar vista previa del correo en la lista"
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "Désactiver le regroupement par conversation",
|
"label": "Désactiver le regroupement par conversation",
|
||||||
"description": "Afficher les e-mails comme messages individuels plutôt que groupés par conversation"
|
"description": "Afficher les e-mails comme messages individuels plutôt que groupés par conversation"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Texte brut uniquement",
|
||||||
|
"description": "Désactiver l'éditeur de texte enrichi et envoyer tous les e-mails en texte brut uniquement, y compris les réponses et les transferts"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "Afficher l'aperçu",
|
"label": "Afficher l'aperçu",
|
||||||
"description": "Afficher l'aperçu de l'email dans la liste"
|
"description": "Afficher l'aperçu de l'email dans la liste"
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "Disabilita raggruppamento conversazioni",
|
"label": "Disabilita raggruppamento conversazioni",
|
||||||
"description": "Mostra le email come messaggi singoli anziché raggruppati per conversazione"
|
"description": "Mostra le email come messaggi singoli anziché raggruppati per conversazione"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Solo testo normale",
|
||||||
|
"description": "Disabilita l'editor di testo formattato e invia tutte le email solo come testo normale, incluse risposte e inoltri"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "Mostra anteprima testo",
|
"label": "Mostra anteprima testo",
|
||||||
"description": "Visualizza l'anteprima del messaggio nell'elenco"
|
"description": "Visualizza l'anteprima del messaggio nell'elenco"
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "会話グループ化を無効にする",
|
"label": "会話グループ化を無効にする",
|
||||||
"description": "メールを会話ごとにグループ化せず、個別のメッセージとして表示します"
|
"description": "メールを会話ごとにグループ化せず、個別のメッセージとして表示します"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "プレーンテキストのみ",
|
||||||
|
"description": "リッチテキストエディターを無効にし、返信や転送を含むすべてのメールをプレーンテキストのみで送信します"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "プレビューテキストを表示",
|
"label": "プレビューテキストを表示",
|
||||||
"description": "リストにメールのプレビューを表示"
|
"description": "リストにメールのプレビューを表示"
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "Conversatiegroepering uitschakelen",
|
"label": "Conversatiegroepering uitschakelen",
|
||||||
"description": "Toon e-mails als afzonderlijke berichten in plaats van gegroepeerd op gesprek"
|
"description": "Toon e-mails als afzonderlijke berichten in plaats van gegroepeerd op gesprek"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Alleen platte tekst",
|
||||||
|
"description": "Schakel de rich text-editor uit en verzend alle e-mails alleen als platte tekst, inclusief antwoorden en doorgestuurde berichten"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "Voorbeeldtekst tonen",
|
"label": "Voorbeeldtekst tonen",
|
||||||
"description": "E-mailvoorbeeld weergeven in de lijst"
|
"description": "E-mailvoorbeeld weergeven in de lijst"
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "Desativar agrupamento de conversas",
|
"label": "Desativar agrupamento de conversas",
|
||||||
"description": "Mostrar e-mails como mensagens individuais em vez de agrupados por conversa"
|
"description": "Mostrar e-mails como mensagens individuais em vez de agrupados por conversa"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Apenas texto simples",
|
||||||
|
"description": "Desativar o editor de texto formatado e enviar todos os e-mails apenas como texto simples, incluindo respostas e encaminhamentos"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "Mostrar Texto de Visualização",
|
"label": "Mostrar Texto de Visualização",
|
||||||
"description": "Exibir visualização do e-mail na lista"
|
"description": "Exibir visualização do e-mail na lista"
|
||||||
|
|||||||
@@ -803,6 +803,10 @@
|
|||||||
"label": "Отключить группировку по беседам",
|
"label": "Отключить группировку по беседам",
|
||||||
"description": "Отображать письма как отдельные сообщения, а не сгруппированные по беседам"
|
"description": "Отображать письма как отдельные сообщения, а не сгруппированные по беседам"
|
||||||
},
|
},
|
||||||
|
"plain_text_mode": {
|
||||||
|
"label": "Только простой текст",
|
||||||
|
"description": "Отключить редактор форматированного текста и отправлять все письма только в виде простого текста, включая ответы и пересылки"
|
||||||
|
},
|
||||||
"show_preview": {
|
"show_preview": {
|
||||||
"label": "Показывать текст предпросмотра",
|
"label": "Показывать текст предпросмотра",
|
||||||
"description": "Отображать предпросмотр письма в списке"
|
"description": "Отображать предпросмотр письма в списке"
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ interface SettingsState {
|
|||||||
autoSaveDraftInterval: number; // milliseconds
|
autoSaveDraftInterval: number; // milliseconds
|
||||||
sendConfirmation: boolean;
|
sendConfirmation: boolean;
|
||||||
defaultReplyMode: ReplyMode;
|
defaultReplyMode: ReplyMode;
|
||||||
|
plainTextMode: boolean; // Send plain text only (no rich text editor)
|
||||||
|
|
||||||
// Privacy & Security
|
// Privacy & Security
|
||||||
sessionTimeout: number; // minutes (0 = never)
|
sessionTimeout: number; // minutes (0 = never)
|
||||||
@@ -237,6 +238,7 @@ const DEFAULT_SETTINGS = {
|
|||||||
autoSaveDraftInterval: 60000, // 1 minute
|
autoSaveDraftInterval: 60000, // 1 minute
|
||||||
sendConfirmation: false,
|
sendConfirmation: false,
|
||||||
defaultReplyMode: 'reply' as ReplyMode,
|
defaultReplyMode: 'reply' as ReplyMode,
|
||||||
|
plainTextMode: false,
|
||||||
|
|
||||||
// Privacy & Security
|
// Privacy & Security
|
||||||
sessionTimeout: 0, // Never
|
sessionTimeout: 0, // Never
|
||||||
@@ -344,6 +346,7 @@ export const useSettingsStore = create<SettingsState>()(
|
|||||||
autoSaveDraftInterval: state.autoSaveDraftInterval,
|
autoSaveDraftInterval: state.autoSaveDraftInterval,
|
||||||
sendConfirmation: state.sendConfirmation,
|
sendConfirmation: state.sendConfirmation,
|
||||||
defaultReplyMode: state.defaultReplyMode,
|
defaultReplyMode: state.defaultReplyMode,
|
||||||
|
plainTextMode: state.plainTextMode,
|
||||||
sessionTimeout: state.sessionTimeout,
|
sessionTimeout: state.sessionTimeout,
|
||||||
emailNotificationsEnabled: state.emailNotificationsEnabled,
|
emailNotificationsEnabled: state.emailNotificationsEnabled,
|
||||||
emailNotificationSound: state.emailNotificationSound,
|
emailNotificationSound: state.emailNotificationSound,
|
||||||
|
|||||||
Reference in New Issue
Block a user