profile page

This commit is contained in:
mantikoros 2022-01-26 17:40:44 -06:00
parent 363a7ba7ae
commit dee258fb6b
4 changed files with 111 additions and 4 deletions

View File

@ -31,6 +31,10 @@ function getNavigationOptions(user: User, options: { mobile: boolean }) {
name: 'Home',
href: '/',
},
{
name: 'Profile',
href: '/profile',
},
...(mobile
? [
{
@ -68,9 +72,7 @@ function ProfileSummary(props: { user: User }) {
return (
<Col className="avatar items-center sm:flex-row gap-2 sm:gap-0">
<div className="rounded-full w-10 h-10 sm:mr-4">
{user.avatarUrl && (
<Image src={user.avatarUrl} width={40} height={40} />
)}
{user.avatarUrl && <img src={user.avatarUrl} width={40} height={40} />}
</div>
<div className="truncate text-left" style={{ maxWidth: 170 }}>
<div className="hidden sm:flex">{user.name}</div>

View File

@ -1,5 +1,11 @@
import { useEffect, useState } from 'react'
import { listenForLogin, listenForUser, User } from '../lib/firebase/users'
import { PrivateUser } from '../../common/user'
import {
listenForLogin,
listenForPrivateUser,
listenForUser,
User,
} from '../lib/firebase/users'
export const useUser = () => {
const [user, setUser] = useState<User | null | undefined>(undefined)
@ -14,3 +20,15 @@ export const useUser = () => {
return user
}
export const usePrivateUser = (userId?: string) => {
const [privateUser, setPrivateUser] = useState<
PrivateUser | null | undefined
>(undefined)
useEffect(() => {
if (userId) return listenForPrivateUser(userId, setPrivateUser)
}, [userId])
return privateUser
}

View File

@ -53,6 +53,16 @@ export function listenForUser(userId: string, setUser: (user: User) => void) {
})
}
export function listenForPrivateUser(
userId: string,
setPrivateUser: (privateUser: PrivateUser) => void
) {
const userRef = doc(db, 'private-users', userId)
return onSnapshot(userRef, (userSnap) => {
setPrivateUser(userSnap.data() as PrivateUser)
})
}
const CACHED_USER_KEY = 'CACHED_USER_KEY'
// used to avoid weird race condition

77
web/pages/profile.tsx Normal file
View File

@ -0,0 +1,77 @@
import Image from 'next/image'
import { useEffect, useState } from 'react'
import { AddFundsButton } from '../components/add-funds-button'
import { Page } from '../components/page'
import { SEO } from '../components/SEO'
import { Title } from '../components/title'
import { usePrivateUser, useUser } from '../hooks/use-user'
import { formatMoney } from '../lib/util/format'
export default function ProfilePage() {
const user = useUser()
const privateUser = usePrivateUser(user?.id)
const [avatarUrl, setAvatarUrl] = useState(user?.avatarUrl || '')
const [name, setName] = useState(user?.name || '')
const [username, setUsername] = useState(user?.username || '')
useEffect(() => {
if (user) {
setAvatarUrl(user.avatarUrl || '')
setName(user.name || '')
setUsername(user.username || '')
}
}, [user])
return (
<Page>
<SEO title="Profile" description="User profile settings" url="/profile" />
<Title text="Profile" />
<p>
<img
src={avatarUrl}
width={80}
height={80}
className="rounded-full bg-gray-400 flex items-center justify-center"
/>
</p>
<label className="label">
<span className="label-text">Display name</span>
</label>
<input
type="text"
placeholder="Display name"
className="input input-bordered"
value={name}
onChange={(e) => setName(e.target.value || '')}
/>
<label className="label">
<span className="label-text">Username</span>
</label>
<input
type="text"
placeholder="Username"
className="input input-bordered"
value={username}
onChange={(e) => setUsername(e.target.value || '')}
/>
<label className="label">
<span className="label-text">Email</span>
</label>
<p>{privateUser?.email}</p>
<label className="label">
<span className="label-text">Balance</span>
</label>
<p>{formatMoney(user?.balance || 0)}</p>
<AddFundsButton />
</Page>
)
}