alternative tangled frontend (extremely wip)

feat: basic authed navbar

serenity 7f0a9d3b 17e572f1

+38 -24
+31 -20
src/components/Nav/NavBarAuthed.tsx
··· 1 1 import { UnderlineIconRouterLink } from "@/components/Animated/UnderlineIconRouterLink"; 2 2 import { StrandIcon } from "@/components/Icons/Branding/StrandIcon"; 3 + import { Loading } from "@/components/Icons/Loading"; 3 4 import { LucideLogIn } from "@/components/Icons/LucideLogIn"; 5 + import { useOAuth } from "@/lib/oauth"; 6 + import { useAvatarQuery } from "@/lib/queries/get-avatar"; 7 + import { err } from "@/lib/result"; 4 8 5 9 export const NavBarAuthed = () => { 10 + const oauth = useOAuth(); 11 + const { session } = oauth; 12 + const did = session ? session.did : null; 13 + const repoUrl = session ? new URL(session.server.issuer) : null; 14 + const { 15 + isLoading: isAvatarLoading, 16 + data: avatarQueryData, 17 + error: avatarQueryError, 18 + } = useAvatarQuery({ did, repoUrl }); 19 + 20 + const avatarSrc = avatarQueryData === "#" ? undefined : avatarQueryData; 21 + 6 22 return ( 7 23 <div className="bg-surface0 flex w-full items-center justify-between p-3"> 8 - <div className="flex items-center gap-1"> 24 + <div className="flex max-h-12 items-center gap-1"> 9 25 <UnderlineIconRouterLink 10 26 to="/" 11 27 label="Strand" ··· 16 32 className="text-lg font-semibold" 17 33 /> 18 34 </div> 19 - <div className="flex items-center gap-1"> 20 - <UnderlineIconRouterLink 21 - to="/login" 22 - label="Sign In" 23 - icon={LucideLogIn({})} 24 - iconClassName="text-crust" 25 - labelClassName="text-crust" 26 - underlineClassName="bg-crust" 27 - className="bg-accent rounded-sm p-1.5 pr-3 pl-3" 28 - position="right" 29 - iconTransitions={{ duration: 0.2, ease: "easeInOut" }} 30 - iconVariants={{ 31 - hover: { 32 - x: [0, 10, -10, 0], 33 - opacity: [1, 0, 0, 1], 34 - }, 35 - }} 36 - /> 35 + <div className="flex max-h-12 items-center gap-1"> 36 + <button className="cursor-pointer"> 37 + {isAvatarLoading ? ( 38 + <Loading /> 39 + ) : avatarQueryError ? ( 40 + <p>{avatarQueryError.message}</p> 41 + ) : ( 42 + <img 43 + src={avatarSrc} 44 + className="border-accent h-10 rounded-full border transition-all hover:border-2" 45 + /> 46 + )} 47 + </button> 37 48 </div> 38 49 </div> 39 50 ); 40 - } 51 + };
+7 -4
src/lib/queries/get-avatar.ts
··· 130 130 return ok(bskyProfile); 131 131 }; 132 132 133 - export const avatarQueryKey = (did: string) => ["avatar", did]; 133 + export const avatarQueryKey = (did: string | null) => ["avatar", did]; 134 134 135 135 /** 136 136 * Hook that wraps useQuery with the query function being the getAvatar query. See below for the query function definition itself. ··· 145 145 did, 146 146 repoUrl, 147 147 }: { 148 - did: string; 149 - repoUrl: URL; 148 + did: string | null; 149 + repoUrl: URL | null; 150 150 }) => { 151 151 return useQuery({ 152 152 queryKey: avatarQueryKey(did), 153 - queryFn: () => getAvatar({ did, repoUrl }), 153 + queryFn: () => { 154 + if (!did || !repoUrl) return ""; 155 + return getAvatar({ did, repoUrl }); 156 + }, 154 157 }); 155 158 };