diff --git a/web/hooks/use-user.ts b/web/hooks/use-user.ts index 3499b230..24075851 100644 --- a/web/hooks/use-user.ts +++ b/web/hooks/use-user.ts @@ -1,9 +1,11 @@ -import { useEffect, useState } from 'react' -import { getCachedUser, listenForLogin, listenForUser, User } from '../lib/firebase/users' +import { useEffect, useLayoutEffect, useState } from 'react' +import { listenForLogin, listenForUser, User } from '../lib/firebase/users' export const useUser = () => { - const [user, setUser] = useState<User | null | undefined>(getCachedUser()) - useEffect(() => listenForLogin(setUser), []) + const [user, setUser] = useState<User | null | undefined>(undefined) + + // Use layout effect to trigger re-render before first paint. + useLayoutEffect(() => listenForLogin(setUser), []) const userId = user?.id diff --git a/web/lib/firebase/users.ts b/web/lib/firebase/users.ts index a9536fb4..7d98cda7 100644 --- a/web/lib/firebase/users.ts +++ b/web/lib/firebase/users.ts @@ -61,30 +61,34 @@ export function listenForUser(userId: string, setUser: (user: User) => void) { } const CACHED_USER_KEY = 'CACHED_USER_KEY' -export function listenForLogin(onUser: (_user: User | null) => void) { - return onAuthStateChanged(auth, async (user) => { - if (user) { - let fetchedUser = await getUser(user.uid) - if (!fetchedUser) { +export function listenForLogin(onUser: (user: User | null) => void) { + const cachedUser = localStorage.getItem(CACHED_USER_KEY) + onUser(cachedUser ? JSON.parse(cachedUser) : null) + + return onAuthStateChanged(auth, async (fbUser) => { + if (fbUser) { + let user = await getUser(fbUser.uid) + if (!user) { // User just created an account; save them to our database. - fetchedUser = { - id: user.uid, - name: user.displayName || 'Default Name', - username: user.displayName?.replace(/\s+/g, '') || 'DefaultUsername', - avatarUrl: user.photoURL || '', - email: user.email || 'default@blah.com', + user = { + id: fbUser.uid, + name: fbUser.displayName || 'Default Name', + username: + fbUser.displayName?.replace(/\s+/g, '') || 'DefaultUsername', + avatarUrl: fbUser.photoURL || '', + email: fbUser.email || 'default@blah.com', balance: STARTING_BALANCE, // TODO: use Firestore timestamp? createdTime: Date.now(), lastUpdatedTime: Date.now(), } - await setUser(user.uid, fetchedUser) + await setUser(fbUser.uid, user) } - onUser(fetchedUser) + onUser(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(fetchedUser)) + localStorage.setItem(CACHED_USER_KEY, JSON.stringify(user)) } else { // User logged out; reset to null onUser(null) @@ -93,13 +97,6 @@ export function listenForLogin(onUser: (_user: User | null) => void) { }) } -export function getCachedUser() { - if (typeof window !== 'undefined') { - const cachedUser = localStorage.getItem(CACHED_USER_KEY) - return cachedUser ? (JSON.parse(cachedUser) as User) : undefined - } -} - export async function firebaseLogin() { const provider = new GoogleAuthProvider() signInWithPopup(auth, provider)