1import * as React from 'react';
2
3import { setSessionAsync } from '../apiClient';
4import { getUserProfileAsync, UserAccount, UserData } from '../functions/getUserProfileAsync';
5import { restoreUserAsync } from '../functions/restoreUserAsync';
6import { startAuthSessionAsync } from '../functions/startAuthSessionAsync';
7import { useIsMounted } from '../hooks/useIsMounted';
8import { resetBranchQueries } from '../queries/useBranchesForApp';
9
10type UserContext = {
11  userData?: UserData;
12  isAuthenticated: boolean;
13  selectedAccount?: UserAccount;
14};
15
16type UserActionsContext = {
17  login: (loginType: 'signup' | 'login') => Promise<void>;
18  logout: () => Promise<void>;
19  restore: () => Promise<void>;
20  setUserData: (userData: UserData) => void;
21  setSelectedAccount: (accountId: string) => void;
22};
23
24const ActionsContext = React.createContext<UserActionsContext | null>(null);
25const Context = React.createContext<UserContext>(null);
26
27type UserContextProviderProps = {
28  children: React.ReactNode;
29  initialUserData?: UserData;
30};
31
32export function UserContextProvider({ children, initialUserData }: UserContextProviderProps) {
33  const [userData, setUserData] = React.useState<UserData | undefined>(initialUserData);
34  const [selectedAccountId, setSelectedAccount] = React.useState<string>(
35    initialUserData?.accounts[0]?.id ?? ''
36  );
37  const isMounted = useIsMounted();
38
39  const selectedAccount = userData?.accounts.find((account) => account.id === selectedAccountId);
40
41  async function login(type: 'signup' | 'login') {
42    const sessionSecret = await startAuthSessionAsync(type).catch((cancelled) => {});
43
44    if (sessionSecret) {
45      await setSessionAsync(sessionSecret);
46      const userData = await getUserProfileAsync();
47
48      if (isMounted()) {
49        setUserData(userData);
50        setSelectedAccount(userData.accounts[0].id);
51      }
52    } else {
53      await clearSession();
54    }
55  }
56
57  async function logout() {
58    const result = await clearSession();
59    await resetBranchQueries();
60    return result;
61  }
62
63  async function restore() {
64    const userData = await restoreUserAsync();
65
66    if (!userData) {
67      clearSession();
68    } else {
69      if (isMounted()) {
70        setUserData(userData);
71        setSelectedAccount(userData.accounts[0].id);
72      }
73    }
74  }
75
76  async function clearSession() {
77    if (isMounted()) {
78      setUserData(undefined);
79      setSelectedAccount(undefined);
80    }
81
82    return await setSessionAsync(null);
83  }
84
85  const actions = React.useMemo(() => {
86    return {
87      login,
88      logout,
89      restore,
90      setUserData,
91      setSelectedAccount,
92    };
93  }, []);
94
95  return (
96    <ActionsContext.Provider value={actions}>
97      <Context.Provider value={{ userData, selectedAccount, isAuthenticated: userData != null }}>
98        {children}
99      </Context.Provider>
100    </ActionsContext.Provider>
101  );
102}
103
104export const useUser = () => React.useContext<UserContext>(Context);
105export const useUserActions = () => React.useContext(ActionsContext);
106