main
tsx 101 lines 2.77 KB
Raw
1 import React, { createContext, useContext, useState, useEffect } from "react";
2 import { debug, error } from "@tauri-apps/plugin-log";
3 import { invoke } from "@tauri-apps/api/core";
4 import { K_STORE_ACCESS_TOKEN, UserInfo } from "../lib/crate/generated";
5 import { storeAccount } from "../lib/store/store";
6
7 type AccountContextType = {
8 loggedIn: boolean;
9 accessToken: string | undefined;
10 user: UserInfo | undefined;
11 provider: undefined | "google" | "apple";
12 onLoggedOut: () => void;
13 onLoggedIn: (provider: "google" | "apple", accessToken: string) => void;
14 };
15
16 const AccountContext = createContext<AccountContextType>({
17 loggedIn: false,
18 accessToken: undefined,
19 user: undefined,
20 provider: undefined,
21 onLoggedOut: () => {},
22 onLoggedIn: (_provider, _accessToken) => {},
23 });
24
25 enum State {
26 INITIALIZING,
27 LOADED,
28 }
29
30 export function AccountProvider({ children }: { children: React.ReactNode }) {
31 const [state, setState] = useState<State>(State.INITIALIZING);
32 const [loggedIn, setLoggedIn] = useState(false);
33 const [accessToken, setAccessToken] = useState<string>();
34 const [user, setUser] = useState<UserInfo>();
35 const [provider, setProvider] = useState<"google" | "apple">();
36
37 const onLoggedOut = () => {
38 setState(State.INITIALIZING);
39 storeAccount().delete(K_STORE_ACCESS_TOKEN);
40 setLoggedIn(false);
41 setAccessToken(undefined);
42 setUser(undefined);
43 setState(State.LOADED);
44 };
45
46 const onLoggedIn = async (
47 provider: "google" | "apple",
48 accessToken: string,
49 ) => {
50 setState(State.INITIALIZING);
51 storeAccount().set(K_STORE_ACCESS_TOKEN, accessToken);
52 const user = await invoke<UserInfo>("get_profile", {
53 accessToken,
54 });
55 debug(`User: ${JSON.stringify(user)}`);
56 setUser(user);
57 setProvider(provider);
58 setLoggedIn(true);
59 setState(State.LOADED);
60 };
61
62 const init = async () => {
63 try {
64 const accessToken =
65 await storeAccount().get<string>(K_STORE_ACCESS_TOKEN);
66 const user = await invoke<UserInfo>("get_profile", {
67 accessToken,
68 });
69 setUser(user);
70 setLoggedIn(!!accessToken);
71 setAccessToken(accessToken);
72 setState(State.LOADED);
73 } catch (e) {
74 // Force log out for now
75 onLoggedOut();
76 error(`AccountProvider: ${e}`);
77 setState(State.LOADED);
78 }
79 };
80
81 useEffect(() => {
82 init();
83 }, []);
84
85 return (
86 <AccountContext.Provider
87 value={{ loggedIn, accessToken, user, onLoggedOut, onLoggedIn, provider }}
88 >
89 {state === State.INITIALIZING && (
90 <div className="flex items-center justify-center min-h-screen">
91 <span>Loading...</span>
92 </div>
93 )}
94 {state === State.LOADED && children}
95 </AccountContext.Provider>
96 );
97 }
98
99 export function useAccount() {
100 return useContext(AccountContext);
101 }