import { oAuthClient, type TypedExpoOAuthClientInstance, } from "@/lib/utils/atproto/oauth"; import { isDevMode } from "@/lib/utils/env"; import { Agent } from "@atproto/api"; import type { OAuthSession } from "@atproto/oauth-client"; import type { Dispatch, ReactNode, SetStateAction } from "react"; import { createContext, useContext, useEffect, useState } from "react"; export interface OAuthContextValue { session?: OAuthSession; agent?: Agent; client: TypedExpoOAuthClientInstance; isLoading: boolean; } const OAuthContext = createContext< [OAuthContextValue, Dispatch>] | null >(null); export const useOAuthValue = () => { const value = useContext(OAuthContext); if (!value) throw new Error( "OAuth provider failed to initialise. Did you access this out of tree somehow? Tried to access OAuth value before it was initialised.", ); return value[0]; }; export const useOAuthSetter = () => { const value = useContext(OAuthContext); if (!value) throw new Error( "OAuth provider failed to initialise. Did you access this out of tree somehow? Tried to access OAuth value before it was initialised.", ); return value[1]; }; export const useOAuthSession = () => { const { session } = useOAuthValue(); return session; }; export const useOAuthAgent = () => { const { agent } = useOAuthValue(); return agent; }; export const useOAuthAgentGuaranteed = () => { const { agent } = useOAuthValue(); if (!agent) throw new Error( "Tried to access OAuth agent before it was created. Ensure that you are calling useOAuthAgentGuaranteed *after* you have a valid OAuth session.", ); return agent; }; export const useOAuthClient = () => { const { client } = useOAuthValue(); return client; }; export const useOAuthSessionGuaranteed = () => { const { session } = useOAuthValue(); if (!session) throw new Error( "Tried to access OAuth session before it was created. Ensure that you are calling useOAuthSessionGuaranteed *after* you have a valid OAuth session.", ); return session; }; export const OAuthProvider = ({ children }: { children: ReactNode }) => { const providedOAuthClient = oAuthClient; const [oAuth, setOAuth] = useState({ client: providedOAuthClient, isLoading: true, }); useEffect(() => { const initOAuth = async () => { try { const result = await providedOAuthClient.init(); if (result) { const { session, state } = result; if (isDevMode) { console.log("session state:", state); if (state != null) { console.log( `${session.sub} was successfully authenticated (state: ${state})`, ); } else { console.log( `${session.sub} was restored (last active session), token_endpoint: ${session.serverMetadata.token_endpoint}`, ); } } const agent = new Agent(session); setOAuth({ session, agent, client: providedOAuthClient, isLoading: false, }); return; } setOAuth((o) => ({ ...o, isLoading: false, })); return; } catch (err: unknown) { console.error( "something went wrong when trying to init the oauth client.", ); console.error(err); } }; console.log("initOAuth start"); initOAuth() .then(() => { console.log("initOAuth end"); }) .catch((err: unknown) => { console.error( "something went wrong when trying to init the oauth client.", ); console.error(err); }); if (isDevMode) { console.log("finished initialising oauth client"); } }, [providedOAuthClient]); return {children}; };