This repository has no description
1<script lang="ts">
2 import { browser } from "$app/environment";
3 import { resolve } from "$app/paths";
4 import { page } from "$app/state";
5 import { getAuth } from "$lib/auth.svelte";
6 import { createAction } from "$lib/action.svelte";
7 import Button from "$lib/components/ui/Button.svelte";
8 import CircleAlert from "$icon/circle-alert";
9 import { onMount } from "svelte";
10 import { SvelteURL } from "svelte/reactivity";
11
12 const appPath = (path: string) => resolve(path as "/");
13 const auth = getAuth();
14 const returnTo = $derived(page.url.searchParams.get("return_url") ?? "/");
15
16 let identifier = $state(page.url.searchParams.get("identifier") ?? "");
17 const login = createAction(async (event: SubmitEvent) => {
18 event.preventDefault();
19 await auth.signIn(identifier, returnTo);
20 });
21
22 onMount(() => {
23 if (browser && location.hostname === "localhost") {
24 const url = new SvelteURL(location.href);
25 url.hostname = "127.0.0.1";
26 location.replace(url);
27 }
28 });
29</script>
30
31<svelte:head>
32 <title>Login · Tangled</title>
33</svelte:head>
34
35<section class="w-full max-w-md">
36 <div class="mb-4 flex flex-col items-center">
37 <a href={appPath("/")} class="flex items-center gap-2 no-underline hover:no-underline">
38 <img src="/logos/logotype.svg" alt="tangled" class="h-24 w-48 dark:invert" />
39 </a>
40 <p class="text-center typography-heading-4 font-normal text-foreground-default italic">
41 tightly-knit social coding.
42 </p>
43 </div>
44
45 <form class="mt-4" onsubmit={login.run}>
46 <div class="flex flex-col">
47 <label for="identifier" class="py-2 text-sm text-foreground-default">Handle</label>
48 <input
49 id="identifier"
50 bind:value={identifier}
51 disabled={login.loading}
52 type="text"
53 autocomplete="username"
54 autocapitalize="none"
55 spellcheck="false"
56 inputmode="url"
57 required
58 placeholder="akshay.tngl.sh"
59 class="w-full rounded-sm border border-border-default bg-background-default p-3 typography-paragraph-large text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none"
60 />
61 <p class="mt-1 text-sm text-foreground-subtle">
62 Use your <span class="text-foreground-default">AT Protocol</span> handle to log in. If
63 you're unsure, this is likely your Tangled
64 <code
65 class="rounded-sm bg-background-inset px-1 typography-monospace-small font-mono text-foreground-default"
66 >(.tngl.sh)</code
67 >
68 or Bluesky
69 <code
70 class="rounded-sm bg-background-inset px-1 typography-monospace-small font-mono text-foreground-default"
71 >(.bsky.social)</code
72 >
73 account.
74 </p>
75 </div>
76
77 <Button
78 type="submit"
79 size="sm"
80 loading={login.loading}
81 spinnerClass="size-4"
82 aria-label={login.loading ? "Logging in" : undefined}
83 class="my-2 mt-6 w-full typography-paragraph-large disabled:opacity-100"
84 >
85 Login
86 </Button>
87 </form>
88
89 <p class="text-sm text-foreground-subtle">
90 Don't have an account? <a href={appPath("/signup")} class="text-foreground-default underline"
91 >Create an account</a
92 >
93 on Tangled now!
94 </p>
95
96 {#if auth.state.kind === "failed"}
97 <div
98 class="my-2 flex gap-2 rounded-sm border border-border-danger bg-background-danger-subtle px-3 py-2 text-foreground-danger shadow-xs"
99 >
100 <span class="py-1" aria-hidden="true"><CircleAlert class="size-4" /></span>
101 <div>
102 <h5 class="font-medium">Login error</h5>
103 <p class="text-sm">{auth.state.message} Please try again.</p>
104 </div>
105 </div>
106 {/if}
107</section>