Embed logo in Outlook drafts
This commit is contained in:
1 parent
40f314c540
commit
7bbf2e0d4d
3 files changed
+75
-20
No files matched your search
@@ -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 = () => {
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in new issue
Block a user