Merge remote-tracking branch 'origin/main' into pr/quote-header-i18n

This commit is contained in:
Linus Rath
2026-05-30 16:15:18 +02:00
59 changed files with 2130 additions and 337 deletions
+2
View File
@@ -9,6 +9,7 @@ import { ProtocolLaunchHandlerProvider } from "@/components/protocol/protocol-la
import { ProInterfaceRedirect } from "@/components/pro/pro-interface-redirect";
import { PluginDialogHost } from "@/components/plugins/plugin-dialog-host";
import { PluginConsentDialog } from "@/components/plugins/plugin-consent-dialog";
import { PWAInstallPrompt } from "@/components/pwa-install-prompt";
import { locales } from "@/i18n/routing";
export default async function LocaleLayout({
@@ -41,6 +42,7 @@ export default async function LocaleLayout({
{children}
<PluginDialogHost />
<PluginConsentDialog />
<PWAInstallPrompt />
</ProtocolLaunchHandlerProvider>
</TourProvider>
</EmbeddedBridgeProvider>
+23 -9
View File
@@ -55,7 +55,7 @@ import { useProMultiAccountMailboxes } from "@/hooks/use-pro-multi-account-mailb
import { Input } from "@/components/ui/input";
import { FilePreviewModal } from "@/components/files/file-preview-modal";
import { isFilePreviewable } from "@/lib/file-preview";
import { appendPlainTextSignature } from "@/lib/signature-utils";
import { appendHtmlSignature, appendPlainTextSignature } from "@/lib/signature-utils";
import { computeReplyThreadingHeaders } from "@/lib/email-threading";
import { EML_IMPORT_ACCEPT, expandImportableEmails } from "@/lib/eml-import";
import { resolveReplyFrom } from "@/lib/reply-identity";
@@ -72,6 +72,7 @@ import { plainTextToComposerBody } from "@/lib/email-composer-utils";
import { appLifecycleHooks, uiHooks, routerHooks, toastHooks, emailHooks } from "@/lib/plugin-hooks";
import { emailToReadView } from "@/lib/plugin-projection";
import { buildQuoteHeader } from "@/lib/quote-header";
import { buildReplySubject, buildForwardSubject } from "@/lib/subject-prefix";
import { useLocaleStore } from "@/stores/locale-store";
import type { QuoteHeader } from "@/lib/plugin-types";
@@ -752,9 +753,9 @@ export default function Home() {
let title = t('email_composer.new_message');
if (baseSubject) {
if (effectiveMode === 'reply' || effectiveMode === 'replyAll') {
title = baseSubject.startsWith('Re:') ? baseSubject : `Re: ${baseSubject}`;
title = buildReplySubject(baseSubject, t('email_composer.prefix.reply'));
} else if (effectiveMode === 'forward') {
title = baseSubject.startsWith('Fwd:') ? baseSubject : `Fwd: ${baseSubject}`;
title = buildForwardSubject(baseSubject, t('email_composer.prefix.forward'));
} else {
title = baseSubject;
}
@@ -1130,6 +1131,7 @@ export default function Home() {
inReplyTo?: string[];
references?: string[];
delayedUntil?: string;
requestReadReceipt?: boolean;
}) => {
if (!client) return;
@@ -1137,7 +1139,7 @@ export default function Home() {
const effectiveMode = pendingDraft?.mode ?? composerMode;
const originalEmailId = selectedEmail?.id;
const result = await sendEmail(client, data.to, data.subject, data.body, data.cc, data.bcc, data.identityId, data.fromEmail, data.draftId, data.fromName, data.htmlBody, data.attachments, data.inReplyTo, data.references, data.delayedUntil, data.envelopeMailFrom);
const result = await sendEmail(client, data.to, data.subject, data.body, data.cc, data.bcc, data.identityId, data.fromEmail, data.draftId, data.fromName, data.htmlBody, data.attachments, data.inReplyTo, data.references, data.delayedUntil, data.envelopeMailFrom, { requestReadReceipt: data.requestReadReceipt });
setShowComposer(false);
if (result.scheduled) {
await refreshScheduledMetadata(client);
@@ -2098,9 +2100,21 @@ export default function Home() {
// Append signature from the sending identity (fall back to primary
// when the reply-from lives on the same identity but a different alias).
const finalBody = appendPlainTextSignature(body, sendingIdentity, {
separator: useSettingsStore.getState().signatureSeparatorEnabled,
});
const separator = useSettingsStore.getState().signatureSeparatorEnabled;
const finalBody = appendPlainTextSignature(body, sendingIdentity, { separator });
// When the identity has an HTML signature, send a matching HTML body so the
// signature keeps its formatting; appendPlainTextSignature would otherwise
// flatten it to plain text. Text-only identities keep the plain-text-only
// behavior (htmlBody stays undefined).
const escapedBody = body
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>');
const finalHtmlBody = sendingIdentity?.htmlSignature?.trim()
? appendHtmlSignature(`<div>${escapedBody}</div>`, sendingIdentity, { separator })
: undefined;
const originalEmailId = selectedEmail.id;
const sendDelaySeconds = useSettingsStore.getState().sendDelaySeconds;
@@ -2124,7 +2138,7 @@ export default function Home() {
const result = await sendEmail(
client,
[sender.email],
`Re: ${selectedEmail.subject || "(no subject)"}`,
buildReplySubject(selectedEmail.subject || "(no subject)", t('email_composer.prefix.reply')),
finalBody,
undefined,
undefined,
@@ -2132,7 +2146,7 @@ export default function Home() {
headerFromEmail,
undefined,
headerFromName,
undefined,
finalHtmlBody,
undefined,
threading?.inReplyTo,
threading?.references,