import React, { useEffect, useMemo, useState } from "react"; import { initializeApp } from "@firebase/app"; import { getAuth, GithubAuthProvider, signInWithCredential, signInWithPopup, signOut, } from "@firebase/auth"; import config from "../config"; import useTokenStore from "../hooks/useTokenStore"; import { serializeError } from "serialize-error"; interface User { displayName: string | null; email: string | null; } interface ContextValue { signIn: () => void; signOut: () => void; clearError: () => void; user: User | null; error: string | null; } const defaultValue = { signIn: () => {}, signOut: () => {}, clearError: () => {}, user: null, error: null, }; export const FirebaseContext = React.createContext(defaultValue); interface Props { children: React.ReactNode; } const FirebaseProvider: React.FC = ({ children }) => { const [user, setUser] = useState(null); const [error, setError] = useState(null); const app = initializeApp(config.firebase); const auth = getAuth(app); const provider = new GithubAuthProvider(); // See https://developer.github.com/v3/oauth/#scopes provider.addScope("repo"); const [token, setToken, deleteToken] = useTokenStore(); // Check if the stored token is still valid. useEffect(() => { if (user || !token) { return; } const signIn = async () => { try { const credential = GithubAuthProvider.credential(token); const res = await signInWithCredential(auth, credential); setUser(res.user.providerData[0]); } catch (err) { deleteToken(); } }; signIn(); }, [user, token]); const value = useMemo( () => ({ user, error, signIn: async () => { if (user) { return; } try { const res = await signInWithPopup(auth, provider); const credential = GithubAuthProvider.credentialFromResult(res); if (!credential?.accessToken) { return; } setToken(credential.accessToken); setUser(res.user.providerData[0]); } catch (err) { const { message } = serializeError(err); setError( message || "Something went wrong during the OAuth authentication flow." ); } }, signOut: async () => { deleteToken(); setUser(null); await signOut(auth); }, clearError: () => { setError(null); }, }), [user, error] ); return ( {children} ); }; export default FirebaseProvider;