manifold/web/components/auth-context.tsx
Marshall Polaris 5649161348
Pass page props user to auth provider if present (#724)
* Pass page props user to auth provider if present

* Rename `user` -> `serverUser`

* Don't load from local storage if server told us a user
2022-08-08 22:42:52 -07:00

86 lines
2.7 KiB
TypeScript

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<AuthUser>(undefined)
export function AuthProvider(props: {
children: ReactNode
serverUser?: AuthUser
}) {
const { children, serverUser } = props
const [authUser, setAuthUser] = useStateCheckEquality<AuthUser>(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 (
<AuthContext.Provider value={authUser}>{children}</AuthContext.Provider>
)
}