pleroma-like client for Bluesky pl.hexmani.ac
bluesky pleroma social-media
at main 4.3 kB view raw
1import { Did, isHandle } from "@atcute/lexicons/syntax"; 2import { 3 configureOAuth, 4 createAuthorizationUrl, 5 deleteStoredSession, 6 finalizeAuthorization, 7 getSession, 8 OAuthUserAgent, 9 resolveFromIdentity, 10 resolveFromService, 11 Session, 12} from "@atcute/oauth-browser-client"; 13import { Component, createSignal } from "solid-js"; 14import Container from "./container"; 15 16configureOAuth({ 17 metadata: { 18 client_id: import.meta.env.VITE_OAUTH_CLIENT_ID, 19 redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URL, 20 }, 21}); 22 23export const [loginState, setLoginState] = createSignal(false); 24let agent: OAuthUserAgent; 25 26const Login: Component = () => { 27 const [notice, setNotice] = createSignal(""); 28 const [loginInput, setLoginInput] = createSignal(""); 29 30 const login = async (handle: string) => { 31 try { 32 if (!handle) return; 33 let resolved; 34 document.querySelector(".submitInfo")!.removeAttribute("hidden"); 35 document 36 .querySelector('button[type="submit"]')! 37 .setAttribute("disabled", ""); 38 if (!isHandle(handle)) { 39 setNotice(`Resolving your service...`); 40 resolved = await resolveFromService(handle); 41 } else { 42 setNotice(`Resolving your identity...`); 43 resolved = await resolveFromIdentity(handle); 44 } 45 46 setNotice(`Contacting your data server...`); 47 const authUrl = await createAuthorizationUrl({ 48 scope: import.meta.env.VITE_OAUTH_SCOPE, 49 ...resolved, 50 }); 51 52 setNotice(`Redirecting...`); 53 await new Promise((resolve) => setTimeout(resolve, 500)); 54 55 location.assign(authUrl); 56 } catch (e: unknown) { 57 if (e instanceof Error) { 58 console.error(e); 59 setNotice(`${e.message}`); 60 } else { 61 console.error(e); 62 setNotice(`Unknown error, check console ¯\\_(ツ)_/¯`); 63 } 64 } finally { 65 document 66 .querySelector('button[type="submit"]')! 67 .removeAttribute("disabled"); 68 } 69 }; 70 71 return ( 72 <> 73 <Container 74 title="Log in" 75 children={ 76 <> 77 <div class="login"> 78 <form name="login" id="login" onclick={(e) => e.preventDefault()}> 79 <label for="handle">Handle</label> 80 <br /> 81 <input 82 type="text" 83 id="handle" 84 name="handle" 85 maxlength="255" 86 placeholder="soykaf.com" 87 onInput={(e) => setLoginInput(e.currentTarget.value)} 88 required 89 /> 90 <br /> 91 <button type="submit" onclick={() => login(loginInput())}> 92 Login 93 </button> 94 </form> 95 <p class="submitInfo" hidden> 96 {notice()} 97 </p> 98 </div> 99 </> 100 } 101 /> 102 </> 103 ); 104}; 105 106const retrieveSession = async (): Promise<void> => { 107 const init = async (): Promise<Session | undefined> => { 108 const params = new URLSearchParams(location.hash.slice(1)); 109 110 if (params.has("state") && (params.has("code") || params.has("error"))) { 111 history.replaceState(null, "", location.pathname + location.search); 112 113 const session = await finalizeAuthorization(params); 114 console.log("Finalizing authorization...", session); 115 const agent = new OAuthUserAgent(session); 116 console.log(await agent.getSession()); 117 const did = session.info.sub; 118 119 localStorage.setItem("currentUser", did); 120 return session; 121 } else { 122 const currentUser = localStorage.getItem("currentUser"); 123 124 if (currentUser) { 125 try { 126 console.log("Retrieving session..."); 127 return await getSession(currentUser as Did); 128 } catch (err) { 129 deleteStoredSession(currentUser as Did); 130 localStorage.removeItem("currentUser"); 131 throw err; 132 } 133 } 134 } 135 }; 136 137 const session = await init().catch(() => {}); 138 139 if (session) { 140 console.log("Retrieved session!", session); 141 agent = new OAuthUserAgent(session); 142 setLoginState(true); 143 } 144}; 145 146const killSession = async (): Promise<void> => { 147 await agent.signOut(); 148 setLoginState(false); 149 localStorage.removeItem("currentUser"); 150 location.href = "/"; 151}; 152 153export { agent, killSession, Login, retrieveSession };