minimal streamplace frontend
at main 98 lines 3.7 kB view raw
1import { Show, createEffect, createSignal, onCleanup } from "solid-js"; 2 3import { signIn } from "../auth/login"; 4import { showLoginModal, setShowLoginModal } from "../auth/login-modal"; 5import { signOut } from "../auth/session-manager"; 6import { agent, loggedInHandle } from "../auth/state"; 7 8export function LoginButton() { 9 const [handle, setHandle] = createSignal(""); 10 const [loading, setLoading] = createSignal(false); 11 12 const handleSignIn = async () => { 13 const h = handle().trim(); 14 if (!h) return; 15 setLoading(true); 16 try { 17 await signIn(h); 18 } catch (err) { 19 console.error("Sign in failed:", err); 20 setLoading(false); 21 } 22 }; 23 24 const handleKeyDown = (e: KeyboardEvent) => { 25 if (e.key === "Enter") handleSignIn(); 26 }; 27 28 createEffect(() => { 29 if (!showLoginModal()) return; 30 const onEsc = (e: KeyboardEvent) => { 31 if (e.key === "Escape") setShowLoginModal(false); 32 }; 33 document.addEventListener("keydown", onEsc); 34 onCleanup(() => document.removeEventListener("keydown", onEsc)); 35 }); 36 37 return ( 38 <div class="flex items-center gap-3"> 39 <Show 40 when={agent()} 41 fallback={ 42 <> 43 <button 44 class="bg-sp-accent text-sp-bg hover:bg-sp-accent/80 rounded-sm border border-transparent px-3 py-1.5 text-sm font-medium transition-colors" 45 onClick={() => setShowLoginModal(true)} 46 > 47 Sign in 48 </button> 49 <Show when={showLoginModal()}> 50 <div 51 class="fixed inset-0 z-50 flex items-center justify-center bg-black/60" 52 onClick={(e) => { 53 if (e.target === e.currentTarget) setShowLoginModal(false); 54 }} 55 > 56 <div class="bg-sp-surface border-sp-border mx-4 flex w-full max-w-md flex-col gap-4 rounded-lg border p-5 shadow-lg"> 57 <h2 class="text-sp-text text-lg font-semibold">Sign in</h2> 58 <input 59 type="text" 60 placeholder="handle.bsky.social" 61 class="border-sp-border bg-sp-bg text-sp-text placeholder:text-sp-dim focus:border-sp-accent rounded-sm border px-3 py-1.5 text-sm focus:outline-none" 62 value={handle()} 63 onInput={(e) => setHandle(e.currentTarget.value)} 64 onKeyDown={handleKeyDown} 65 ref={(el: HTMLInputElement) => setTimeout(() => el.focus())} 66 /> 67 <div class="flex justify-end gap-2"> 68 <button 69 class="text-sp-dim hover:text-sp-text rounded-sm px-3 py-1.5 text-sm transition-colors" 70 onClick={() => setShowLoginModal(false)} 71 > 72 Cancel 73 </button> 74 <button 75 class="bg-sp-accent text-sp-bg hover:bg-sp-accent/80 rounded-sm px-3 py-1.5 text-sm font-medium transition-colors disabled:opacity-50" 76 onClick={handleSignIn} 77 disabled={loading() || !handle().trim()} 78 > 79 {loading() ? "..." : "Go"} 80 </button> 81 </div> 82 </div> 83 </div> 84 </Show> 85 </> 86 } 87 > 88 <span class="text-sp-dim hidden text-sm sm:inline">@{loggedInHandle() || "..."}</span> 89 <button 90 class="border-sp-border text-sp-dim hover:border-sp-red hover:text-sp-red rounded-sm border px-3 py-1.5 text-sm transition-colors" 91 onClick={() => signOut()} 92 > 93 Sign out 94 </button> 95 </Show> 96 </div> 97 ); 98}