main
tsx 102 lines 2.78 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 { STORE_ACCESS_TOKEN_KEY, storeAccount } from "../stores/store";
5 import { UserInfo } from "../lib/crate/generated";
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(STORE_ACCESS_TOKEN_KEY);
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(STORE_ACCESS_TOKEN_KEY, 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 = await storeAccount().get<string>(
65 STORE_ACCESS_TOKEN_KEY,
66 );
67 const user = await invoke<UserInfo>("get_profile", {
68 accessToken,
69 });
70 setUser(user);
71 setLoggedIn(!!accessToken);
72 setAccessToken(accessToken);
73 setState(State.LOADED);
74 } catch (e) {
75 // Force log out for now
76 onLoggedOut();
77 error(`AccountProvider: ${e}`);
78 setState(State.LOADED);
79 }
80 };
81
82 useEffect(() => {
83 init();
84 }, []);
85
86 return (
87 <AccountContext.Provider
88 value={{ loggedIn, accessToken, user, onLoggedOut, onLoggedIn, provider }}
89 >
90 {state === State.INITIALIZING && (
91 <div className="flex items-center justify-center min-h-screen">
92 <span>Loading...</span>
93 </div>
94 )}
95 {state === State.LOADED && children}
96 </AccountContext.Provider>
97 );
98 }
99
100 export function useAccount() {
101 return useContext(AccountContext);
102 }