BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
at main 112 lines 3.9 kB view raw
1import { useAppSession } from "$/contexts/app-session"; 2import type { AccountSummary, ActiveSession } from "$/lib/types"; 3import { createMemo, Show } from "solid-js"; 4import { Presence } from "solid-motionone"; 5import { AvatarBadge } from "./AvatarBadge"; 6import { ProfileSkeleton } from "./ProfileSkeleton"; 7import { ReauthBanner } from "./ReauthBanner"; 8 9export function SessionEmptyState() { 10 return ( 11 <div class="grid"> 12 <h2 class="m-0 text-[clamp(1.4rem,2vw,1.85rem)] leading-[1.08] tracking-[-0.03em]">No account connected yet.</h2> 13 <p class="m-0 text-xs leading-[1.55] text-on-surface-variant">Connect your Bluesky account to start exploring.</p> 14 </div> 15 ); 16} 17 18function SessionExpiredState(props: { account: AccountSummary }) { 19 return ( 20 <div class="flex items-center gap-4 [align-content:start] grid-cols-[auto_minmax(0,1fr)]"> 21 <AvatarBadge label={props.account.handle || props.account.did} src={props.account.avatar} tone="muted" /> 22 <div class="grid"> 23 <h2 class="m-0 text-[clamp(1.3rem,2vw,1.7rem)] tracking-[-0.02em]"> 24 {props.account.handle || props.account.did} 25 </h2> 26 <p class="m-0 text-xs text-on-surface-variant">Stored account</p> 27 <p class="m-0 text-xs text-on-surface-variant">PDS: {props.account.pdsUrl || "PDS unavailable"}</p> 28 </div> 29 </div> 30 ); 31} 32 33function SessionProfile(props: { session: ActiveSession; activeAccount: AccountSummary | null }) { 34 return ( 35 <div class="grid items-center gap-4 [align-content:start] grid-cols-[auto_minmax(0,1fr)]"> 36 <AvatarBadge label={props.session.handle} src={props.activeAccount?.avatar} tone="primary" /> 37 <div class="grid"> 38 <h2 class="m-0 text-[clamp(1.3rem,2vw,1.7rem)] tracking-[-0.02em]">{props.session.handle}</h2> 39 <p class="m-0 text-xs text-on-surface-variant">{props.session.did}</p> 40 </div> 41 <Show when={props.activeAccount}> 42 {(account) => <p class="m-0 text-xs text-on-surface-variant">{account().pdsUrl || "PDS unavailable"}</p>} 43 </Show> 44 </div> 45 ); 46} 47 48export function SessionSpotlight() { 49 const session = useAppSession(); 50 const displayAccount = createMemo(() => 51 session.activeAccount ?? (session.reauthNeeded ? session.primaryAccount : null) 52 ); 53 const label = createMemo(() => { 54 if (session.bootstrapping) { 55 return "Reconnecting"; 56 } 57 58 if (session.activeSession) { 59 return "Connected"; 60 } 61 62 if (session.reauthNeeded && displayAccount()) { 63 return "Expired"; 64 } 65 66 return "Ready"; 67 }); 68 69 return ( 70 <article class="panel-surface grid gap-5 p-5"> 71 <div class="flex items-baseline justify-between gap-3"> 72 <p class="overline-copy text-[0.75rem] text-on-surface-variant">Your account</p> 73 <p class="overline-copy text-[0.68rem] text-on-surface-variant">{label()}</p> 74 </div> 75 76 <SessionBody 77 activeAccount={displayAccount()} 78 activeSession={session.activeSession} 79 bootstrapping={session.bootstrapping} 80 reauthNeeded={session.reauthNeeded} /> 81 82 <Presence> 83 <Show when={session.reauthNeeded}> 84 <ReauthBanner onReauth={() => void session.reauthorizePrimaryAccount()} /> 85 </Show> 86 </Presence> 87 </article> 88 ); 89} 90 91function SessionBody( 92 props: { 93 activeSession: ActiveSession | null; 94 activeAccount: AccountSummary | null; 95 bootstrapping: boolean; 96 reauthNeeded: boolean; 97 }, 98) { 99 return ( 100 <Show when={!props.bootstrapping} fallback={<ProfileSkeleton />}> 101 <Show 102 when={props.activeSession} 103 fallback={ 104 <Show when={props.reauthNeeded && props.activeAccount} fallback={<SessionEmptyState />}> 105 {(account) => <SessionExpiredState account={account()} />} 106 </Show> 107 }> 108 {(currentSession) => <SessionProfile session={currentSession()} activeAccount={props.activeAccount} />} 109 </Show> 110 </Show> 111 ); 112}