frontend client for gemstone. decentralised workplace app
1import {
2 oAuthClient,
3 type TypedExpoOAuthClientInstance,
4} from "@/lib/utils/atproto/oauth";
5import { isDevMode } from "@/lib/utils/env";
6import { Agent } from "@atproto/api";
7import type { OAuthSession } from "@atproto/oauth-client";
8import type { Dispatch, ReactNode, SetStateAction } from "react";
9import { createContext, useContext, useEffect, useState } from "react";
10
11export interface OAuthContextValue {
12 session?: OAuthSession;
13 agent?: Agent;
14 client: TypedExpoOAuthClientInstance;
15 isLoading: boolean;
16}
17
18const OAuthContext = createContext<
19 [OAuthContextValue, Dispatch<SetStateAction<OAuthContextValue>>] | null
20>(null);
21
22export const useOAuthValue = () => {
23 const value = useContext(OAuthContext);
24 if (!value)
25 throw new Error(
26 "OAuth provider failed to initialise. Did you access this out of tree somehow? Tried to access OAuth value before it was initialised.",
27 );
28 return value[0];
29};
30
31export const useOAuthSetter = () => {
32 const value = useContext(OAuthContext);
33 if (!value)
34 throw new Error(
35 "OAuth provider failed to initialise. Did you access this out of tree somehow? Tried to access OAuth value before it was initialised.",
36 );
37 return value[1];
38};
39
40export const useOAuthSession = () => {
41 const { session } = useOAuthValue();
42 return session;
43};
44
45export const useOAuthAgent = () => {
46 const { agent } = useOAuthValue();
47 return agent;
48};
49
50export const useOAuthAgentGuaranteed = () => {
51 const { agent } = useOAuthValue();
52 if (!agent)
53 throw new Error(
54 "Tried to access OAuth agent before it was created. Ensure that you are calling useOAuthAgentGuaranteed *after* you have a valid OAuth session.",
55 );
56 return agent;
57};
58
59export const useOAuthClient = () => {
60 const { client } = useOAuthValue();
61 return client;
62};
63
64export const useOAuthSessionGuaranteed = () => {
65 const { session } = useOAuthValue();
66 if (!session)
67 throw new Error(
68 "Tried to access OAuth session before it was created. Ensure that you are calling useOAuthSessionGuaranteed *after* you have a valid OAuth session.",
69 );
70 return session;
71};
72
73export const OAuthProvider = ({ children }: { children: ReactNode }) => {
74 const providedOAuthClient = oAuthClient;
75 const [oAuth, setOAuth] = useState<OAuthContextValue>({
76 client: providedOAuthClient,
77 isLoading: true,
78 });
79
80 useEffect(() => {
81 const initOAuth = async () => {
82 try {
83 const result = await providedOAuthClient.init();
84 if (result) {
85 const { session, state } = result;
86
87 if (isDevMode) {
88 console.log("session state:", state);
89 if (state != null) {
90 console.log(
91 `${session.sub} was successfully authenticated (state: ${state})`,
92 );
93 } else {
94 console.log(
95 `${session.sub} was restored (last active session), token_endpoint: ${session.serverMetadata.token_endpoint}`,
96 );
97 }
98 }
99 const agent = new Agent(session);
100 setOAuth({
101 session,
102 agent,
103 client: providedOAuthClient,
104 isLoading: false,
105 });
106 return;
107 }
108 setOAuth((o) => ({
109 ...o,
110 isLoading: false,
111 }));
112 return;
113 } catch (err: unknown) {
114 console.error(
115 "something went wrong when trying to init the oauth client.",
116 );
117 console.error(err);
118 }
119 };
120
121 console.log("initOAuth start");
122
123 initOAuth()
124 .then(() => {
125 console.log("initOAuth end");
126 })
127 .catch((err: unknown) => {
128 console.error(
129 "something went wrong when trying to init the oauth client.",
130 );
131 console.error(err);
132 });
133
134 if (isDevMode) {
135 console.log("finished initialising oauth client");
136 }
137 }, [providedOAuthClient]);
138
139 return <OAuthContext value={[oAuth, setOAuth]}>{children}</OAuthContext>;
140};