import React, { useMemo, useState, useEffect } from 'react'; import { useApolloClient } from '@apollo/client'; import { isFunction } from 'lodash'; import { context as Context } from './context'; import { AuthContext, User } from './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; }; type SignInMutationResult = { token: string; user: User; }; type SignInMutationVariables = { email: string; staySignedIn: boolean; password: string; }; 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]); useEffect(() => { loadUser(); }, []); const loadUser = async () => { if (tokenStorage.token) { try { const result = await client.query({ query: QUERY_ME, fetchPolicy: 'network-only', }); if (result.data.me) { setUser(result.data.me); } } catch (e) {} } setLoading(false); }; const signIn: AuthContext['signIn'] = async ( email: string, password: string, staySignedIn: boolean, validate?: (user: User) => boolean ) => { const result = await client.mutate< SignInMutationResult, SignInMutationVariables >({ mutation: MUTATION_SIGN_IN, variables: { email, password, staySignedIn, }, }); if (result.data?.user) { if (isFunction(validate) && !validate(result.data?.user)) { return null; } tokenStorage.setToken(result.data.token); setUser(result.data.user); client.writeQuery({ query: QUERY_ME, data: { me: result.data.user, }, }); return result.data.user; } return null; }; const signOut = () => { return client.clearStore().then(() => { tokenStorage.setToken(''); setUser(null); }); }; return ( {props.children} ); }