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