Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 52 additions & 53 deletions src/components/SubscriptionForm.astro
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
import { surveySubscription } from "@/site.config";
---

<div class="mt-12 border-t border-gray-200 dark:border-gray-700"></div>
<div class="mx-auto max-w-md pt-12 text-center ec-theme-root">
<p class="text-lg font-semibold text-accent-2">Email Updates</p>
Expand All @@ -18,91 +22,86 @@
/>
<button
type="submit"
class="shrink-0 cursor-pointer rounded-sm bg-gray-500 px-4 py-2 text-sm font-semibold text-white hover:bg-gray-600"
id="subscription-submit"
class="shrink-0 cursor-pointer rounded-sm bg-gray-500 px-4 py-2 text-sm font-semibold text-white hover:bg-gray-600 disabled:cursor-not-allowed disabled:opacity-60"
>
Subscribe
</button>
</form>
<div id="subscription-instructions" class="ec-panel mt-6 hidden text-left text-sm" hidden>
<p class="text-sm">
I am thrilled you want to join my newsletter! Unfortunately, spammers were abusing my public signup form, so I've moved my newsletter to invite-only. To join, please send me an email so I can be certain that you want to subscribe. Here's an example message you could send:
</p>
<pre id="subscription-message" class="ec-code-block mt-4"></pre>
<button type="button" id="subscription-copy-all" class="ec-inline-btn mt-4 cursor-pointer px-3 py-2 text-sm">
Copy message
</button>
<p class="mt-4 text-xs text-gray-500 dark:text-gray-400">
I'll manually add your address when I receive your email and let you know you've been added.
</p>
</div>
<div id="subscription-status" class="ec-panel mt-6 hidden text-left text-sm" hidden></div>
</div>

<script>
<script is:inline define:vars={{ surveySubscription }}>
const form = document.getElementById("subscription-form");
const emailInput = document.getElementById("subscription-email");
const instructions = document.getElementById("subscription-instructions");
const messageEl = document.getElementById("subscription-message");
const copyAllButton = document.getElementById("subscription-copy-all");

const subscriptionTo = "andrew@andrewmarder.net";
const subscriptionSubject = "new newsletter subscriber";

function buildMessage(email: string) {
return `To: ${subscriptionTo}
Subject: ${subscriptionSubject}

Hi Andrew,
const submitButton = document.getElementById("subscription-submit");
const status = document.getElementById("subscription-status");

Please subscribe the following email address to your newsletter:
const successMessage = "Thanks for signing up! I'll be in touch.";
const errorMessage =
'Something went wrong. Please email me at <a href="mailto:subscribe@andrewmarder.net" class="underline">subscribe@andrewmarder.net</a> and I\'ll add you to the newsletter.';

${email}

Thank you!`;
function showStatus(message, isHtml = false) {
if (!status) return;
if (isHtml) {
status.innerHTML = message;
} else {
status.textContent = message;
}
status.hidden = false;
status.classList.remove("hidden");
status.scrollIntoView({ behavior: "smooth", block: "nearest" });
}

if (
form instanceof HTMLFormElement &&
emailInput instanceof HTMLInputElement &&
instructions instanceof HTMLElement &&
messageEl instanceof HTMLElement &&
copyAllButton instanceof HTMLButtonElement
submitButton instanceof HTMLButtonElement &&
status instanceof HTMLElement
) {
form.addEventListener("submit", (event) => {
form.addEventListener("submit", async (event) => {
event.preventDefault();

if (!form.checkValidity()) {
form.reportValidity();
return;
}

const umami = (window as Window & { umami?: { track: (event: string) => void } }).umami;
const umami = window.umami;
if (umami && typeof umami.track === "function") {
umami.track("Subscribe");
}

const email = emailInput.value.trim();
messageEl.textContent = buildMessage(email);
instructions.hidden = false;
instructions.classList.remove("hidden");
instructions.scrollIntoView({ behavior: "smooth", block: "nearest" });
});
const originalLabel = submitButton.textContent;

submitButton.disabled = true;
submitButton.textContent = "Subscribing…";

copyAllButton.addEventListener("click", async () => {
const text = messageEl.textContent?.trim() ?? "";
if (!text) return;
let succeeded = false;

try {
await navigator.clipboard.writeText(text);
const original = copyAllButton.textContent;
copyAllButton.textContent = "Copied!";
window.setTimeout(() => {
copyAllButton.textContent = original;
}, 1500);
const response = await fetch(surveySubscription.responseUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data: { [surveySubscription.emailFieldId]: email },
finished: true,
surveyId: surveySubscription.surveyId,
}),
});
succeeded = response.ok;
} catch {
copyAllButton.textContent = "Copy failed";
window.setTimeout(() => {
copyAllButton.textContent = "Copy message";
}, 1500);
succeeded = false;
}

if (succeeded) {
submitButton.textContent = "Success!";
showStatus(successMessage);
} else {
showStatus(errorMessage, true);
submitButton.disabled = false;
submitButton.textContent = originalLabel;
}
});
}
Expand Down
8 changes: 6 additions & 2 deletions src/site.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@ export const menuLinks: { path: string; title: string }[] = [
},
];

// Newsletter subscription form URL (Keila)
export const keilaFormUrl = "https://keila.andrewmarder.net/forms/nfrm_BzLMaLXv";
export const surveySubscription = {
responseUrl:
"https://surveys.andrewmarder.net/api/v1/client/cmqv2922w000301ypsz05w87q/responses",
surveyId: "cmqv296ta000a01ypv4bhl0m9",
emailFieldId: "hs3avz7lxabnw0xugjwl7t73",
} as const;
Loading