<script lang="ts">
import { page } from "$app/state";
import { resolve } from "$app/paths";
import Check from "$icon/check";
import Mail from "$icon/mail";
import X from "$icon/x";
import { createAction, createLoad } from "#lib/action.svelte.ts";
import { ClientResponseError } from "#lib/api/client.ts";
import { createDeliberiClient } from "#lib/api/deliberi.ts";
import {
dismissNewsletter,
getNewsletterStatus,
subscribeNewsletter
} from "#lib/api/newsletter.ts";
import { getAuth } from "#lib/auth.svelte.ts";
import PreviewCard from "#lib/components/timeline/PreviewCard.svelte";
import Button from "#lib/components/ui/Button.svelte";
const auth = getAuth();
const deliberi = () => {
const agent = auth.agent;
const deliberiUrl = page.data.publicConfig?.deliberiUrl as string | undefined;
return agent && deliberiUrl ? createDeliberiClient({ deliberiUrl, agent }) : null;
};
// stays hidden while loading or on failure; it's a prompt, not content
const status = createLoad(async () => {
const ctx = deliberi();
return ctx ? ((await getNewsletterStatus(ctx)) ?? "unset") : null;
});
let dismissed = $state(false);
const subscribe = createAction(
async () => {
const ctx = deliberi();
if (!ctx) throw new Error("Your session is still loading. Try again.");
await subscribeNewsletter(ctx);
return true;
},
() => "Couldn't sign you up. Try again later."
);
const needsEmail = $derived(
subscribe.cause instanceof ClientResponseError &&
subscribe.cause.error === "NoVerifiedPrimaryEmail"
);
// fire and forget like the old widget: a failed write only brings the card back next visit
const dismiss = () => {
dismissed = true;
const ctx = deliberi();
if (ctx) dismissNewsletter(ctx).catch(() => {});
};
</script>
{#if status.data === "unset" && !dismissed}
<div data-testid="newsletterCard">
<PreviewCard title="Newsletter" icon={Mail}>
{#snippet action()}
<Button
variant="ghost"
size="small"
square
icon={X}
aria-label="Dismiss"
class="self-stretch [--btn-radius:var(--radius-md)]"
onclick={dismiss}
/>
{/snippet}
<div class="flex flex-col gap-sm p-sm typography-paragraph-small">
<p class="text-foreground-default">
We've got a newsletter! Get updates on what we're shipping at Tangled, sent to
your primary email.
</p>
{#if subscribe.data}
<p class="flex items-center gap-xs text-foreground-success" role="status">
<Check class="size-3.5" aria-hidden="true" />
You're signed up!
</p>
{:else}
<Button
class="self-start"
loading={subscribe.loading}
onclick={() => subscribe.run()}
>
Subscribe
</Button>
{#if needsEmail}
<p class="text-foreground-danger" role="alert">
Add and verify an email address in
<a href={resolve("/settings/emails")}>settings</a> first.
</p>
{:else if subscribe.error}
<p class="text-foreground-danger" role="alert">{subscribe.error}</p>
{/if}
{/if}
</div>
</PreviewCard>
</div>
{/if}