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