import { useMemo, useState, useEffect, useCallback } from 'react'; import { useApolloClient } from '@apollo/client'; import { isFunction } from 'lodash'; import { context as Context } from './context'; import { AuthContext, User } from './types'; import { MutationSignInArgs, Mutation } from 'libs/graphql/types'; import TokenStorage from '../tokenstorage/TokenStorage'; import { QUERY_ME } from './queries'; import { MUTATION_SIGN_IN } from './mutations'; export interface AuthProviderProps { tokenStorage?: TokenStorage; children?: React.ReactNode; } type MeQueryResult = { me: User | null; }; export function AuthProvider(props: AuthProviderProps) { const client = useApolloClient(); const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const tokenStorage = useMemo(() => { if (props.tokenStorage) { return props.tokenStorage; } return new TokenStorage(); }, [props.tokenStorage]); const loadUser = useCallback(async () => { if (!tokenStorage.token) { return; } try { const result = await client.query({ query: QUERY_ME, fetchPolicy: 'network-only', }); if (result.data.me) { setUser(result.data.me); } } catch (e) {} }, [setUser, client, tokenStorage]); useEffect(() => { loadUser().finally(() => { setLoading(false); }); }, [loadUser, setLoading]); const signIn: AuthContext['signIn'] = async ( email: string, password: string, staySignedIn: boolean, validate?: (user: User) => boolean ) => { const result = await client.mutate< Pick, MutationSignInArgs >({ mutation: MUTATION_SIGN_IN, variables: { email, password, staySignedIn, }, }); if (!result.data?.signIn?.user) { return null; } if (isFunction(validate) && !validate(result.data.signIn.user)) { return null; } tokenStorage.setToken(result.data.signIn.token); setUser(result.data.signIn.user); client.writeQuery({ query: QUERY_ME, data: { me: result.data.signIn.user, }, }); return result.data.signIn.user; }; const signOut = () => { return client.clearStore().then(() => { tokenStorage.setToken(''); setUser(null); }); }; return ( {props.children} ); }