|
1 | | -// Landing-page waitlist form + secondary actions, rendered by |
2 | | -// <WaitlistForm action="…" github="…" discord="…" producthunt="…" />. |
| 1 | +// Landing-page get-started actions + release-updates form, rendered by |
| 2 | +// <WaitlistForm action="…" github="…" quickstart="…" discord="…" producthunt="…" />. |
3 | 3 | // |
4 | 4 | // RDX renders the component as a bare lowercased <waitlistform> element (no |
5 | 5 | // hyphen → not a valid custom-element name), so we upgrade it by query |
6 | 6 | // selector rather than via customElements.define. The MailerLite JSONP |
7 | 7 | // endpoint sends no CORS headers, so we POST with mode:"no-cors" and treat a |
8 | | -// resolved fetch as success (the submission still reaches them). |
| 8 | +// resolved fetch as success (the submission still reaches them). Because that |
| 9 | +// fetch always resolves, the email is validated before we ever submit — |
| 10 | +// otherwise an empty or malformed address would land on the success state. |
9 | 11 |
|
10 | 12 | (function () { |
11 | 13 | var GITHUB = |
|
14 | 16 | var DISCORD = |
15 | 17 | '<svg viewBox="0 0 640 512" width="19" height="19" fill="currentColor" aria-hidden="true">' + |
16 | 18 | '<path d="M524.5 69.8a1.5 1.5 0 0 0 -.8-.7A485.1 485.1 0 0 0 404.1 32a1.8 1.8 0 0 0 -1.9 .9 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.1-30.6 1.9 1.9 0 0 0 -1.9-.9A483.7 483.7 0 0 0 116.1 69.1a1.7 1.7 0 0 0 -.8 .7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 0 0 .8 1.4A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.1-.7A348.2 348.2 0 0 0 208.1 430.4a1.9 1.9 0 0 0 -1-2.6 321.2 321.2 0 0 1 -45.9-21.9 1.9 1.9 0 0 1 -.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8 0 0 1 1.9-.3c96.2 43.9 200.4 43.9 295.5 0a1.8 1.8 0 0 1 1.9 .2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9 0 0 1 -.2 3.1 301.4 301.4 0 0 1 -45.9 21.8 1.9 1.9 0 0 0 -1 2.6 391.1 391.1 0 0 0 30 48.8 1.9 1.9 0 0 0 2.1 .7A486 486 0 0 0 610.7 405.7a1.9 1.9 0 0 0 .8-1.4C623.7 277.6 590.9 167.5 524.5 69.8zM222.5 337.6c-29 0-52.8-26.6-52.8-59.2S193.1 219.1 222.5 219.1c29.7 0 53.3 26.8 52.8 59.2C275.3 311 251.9 337.6 222.5 337.6zm195.4 0c-29 0-52.8-26.6-52.8-59.2S388.4 219.1 417.9 219.1c29.7 0 53.3 26.8 52.8 59.2C470.7 311 447.3 337.6 417.9 337.6z"/></svg>'; |
| 19 | + var BOOK = |
| 20 | + '<svg viewBox="0 0 448 512" width="16" height="16" fill="currentColor" aria-hidden="true">' + |
| 21 | + '<path d="M96 0C43 0 0 43 0 96V416c0 53 43 96 96 96H384h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V384c17.7 0 32-14.3 32-32V32c0-17.7-14.3-32-32-32H384 96zM64 416c0-17.7 14.3-32 32-32H352v64H96c-17.7 0-32-14.3-32-32zM128 136c0-4.4 3.6-8 8-8H328c4.4 0 8 3.6 8 8v16c0 4.4-3.6 8-8 8H136c-4.4 0-8-3.6-8-8V136zm8 56H328c4.4 0 8 3.6 8 8v16c0 4.4-3.6 8-8 8H136c-4.4 0-8-3.6-8-8V200c0-4.4 3.6-8 8-8z"/></svg>'; |
| 22 | + |
| 23 | + // Pragmatic email check: non-empty local part, single @, dotted domain. |
| 24 | + // Stricter than type="email"'s native rule, which accepts things like "a@b". |
| 25 | + var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
| 26 | + var ERR_DEFAULT = "Something went wrong — try again, or ping us on Discord."; |
17 | 27 |
|
18 | 28 | function attr(host, name, fallback) { |
19 | 29 | var v = host.getAttribute(name); |
|
23 | 33 | function build(host) { |
24 | 34 | var action = attr(host, "action", ""); |
25 | 35 | var github = attr(host, "github", "https://github.com/NodeDB-Lab/nodedb"); |
| 36 | + var quickstart = attr(host, "quickstart", "/docs/introduction/quickstart"); |
26 | 37 | var discord = attr(host, "discord", "https://discord.gg/s54gDMVc7B"); |
27 | 38 | var ph = attr(host, "producthunt", "https://www.producthunt.com/products/nodedb"); |
28 | 39 |
|
29 | 40 | host.innerHTML = |
30 | | - '<form class="ndb-wl-form" action="' + action + '" method="post" novalidate>' + |
31 | | - '<input class="ndb-wl-email" type="email" name="fields[email]" required ' + |
32 | | - 'autocomplete="email" placeholder="you@work.com" aria-label="Work email" />' + |
33 | | - '<input type="hidden" name="ml-submit" value="1" />' + |
34 | | - '<input type="hidden" name="anticsrf" value="true" />' + |
35 | | - '<button class="ndb-wl-submit" type="submit"><span class="ndb-wl-label">Get Early Access</span></button>' + |
36 | | - "</form>" + |
37 | | - '<p class="ndb-wl-msg ndb-wl-error" role="alert" hidden>Something went wrong — try again, or ping us on Discord.</p>' + |
38 | | - '<div class="ndb-wl-msg ndb-wl-success" hidden>' + |
39 | | - "<strong>You're on the list.</strong> Check your inbox for a confirmation email. See you on launch day — May 1." + |
40 | | - "</div>" + |
41 | 41 | '<div class="ndb-wl-actions">' + |
42 | | - '<a class="ndb-wl-btn" href="' + github + '" target="_blank" rel="noopener">' + GITHUB + "<span>Star on GitHub</span></a>" + |
| 42 | + '<a class="ndb-wl-btn ndb-wl-btn-primary" href="' + github + '" target="_blank" rel="noopener">' + GITHUB + "<span>Star on GitHub</span></a>" + |
| 43 | + '<a class="ndb-wl-btn" href="' + quickstart + '">' + BOOK + "<span>Read the Quickstart</span></a>" + |
43 | 44 | '<a class="ndb-wl-btn" href="' + discord + '" target="_blank" rel="noopener">' + DISCORD + "<span>Join Discord</span></a>" + |
44 | | - '<a class="ndb-wl-btn ndb-wl-btn-ph" href="' + ph + '" target="_blank" rel="noopener"><span class="ndb-wl-ph-mark">P</span><span>Product Hunt</span></a>' + |
| 45 | + "</div>" + |
| 46 | + '<div class="ndb-wl-updates">' + |
| 47 | + '<p class="ndb-wl-updates-title">Want release updates?</p>' + |
| 48 | + '<p class="ndb-wl-updates-sub">New engines, benchmarks, and the 1.0 launch — no spam, unsubscribe anytime.</p>' + |
| 49 | + '<form class="ndb-wl-form" action="' + action + '" method="post">' + |
| 50 | + '<input class="ndb-wl-email" type="email" name="fields[email]" required ' + |
| 51 | + 'autocomplete="email" placeholder="you@work.com" aria-label="Work email" />' + |
| 52 | + '<input type="hidden" name="ml-submit" value="1" />' + |
| 53 | + '<input type="hidden" name="anticsrf" value="true" />' + |
| 54 | + '<button class="ndb-wl-submit" type="submit"><span class="ndb-wl-label">Notify me</span></button>' + |
| 55 | + "</form>" + |
| 56 | + '<p class="ndb-wl-msg ndb-wl-error" role="alert" hidden>' + ERR_DEFAULT + "</p>" + |
| 57 | + '<div class="ndb-wl-msg ndb-wl-success" hidden>' + |
| 58 | + "<strong>You're subscribed.</strong> Check your inbox to confirm — we'll email you on new releases and the 1.0 launch." + |
| 59 | + "</div>" + |
| 60 | + "</div>" + |
| 61 | + '<div class="ndb-wl-ph-row">' + |
| 62 | + '<a class="ndb-wl-btn ndb-wl-btn-ph" href="' + ph + '" target="_blank" rel="noopener"><span class="ndb-wl-ph-mark">P</span><span>See us on Product Hunt</span></a>' + |
45 | 63 | "</div>"; |
46 | 64 |
|
47 | 65 | var form = host.querySelector("form"); |
| 66 | + var email = host.querySelector(".ndb-wl-email"); |
48 | 67 | var btn = host.querySelector(".ndb-wl-submit"); |
49 | 68 | var label = host.querySelector(".ndb-wl-label"); |
50 | 69 | var errEl = host.querySelector(".ndb-wl-error"); |
51 | 70 | var okEl = host.querySelector(".ndb-wl-success"); |
52 | 71 |
|
| 72 | + function showError(msg) { |
| 73 | + errEl.textContent = msg || ERR_DEFAULT; |
| 74 | + errEl.hidden = false; |
| 75 | + btn.disabled = false; |
| 76 | + label.textContent = "Notify me"; |
| 77 | + } |
| 78 | + |
53 | 79 | form.addEventListener("submit", function (e) { |
54 | 80 | e.preventDefault(); |
55 | 81 | errEl.hidden = true; |
| 82 | + |
| 83 | + // Validate before submitting. The no-cors fetch below always resolves, |
| 84 | + // so without this guard an empty or malformed email would still show the |
| 85 | + // success state. reportValidity() surfaces the native browser bubble. |
| 86 | + var value = email.value.trim(); |
| 87 | + email.value = value; |
| 88 | + if (!EMAIL_RE.test(value) || !form.checkValidity()) { |
| 89 | + form.reportValidity(); |
| 90 | + showError("Please enter a valid email address."); |
| 91 | + return; |
| 92 | + } |
| 93 | + |
56 | 94 | btn.disabled = true; |
57 | 95 | label.textContent = "Submitting…"; |
58 | 96 |
|
|
70 | 108 | okEl.hidden = false; |
71 | 109 | }) |
72 | 110 | .catch(function () { |
73 | | - errEl.hidden = false; |
74 | | - btn.disabled = false; |
75 | | - label.textContent = "Get Early Access"; |
| 111 | + showError(); |
76 | 112 | }); |
77 | 113 | }); |
78 | 114 | } |
|
0 commit comments