fix login confusion and enforce reactive subscription after login
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { redeemOtp } from '$lib/server/member-otp';
|
||||
import { slugify, handle } from '@shared/slugify';
|
||||
import { setSessionCookie, clearLegacyCookies } from '$lib/server/session';
|
||||
|
||||
export const actions = {
|
||||
default: async (event) => {
|
||||
const fd = await event.request.formData();
|
||||
const famName = (fd.get('family') || '').toString().trim();
|
||||
const name = (fd.get('name') || '').toString().trim();
|
||||
const otp = (fd.get('otp') || '').toString().trim();
|
||||
|
||||
if (!famName) return fail(400, { error: 'Enter your family name.', famName, name, otp });
|
||||
if (!name) return fail(400, { error: 'Enter your name.', famName, name, otp });
|
||||
if (!otp) return fail(400, { error: 'Enter the code shown by your parent.', famName, name, otp });
|
||||
|
||||
const famSlug = slugify(famName);
|
||||
try {
|
||||
const token = await redeemOtp({ famSlug, username: name, otp });
|
||||
clearLegacyCookies(event.cookies);
|
||||
setSessionCookie(event.cookies, token);
|
||||
} catch (e) {
|
||||
return fail(400, {
|
||||
error: e instanceof Error ? e.message : 'Join failed',
|
||||
famName,
|
||||
name,
|
||||
otp
|
||||
});
|
||||
}
|
||||
|
||||
const handleName = handle(name);
|
||||
throw redirect(303, `/${famSlug}/${encodeURIComponent(handleName)}`);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import { Button } from '$lib/components';
|
||||
import { homeIcon } from '$lib/components/icons';
|
||||
|
||||
let name = $state('');
|
||||
let family = $state('');
|
||||
let otp = $state('');
|
||||
let { form } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head><title>Join a family</title></svelte:head>
|
||||
|
||||
<main class="mx-auto flex min-h-screen max-w-md flex-col items-center justify-center px-6">
|
||||
<section class="w-full rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-sm">
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 text-2xl">
|
||||
<span class="home-badge">{@html homeIcon}</span>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold text-slate-900">Join the family</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
Enter your family name and the code your parent gave you to get started.
|
||||
</p>
|
||||
|
||||
<form
|
||||
class="mt-6 flex flex-col gap-3"
|
||||
method="POST"
|
||||
use:enhance={() => {
|
||||
return async ({ result, update }) => {
|
||||
if (result.type === 'failure') {
|
||||
family = (result.data as any)?.family || '';
|
||||
name = (result.data as any)?.name || '';
|
||||
otp = (result.data as any)?.otp || '';
|
||||
}
|
||||
await update();
|
||||
};
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="family"
|
||||
bind:value={family}
|
||||
placeholder="Family name"
|
||||
autocomplete="organization"
|
||||
class="w-full rounded-lg border border-slate-300 px-4 py-3 text-center text-lg text-slate-900 outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
bind:value={name}
|
||||
placeholder="Your name"
|
||||
autocomplete="name"
|
||||
class="w-full rounded-lg border border-slate-300 px-4 py-3 text-center text-lg text-slate-900 outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
name="otp"
|
||||
bind:value={otp}
|
||||
inputmode="numeric"
|
||||
maxlength="6"
|
||||
placeholder="6-digit code"
|
||||
autocomplete="one-time-code"
|
||||
class="w-full rounded-lg border border-slate-300 px-4 py-3 text-center text-2xl tracking-[0.5em] text-slate-900 outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200"
|
||||
/>
|
||||
{#if form?.error}
|
||||
<p class="text-sm font-medium text-rose-600">{form.error}</p>
|
||||
{/if}
|
||||
<Button type="submit" variant="primary" size="lg">Join</Button>
|
||||
</form>
|
||||
|
||||
<p class="mt-6 text-xs text-slate-400">
|
||||
Code is valid for 20 minutes. Ask your parent for a new one if it expires.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.home-badge :global(svg) {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user