[READ-ONLY] Mirror of https://github.com/mrgnw/textme. textme.cc
0

Configure Feed

Select the types of activity you want to include in your feed.

Add login page and AuthPill signed-in state (#4)

+57 -2
+3 -2
src/routes/+layout.server.js
··· 1 1 import { getCloudflareData } from '$lib/cloudflare.js'; 2 2 3 - export function load({ request, params }) { 3 + export function load({ request, params, locals }) { 4 4 const cloudflareData = getCloudflareData(request); 5 5 6 6 return { 7 7 ...cloudflareData, 8 - slug: params.slug 8 + slug: params.slug, 9 + user: locals.user 9 10 }; 10 11 }
+12
src/routes/+layout.svelte
··· 1 1 <script> 2 2 import "../app.css"; 3 3 import { Toaster } from "svelte-sonner"; 4 + import { AuthPill } from "@mrgnw/anahtar/components"; 5 + import { invalidateAll } from "$app/navigation"; 6 + import { page } from "$app/state"; 4 7 /** @type {{children?: import('svelte').Snippet}} */ 5 8 let { children } = $props(); 9 + let user = $derived(page.data.user ?? null); 10 + 11 + async function signOut() { 12 + await fetch("/api/auth/logout", { method: "POST" }); 13 + await invalidateAll(); 14 + } 6 15 </script> 7 16 17 + <div class="fixed right-3 top-3 z-50"> 18 + <AuthPill {user} onSuccess={() => invalidateAll()} onSignOut={signOut} /> 19 + </div> 8 20 {@render children?.()} 9 21 <Toaster position="bottom-center" richColors />
+20
src/routes/login/+page.svelte
··· 1 + <script lang="ts"> 2 + import { AuthFlow } from '@mrgnw/anahtar/components'; 3 + import { goto, invalidateAll } from '$app/navigation'; 4 + import { page } from '$app/state'; 5 + 6 + const next = $derived.by(() => { 7 + const raw = page.url.searchParams.get('next') ?? '/'; 8 + return raw.startsWith('/') && !raw.startsWith('//') ? raw : '/'; 9 + }); 10 + 11 + async function onSuccess() { 12 + await invalidateAll(); 13 + await goto(next); 14 + } 15 + </script> 16 + 17 + <main class="mx-auto mt-[15vh] max-w-sm px-4"> 18 + <h1 class="mb-6 text-center text-xl font-semibold">Sign in</h1> 19 + <AuthFlow {onSuccess} /> 20 + </main>
+22
tests/auth.spec.ts
··· 38 38 await page.goto('/gated'); 39 39 await expect(page.getByTestId('gated-user')).toHaveText(email); 40 40 }); 41 + 42 + test('signing in via the login page, then signing out', async ({ page }) => { 43 + const email = uniqueEmail('ui'); 44 + await page.goto('/login?next=%2Fgated'); 45 + 46 + await page.locator('input.anahtar-input').fill(email); 47 + await page.locator('button.anahtar-submit-icon').click(); 48 + 49 + await expect(page.locator('input.anahtar-otp-digit').first()).toBeVisible(); 50 + const code = await otpFor(page, email); 51 + await page.locator('input.anahtar-otp-digit').first().fill(code); 52 + 53 + await page.locator('.anahtar-passkey-later').click(); 54 + await expect(page).toHaveURL('/gated'); 55 + await expect(page.getByTestId('gated-user')).toHaveText(email); 56 + await expect(page.locator('.anahtar-pill-email')).toHaveText(email); 57 + 58 + await page.locator('button[title="Sign out"]').click(); 59 + await expect(page.locator('input.anahtar-pill-email-input')).toBeVisible(); 60 + await page.goto('/gated'); 61 + await expect(page).toHaveURL('/login?next=%2Fgated'); 62 + });