creates video voice memos from audio clips; with bluesky integration.
trill.ptr.pet
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};