Make AuthContext
track the private user doc
This commit is contained in:
parent
0e4b317322
commit
50aff668dd
|
@ -1,10 +1,11 @@
|
|||
import { ReactNode, createContext, useEffect } from 'react'
|
||||
import { User } from 'common/user'
|
||||
import { User, PrivateUser } from 'common/user'
|
||||
import { onIdTokenChanged } from 'firebase/auth'
|
||||
import {
|
||||
auth,
|
||||
listenForUser,
|
||||
getUser,
|
||||
listenForPrivateUser,
|
||||
getUserAndPrivateUser,
|
||||
setCachedReferralInfoForUser,
|
||||
} from 'web/lib/firebase/users'
|
||||
import { deleteTokenCookies, setTokenCookies } from 'web/lib/firebase/auth'
|
||||
|
@ -13,11 +14,13 @@ 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
|
||||
type UserAndPrivateUser = { user: User; privateUser: PrivateUser }
|
||||
|
||||
const CACHED_USER_KEY = 'CACHED_USER_KEY'
|
||||
// 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.
|
||||
type AuthUser = undefined | null | UserAndPrivateUser
|
||||
|
||||
const CACHED_USER_KEY = 'CACHED_USER_KEY_V2'
|
||||
|
||||
const ensureDeviceToken = () => {
|
||||
let deviceToken = localStorage.getItem('device-token')
|
||||
|
@ -36,6 +39,7 @@ export function AuthProvider(props: {
|
|||
}) {
|
||||
const { children, serverUser } = props
|
||||
const [authUser, setAuthUser] = useStateCheckEquality<AuthUser>(serverUser)
|
||||
|
||||
useEffect(() => {
|
||||
if (serverUser === undefined) {
|
||||
const cachedUser = localStorage.getItem(CACHED_USER_KEY)
|
||||
|
@ -50,16 +54,16 @@ export function AuthProvider(props: {
|
|||
id: await fbUser.getIdToken(),
|
||||
refresh: fbUser.refreshToken,
|
||||
})
|
||||
let user = await getUser(fbUser.uid)
|
||||
if (!user) {
|
||||
let current = await getUserAndPrivateUser(fbUser.uid)
|
||||
if (!current) {
|
||||
const deviceToken = ensureDeviceToken()
|
||||
user = (await createUser({ deviceToken })).user as User
|
||||
current = (await createUser({ deviceToken })) as UserAndPrivateUser
|
||||
}
|
||||
setAuthUser(user)
|
||||
setAuthUser(current)
|
||||
// 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)
|
||||
localStorage.setItem(CACHED_USER_KEY, JSON.stringify(current))
|
||||
setCachedReferralInfoForUser(current.user)
|
||||
} else {
|
||||
// User logged out; reset to null
|
||||
deleteTokenCookies()
|
||||
|
@ -69,15 +73,30 @@ export function AuthProvider(props: {
|
|||
})
|
||||
}, [setAuthUser])
|
||||
|
||||
const authUserId = authUser?.id
|
||||
const authUsername = authUser?.username
|
||||
const uid = authUser?.user.id
|
||||
const username = authUser?.user.username
|
||||
useEffect(() => {
|
||||
if (authUserId && authUsername) {
|
||||
identifyUser(authUserId)
|
||||
setUserProperty('username', authUsername)
|
||||
return listenForUser(authUserId, setAuthUser)
|
||||
if (uid && username) {
|
||||
identifyUser(uid)
|
||||
setUserProperty('username', username)
|
||||
const userListener = listenForUser(uid, (user) =>
|
||||
setAuthUser((authUser) => {
|
||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||
return { ...authUser!, user: user! }
|
||||
})
|
||||
)
|
||||
const privateUserListener = listenForPrivateUser(uid, (privateUser) => {
|
||||
setAuthUser((authUser) => {
|
||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||
return { ...authUser!, privateUser: privateUser! }
|
||||
})
|
||||
})
|
||||
return () => {
|
||||
userListener()
|
||||
privateUserListener()
|
||||
}
|
||||
}
|
||||
}, [authUserId, authUsername, setAuthUser])
|
||||
}, [uid, username, setAuthUser])
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={authUser}>{children}</AuthContext.Provider>
|
||||
|
|
|
@ -13,7 +13,8 @@ import {
|
|||
import { AuthContext } from 'web/components/auth-context'
|
||||
|
||||
export const useUser = () => {
|
||||
return useContext(AuthContext)
|
||||
const authUser = useContext(AuthContext)
|
||||
return authUser ? authUser.user : authUser
|
||||
}
|
||||
|
||||
export const usePrivateUser = (userId?: string) => {
|
||||
|
|
|
@ -57,6 +57,16 @@ export async function getPrivateUser(userId: string) {
|
|||
return (await getDoc(doc(privateUsers, userId))).data()!
|
||||
}
|
||||
|
||||
export async function getUserAndPrivateUser(userId: string) {
|
||||
const [user, privateUser] = (
|
||||
await Promise.all([
|
||||
getDoc(doc(users, userId))!, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
getDoc(doc(privateUsers, userId))!, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
])
|
||||
).map((d) => d.data()) as [User, PrivateUser]
|
||||
return { user, privateUser }
|
||||
}
|
||||
|
||||
export async function getUserByUsername(username: string) {
|
||||
// Find a user whose username matches the given username, or null if no such user exists.
|
||||
const q = query(users, where('username', '==', username), limit(1))
|
||||
|
|
Loading…
Reference in New Issue
Block a user