atproto explorer pdsls.dev
atproto tool
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-2 px-1"> 36 <Show when={!scopeFlow.showScopeSelector()}> 37 <Show when={props.onCancel}> 38 <div class="mb-1 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-2" onsubmit={(e) => e.preventDefault()}> 49 <label for="username" class="hidden"> 50 Add account 51 </label> 52 <div class="dark:bg-dark-100 dark:inset-shadow-dark-200 flex grow items-center gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 inset-shadow-xs focus-within:outline-[1px] focus-within:outline-neutral-600 dark:border-neutral-600 dark:focus-within:outline-neutral-400"> 53 <label 54 for="username" 55 class="iconify lucide--user-round-plus shrink-0 text-neutral-500 dark:text-neutral-400" 56 ></label> 57 <input 58 type="text" 59 spellcheck={false} 60 placeholder="user.bsky.social" 61 id="username" 62 name="username" 63 autocomplete="username" 64 autofocus 65 aria-label="Your AT Protocol handle" 66 class="grow py-1 select-none placeholder:text-sm focus:outline-none" 67 onInput={(e) => setLoginInput(e.currentTarget.value)} 68 /> 69 </div> 70 <button 71 onclick={() => initiateLogin(loginInput())} 72 class="grow rounded-lg border-[0.5px] border-neutral-300 bg-neutral-100 px-3 py-2 hover:bg-neutral-200 active:bg-neutral-300 dark:border-neutral-600 dark:bg-neutral-800 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 73 > 74 Continue 75 </button> 76 </form> 77 </Show> 78 79 <Show when={scopeFlow.showScopeSelector()}> 80 <ScopeSelector onConfirm={scopeFlow.complete} onCancel={handleCancel} /> 81 </Show> 82 83 <Show when={notice()}> 84 <div class="text-sm">{notice()}</div> 85 </Show> 86 </div> 87 ); 88};