alternative tangled frontend (extremely wip)

feat: sign out

serenity cc017b67 61c09feb

+75 -4
+24
src/components/Auth/SignOutButton.tsx
··· 1 + import { LucideLogOut } from "@/components/Icons/LucideLogOut"; 2 + import { useOAuth } from "@/lib/oauth"; 3 + import { useNavigate, useRouter } from "@tanstack/react-router"; 4 + 5 + export const SignOutButton = () => { 6 + const { signOut } = useOAuth(); 7 + const navigate = useNavigate(); 8 + const router = useRouter(); 9 + 10 + const handleSignOut = () => { 11 + signOut(); 12 + navigate({ to: "/" }); 13 + }; 14 + 15 + return ( 16 + <button 17 + className="flex cursor-pointer items-center gap-2 pl-2" 18 + onClick={handleSignOut} 19 + > 20 + <LucideLogOut /> 21 + <p className="text-sm">Sign Out</p> 22 + </button> 23 + ); 24 + };
+23
src/components/Icons/LucideLogOut.tsx
··· 1 + import { SVGProps } from "react"; 2 + 3 + export function LucideLogOut(props: SVGProps<SVGSVGElement>) { 4 + return ( 5 + <svg 6 + xmlns="http://www.w3.org/2000/svg" 7 + width="1em" 8 + height="1em" 9 + viewBox="0 0 24 24" 10 + {...props} 11 + > 12 + {/* Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE */} 13 + <path 14 + fill="none" 15 + stroke="currentColor" 16 + strokeLinecap="round" 17 + strokeLinejoin="round" 18 + strokeWidth="2" 19 + d="m16 17l5-5l-5-5m5 5H9m0 9H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" 20 + /> 21 + </svg> 22 + ); 23 + }
+5 -4
src/components/Nav/NavBarAuthed.tsx
··· 1 1 import { UnderlineIconRouterLink } from "@/components/Animated/UnderlineIconRouterLink"; 2 + import { SignOutButton } from "@/components/Auth/SignOutButton"; 2 3 import { DropdownModal } from "@/components/Dropdown/DropdownModal"; 3 4 import { StrandIcon } from "@/components/Icons/Branding/StrandIcon"; 4 5 import { Loading } from "@/components/Icons/Loading"; ··· 37 38 /> 38 39 </div> 39 40 <div className="flex max-h-12 items-center gap-1"> 40 - <button className="cursor-pointer"> 41 + <div> 41 42 {isAvatarLoading ? ( 42 43 <Loading /> 43 44 ) : avatarQueryError ? ( ··· 45 46 ) : ( 46 47 <AvatarWithDropdown uri={avatarUri} /> 47 48 )} 48 - </button> 49 + </div> 49 50 </div> 50 51 </div> 51 52 ); ··· 67 68 return ( 68 69 <DropdownModal 69 70 buttonComponent={AvatarButton} 70 - className="bg-surface0 mt-3.5 w-72 rounded-lg p-2" 71 + className="bg-surface0 mt-3.5 flex w-48 flex-col gap-2 rounded-lg p-2" 71 72 > 72 - <p>Hello</p> 73 + <SignOutButton /> 73 74 </DropdownModal> 74 75 ); 75 76 };
+23
src/lib/oauth/index.tsx
··· 6 6 BrowserOAuthClient, 7 7 OAuthSession, 8 8 } from "@atproto/oauth-client-browser"; 9 + import { useRouter } from "@tanstack/react-router"; 9 10 import { 10 11 createContext, 11 12 ReactNode, ··· 21 22 callbackHandler: (arg0: URLSearchParams) => void; 22 23 handle: string | null; 23 24 setHandle: (arg0: string) => void; 25 + signOut: () => void; 24 26 } | null>(null); 25 27 26 28 export const OAuthProvider = ({ children }: { children: ReactNode }) => { ··· 48 50 [client], 49 51 ); 50 52 53 + // when we move this to a package, remove this. 54 + const router = useRouter(); 55 + 56 + const signOut = () => { 57 + if (!session) 58 + throw new Error( 59 + "No OAuth session found. You should be careful about the ordering of your calls. Ensure that the client and the session is loaded before attempting to call `signOut` on the OAuth provider.", 60 + ); 61 + 62 + session.signOut(); 63 + setSession(null); 64 + }; 65 + 66 + // When we release the package, we need to remove this and replace it with a prop that takes in any effectful function and runs it here. 67 + // For now, we just perform the effect. 68 + 69 + useEffect(() => { 70 + router.invalidate(); 71 + }, [session]); 72 + 51 73 useEffect(() => { 52 74 const oAuthClient = new BrowserOAuthClient({ 53 75 //@ts-expect-error fuck it we ball ··· 88 110 callbackHandler, 89 111 handle, 90 112 setHandle, 113 + signOut, 91 114 }; 92 115 93 116 return <OAuthContext value={contextValue}>{children}</OAuthContext>;