atmosphere explorer
pds.ls
tool
typescript
atproto
1import { createSignal, Show } from "solid-js";
2import "./oauth-config";
3import { useOAuthScopeFlow } from "./scope-flow";
4import { ScopeSelector } from "./scope-selector";
5
6interface LoginProps {
7 onCancel?: () => void;
8}
9
10export const Login = (props: LoginProps) => {
11 const [notice, setNotice] = createSignal("");
12 const [loginInput, setLoginInput] = createSignal("");
13
14 const scopeFlow = useOAuthScopeFlow({
15 onError: (e) => setNotice(`${e}`),
16 onRedirecting: () => {
17 setNotice(`Contacting your data server...`);
18 setTimeout(() => setNotice(`Redirecting...`), 0);
19 },
20 });
21
22 const initiateLogin = (handle: string) => {
23 setNotice("");
24 scopeFlow.initiate(handle);
25 };
26
27 const handleCancel = () => {
28 scopeFlow.cancel();
29 setLoginInput("");
30 setNotice("");
31 props.onCancel?.();
32 };
33
34 return (
35 <div class="flex flex-col gap-y-3">
36 <Show when={!scopeFlow.showScopeSelector()}>
37 <Show when={props.onCancel}>
38 <div class="flex items-center gap-2">
39 <button
40 onclick={handleCancel}
41 class="flex items-center rounded-md p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"
42 >
43 <span class="iconify lucide--arrow-left"></span>
44 </button>
45 <div class="font-semibold">Add account</div>
46 </div>
47 </Show>
48 <form class="flex flex-col gap-3" onsubmit={(e) => e.preventDefault()}>
49 <label for="username" class="hidden">
50 Add account
51 </label>
52 <input
53 type="text"
54 spellcheck={false}
55 placeholder="user.bsky.social"
56 id="username"
57 name="username"
58 autocomplete="username"
59 ref={(el) => setTimeout(() => el.focus())}
60 aria-label="Your AT Protocol handle"
61 class="dark:bg-dark-100 rounded-lg bg-white px-2.5 py-2 outline-1 outline-neutral-200 select-none focus:outline-neutral-400 dark:outline-neutral-600 dark:focus:outline-neutral-400"
62 onInput={(e) => setLoginInput(e.currentTarget.value)}
63 />
64 <button
65 onclick={() => initiateLogin(loginInput())}
66 class="dark:hover:bg-dark-200 dark:active:bg-dark-100 flex w-full items-center justify-center gap-2 rounded-lg border border-neutral-200 px-3 py-2 hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700"
67 >
68 Continue
69 </button>
70 </form>
71 </Show>
72
73 <Show when={scopeFlow.showScopeSelector()}>
74 <ScopeSelector onConfirm={scopeFlow.complete} onCancel={handleCancel} />
75 </Show>
76
77 <Show when={notice()}>
78 <div class="text-sm">{notice()}</div>
79 </Show>
80 </div>
81 );
82};