mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-13 00:03:42 +00:00
feat: hold the campaign preview request until writing settles, since HTML mode runs it from the Edit tab and a designed body is tens of kilobytes to ship on every 250ms pause, and treat a document sitting behind a leading comment as one when deciding whether to adopt a paste as HTML or to open an A/B arm as markup, because a saved email opens with the tool that wrote it before its doctype
This commit is contained in:
@@ -120,6 +120,10 @@ export default function EmailContentEditor({
|
||||
// notes below the editor come from it, and an author writing markup
|
||||
// is exactly who needs them while they write.
|
||||
if (tab !== "preview" && !code) return;
|
||||
// Switching to Preview is a deliberate act and should feel immediate.
|
||||
// Writing markup is not: the request carries the whole body, which for
|
||||
// a designed email is tens of kilobytes, so it waits for a real pause.
|
||||
const settle = tab === "preview" ? 250 : 800;
|
||||
// Responses can land out of order; only the newest request may paint.
|
||||
let active = true;
|
||||
const t = setTimeout(() => {
|
||||
@@ -140,7 +144,7 @@ export default function EmailContentEditor({
|
||||
.catch(() => {
|
||||
if (active) setServerPreview(null);
|
||||
});
|
||||
}, 250);
|
||||
}, settle);
|
||||
return () => {
|
||||
active = false;
|
||||
clearTimeout(t);
|
||||
|
||||
@@ -50,7 +50,7 @@ const DOCUMENT_CSS = `
|
||||
// designed newsletter) must not be nested inside another one: the doctype and
|
||||
// the <head> would land in the body, and the frame would preview something the
|
||||
// recipient will never see. Its own <head> gets our shell instead.
|
||||
const DOCUMENT_ROOT = /^\s*(?:<!doctype\s+html|<html[\s>])/i;
|
||||
const DOCUMENT_ROOT = /^\s*(?:<!--[\s\S]*?-->\s*)*(?:<!doctype\s+html|<html[\s>])/i;
|
||||
const HEAD_OPEN = /<head\b[^>]*>/i;
|
||||
const HTML_OPEN = /<html\b[^>]*>/i;
|
||||
|
||||
|
||||
@@ -34,6 +34,12 @@ describe("detectPastedEmail", () => {
|
||||
expect(detectPastedEmail(clipboard({ "text/plain": "<table without a close" }))).toBeNull();
|
||||
});
|
||||
|
||||
// A saved email opens with the tool that wrote it before its doctype.
|
||||
it("adopts a document behind a leading comment", () => {
|
||||
const doc = "<!-- saved from Mailchimp --><!DOCTYPE html><html><body><p>Hi</p></body></html>";
|
||||
expect(detectPastedEmail(clipboard({ "text/html": doc }))).toBe(doc);
|
||||
});
|
||||
|
||||
it("is null for an empty clipboard", () => {
|
||||
expect(detectPastedEmail(null)).toBeNull();
|
||||
expect(detectPastedEmail(clipboard({}))).toBeNull();
|
||||
@@ -46,6 +52,10 @@ describe("isDocumentBody", () => {
|
||||
expect(isDocumentBody('<style>.x{color:red}</style><p class="x">x</p>')).toBe(true);
|
||||
});
|
||||
|
||||
it("sees a document behind a leading comment", () => {
|
||||
expect(isDocumentBody("<!-- saved from Mailchimp --><!doctype html><html><body>x</body></html>")).toBe(true);
|
||||
});
|
||||
|
||||
it("is false for an ordinary campaign body", () => {
|
||||
expect(isDocumentBody("<p>Hi {{.FirstName}}, do you have ten minutes?</p>")).toBe(false);
|
||||
expect(isDocumentBody('<table><tr><td style="padding:8px">Hi</td></tr></table>')).toBe(false);
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
// a live selection was copied out of a rendered page, not a file of markup.
|
||||
const FRAGMENT_MARKER = /<!--\s*StartFragment\s*-->/i;
|
||||
|
||||
const DOCUMENT_ROOT = /^\s*(?:<!doctype\s+html|<html[\s>])/i;
|
||||
// Leading comments are skipped: a saved email opens with the tool that wrote
|
||||
// it ("<!-- saved from ... -->") before its doctype, and treating that as an
|
||||
// ordinary fragment would nest one document inside another.
|
||||
const DOCUMENT_ROOT = /^\s*(?:<!--[\s\S]*?-->\s*)*(?:<!doctype\s+html|<html[\s>])/i;
|
||||
const STYLE_BLOCK = /<style[\s>]/i;
|
||||
|
||||
// Tags that open a paste which is markup someone copied as text: the source of
|
||||
|
||||
Reference in New Issue
Block a user