Embed logo in Outlook drafts

This commit is contained in:
Michael committed 2026-07-14 17:03:19 +02:00
1 parent 40f314c540
commit 7bbf2e0d4d
3 files changed
+75 -20

No files matched your search

+12 -7
View File
@@ -66,14 +66,19 @@ export function EmailCreatorTab() {
}
};
const downloadOutlook = () => {
const downloadOutlook = async () => {
const slug = form.subject.normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '').toLowerCase() || 'email';
downloadOutlookDraft({
subject: form.subject,
html: data.html,
filename: `${slug}-outlook.eml`,
});
flash('outlook');
try {
setError('');
await downloadOutlookDraft({
subject: form.subject,
html: data.html,
filename: `${slug}-outlook.eml`,
});
flash('outlook');
} catch (err) {
setError(err.message || "Impossible de créer le brouillon Outlook.");
}
};
const download = () => {
+12 -7
View File
@@ -393,14 +393,19 @@ function EmailModal({ renc, onClose }) {
}
};
const downloadOutlook = () => {
const downloadOutlook = async () => {
const slug = renc.titre.normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[^a-zA-Z0-9]+/g, '-').toLowerCase();
downloadOutlookDraft({
subject: data.subject,
html: data.html,
filename: `invitation-${slug}-outlook.eml`,
});
flash('outlook');
try {
setError('');
await downloadOutlookDraft({
subject: data.subject,
html: data.html,
filename: `invitation-${slug}-outlook.eml`,
});
flash('outlook');
} catch (err) {
setError(err.message || "Impossible de créer le brouillon Outlook.");
}
};
const download = () => {
+51 -6
View File
@@ -1,5 +1,7 @@
function utf8Base64(value) {
const bytes = new TextEncoder().encode(String(value ?? ''));
const LOGO_CID = 'sbc-business-club-logo';
const MIME_BOUNDARY = '----=_SBC_Outlook_Draft_Logo';
function bytesBase64(bytes) {
let binary = '';
for (let offset = 0; offset < bytes.length; offset += 0x8000) {
@@ -9,27 +11,70 @@ function utf8Base64(value) {
return globalThis.btoa(binary);
}
function utf8Base64(value) {
return bytesBase64(new TextEncoder().encode(String(value ?? '')));
}
function wrapBase64(value) {
return value.match(/.{1,76}/g)?.join('\r\n') ?? '';
}
export function buildOutlookDraftMime({ subject, html }) {
function logoSource(html) {
return String(html).match(/\bsrc=["']([^"']*\/assets\/logo\.jpg(?:\?[^"']*)?)["']/i)?.[1] || '/assets/logo.jpg';
}
function useInlineLogo(html) {
return String(html).replace(
/(\bsrc\s*=\s*)(["'])(?:https?:\/\/[^"']+)?\/assets\/logo\.jpg(?:\?[^"']*)?\2/gi,
(_match, prefix, quote) => `${prefix}${quote}cid:${LOGO_CID}${quote}`,
);
}
export function buildOutlookDraftMime({ subject, html, logoBase64 = '' }) {
// X-Unsent asks Outlook to open the MIME message as a message to compose.
// Loading HTML as a real MIME body bypasses Outlook's destructive paste merge.
if (!logoBase64) {
return [
'X-Unsent: 1',
`Subject: =?UTF-8?B?${utf8Base64(subject)}?=`,
'MIME-Version: 1.0',
'Content-Type: text/html; charset=UTF-8',
'Content-Transfer-Encoding: base64',
'',
wrapBase64(utf8Base64(html)),
'',
].join('\r\n');
}
return [
'X-Unsent: 1',
`Subject: =?UTF-8?B?${utf8Base64(subject)}?=`,
'MIME-Version: 1.0',
`Content-Type: multipart/related; type="text/html"; boundary="${MIME_BOUNDARY}"`,
'',
`--${MIME_BOUNDARY}`,
'Content-Type: text/html; charset=UTF-8',
'Content-Transfer-Encoding: base64',
'',
wrapBase64(utf8Base64(html)),
wrapBase64(utf8Base64(useInlineLogo(html))),
`--${MIME_BOUNDARY}`,
'Content-Type: image/jpeg; name="logo.jpg"',
'Content-Transfer-Encoding: base64',
`Content-ID: <${LOGO_CID}>`,
'Content-Disposition: inline; filename="logo.jpg"',
'',
wrapBase64(logoBase64),
`--${MIME_BOUNDARY}--`,
'',
].join('\r\n');
}
export function downloadOutlookDraft({ subject, html, filename }) {
const mime = buildOutlookDraftMime({ subject, html });
export async function downloadOutlookDraft({ subject, html, filename }) {
const response = await fetch(logoSource(html), { cache: 'force-cache' });
if (!response.ok) throw new Error("Le logo de l'e-mail n'a pas pu être chargé.");
const logoBase64 = bytesBase64(new Uint8Array(await response.arrayBuffer()));
const mime = buildOutlookDraftMime({ subject, html, logoBase64 });
const blob = new Blob([mime], { type: 'message/rfc822' });
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');