import { ReactNode, createContext, useEffect } from 'react' import { User } from 'common/user' import { onIdTokenChanged } from 'firebase/auth' import { auth, listenForUser, getUser, setCachedReferralInfoForUser, } from 'web/lib/firebase/users' import { deleteTokenCookies, setTokenCookies } from 'web/lib/firebase/auth' import { createUser } from 'web/lib/firebase/api' import { randomString } from 'common/util/random' import { identifyUser, setUserProperty } from 'web/lib/service/analytics' import { useStateCheckEquality } from 'web/hooks/use-state-check-equality' // Either we haven't looked up the logged in user yet (undefined), or we know // the user is not logged in (null), or we know the user is logged in (User). type AuthUser = undefined | null | User const CACHED_USER_KEY = 'CACHED_USER_KEY' const ensureDeviceToken = () => { let deviceToken = localStorage.getItem('device-token') if (!deviceToken) { deviceToken = randomString() localStorage.setItem('device-token', deviceToken) } return deviceToken } export const AuthContext = createContext(undefined) export function AuthProvider(props: { children: ReactNode serverUser?: AuthUser }) { const { children, serverUser } = props const [authUser, setAuthUser] = useStateCheckEquality(serverUser) useEffect(() => { if (serverUser === undefined) { const cachedUser = localStorage.getItem(CACHED_USER_KEY) setAuthUser(cachedUser && JSON.parse(cachedUser)) } }, [setAuthUser, serverUser]) useEffect(() => { return onIdTokenChanged(auth, async (fbUser) => { if (fbUser) { setTokenCookies({ id: await fbUser.getIdToken(), refresh: fbUser.refreshToken, }) let user = await getUser(fbUser.uid) if (!user) { const deviceToken = ensureDeviceToken() user = (await createUser({ deviceToken })) as User } setAuthUser(user) // Persist to local storage, to reduce login blink next time. // Note: Cap on localStorage size is ~5mb localStorage.setItem(CACHED_USER_KEY, JSON.stringify(user)) setCachedReferralInfoForUser(user) } else { // User logged out; reset to null deleteTokenCookies() setAuthUser(null) localStorage.removeItem(CACHED_USER_KEY) } }) }, [setAuthUser]) const authUserId = authUser?.id const authUsername = authUser?.username useEffect(() => { if (authUserId && authUsername) { identifyUser(authUserId) setUserProperty('username', authUsername) return listenForUser(authUserId, setAuthUser) } }, [authUserId, authUsername, setAuthUser]) return ( {children} ) }