creates video voice memos from audio clips; with bluesky integration. trill.ptr.pet
at main 3.4 kB view raw
1import { 2 configureOAuth, 3 defaultIdentityResolver, 4 createAuthorizationUrl, 5 finalizeAuthorization, 6 OAuthUserAgent, 7 getSession, 8 deleteStoredSession, 9} from "@atcute/oauth-browser-client"; 10 11import { 12 CompositeDidDocumentResolver, 13 PlcDidDocumentResolver, 14 WebDidDocumentResolver, 15} from "@atcute/identity-resolver"; 16import type { ActorIdentifier } from "@atcute/lexicons"; 17import type { AtprotoDid } from "@atcute/lexicons/syntax"; 18import { handleResolver, login } from "./at"; 19import { loggingIn } from "./accounts"; 20import { clientId, redirectUri, scope } from "./oauthMetadata"; 21 22const setupOAuth = () => { 23 const metadata = { 24 client_id: clientId, 25 redirect_uri: redirectUri, 26 }; 27 console.log(metadata); 28 configureOAuth({ 29 metadata, 30 identityResolver: defaultIdentityResolver({ 31 handleResolver, 32 33 didDocumentResolver: new CompositeDidDocumentResolver({ 34 methods: { 35 plc: new PlcDidDocumentResolver(), 36 web: new WebDidDocumentResolver(), 37 }, 38 }), 39 }), 40 }); 41}; 42setupOAuth(); 43 44export const sessions = { 45 get: async (did: AtprotoDid) => { 46 const session = await getSession(did, { allowStale: true }); 47 return new OAuthUserAgent(session); 48 }, 49 remove: async (did: AtprotoDid) => { 50 try { 51 const agent = await sessions.get(did); 52 await agent.signOut(); 53 } catch { 54 deleteStoredSession(did); 55 } 56 }, 57}; 58 59export const flow = { 60 start: async (identifier: ActorIdentifier): Promise<void> => { 61 const authUrl = await createAuthorizationUrl({ 62 target: { type: "account", identifier }, 63 scope, 64 }); 65 // recommended to wait for the browser to persist local storage before proceeding 66 await new Promise((resolve) => setTimeout(resolve, 200)); 67 // redirect the user to sign in and authorize the app 68 window.location.assign(authUrl); 69 // if this is on an async function, ideally the function should never ever resolve. 70 // the only way it should resolve at this point is if the user aborted the authorization 71 // by returning back to this page (thanks to back-forward page caching) 72 await new Promise((_resolve, reject) => { 73 const listener = () => { 74 reject(new Error(`user aborted the login request`)); 75 }; 76 window.addEventListener("pageshow", listener, { once: true }); 77 }); 78 }, 79 finalize: async (url: URL): Promise<OAuthUserAgent | null> => { 80 // createAuthorizationUrl asks server to put the params in the hash 81 const params = new URLSearchParams(url.hash.slice(1)); 82 if (!params.has("code")) return null; 83 const { session } = await finalizeAuthorization(params); 84 return new OAuthUserAgent(session); 85 }, 86}; 87 88export const tryFinalizeLogin = async () => { 89 const did = loggingIn.get(); 90 if (!did) return; 91 92 const currentUrl = new URL(window.location.href); 93 // scrub history so auth state cant be replayed 94 try { 95 history.replaceState(null, "", "/"); 96 } catch { 97 // if router was unitialized then we probably dont need to scrub anyway 98 // so its fine 99 } 100 101 loggingIn.set(undefined); 102 await sessions.remove(did); 103 const agent = await flow.finalize(currentUrl); 104 if (!agent) throw "no session was logged into?"; 105 106 return await login(agent); 107}; 108 109export const getSessionClient = async (did: AtprotoDid) => { 110 const session = await sessions.get(did); 111 if (!session) throw `no session found for ${did}`; 112 return await login(session); 113};