profile page
This commit is contained in:
parent
363a7ba7ae
commit
dee258fb6b
|
@ -31,6 +31,10 @@ function getNavigationOptions(user: User, options: { mobile: boolean }) {
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
href: '/',
|
href: '/',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Profile',
|
||||||
|
href: '/profile',
|
||||||
|
},
|
||||||
...(mobile
|
...(mobile
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
|
@ -68,9 +72,7 @@ function ProfileSummary(props: { user: User }) {
|
||||||
return (
|
return (
|
||||||
<Col className="avatar items-center sm:flex-row gap-2 sm:gap-0">
|
<Col className="avatar items-center sm:flex-row gap-2 sm:gap-0">
|
||||||
<div className="rounded-full w-10 h-10 sm:mr-4">
|
<div className="rounded-full w-10 h-10 sm:mr-4">
|
||||||
{user.avatarUrl && (
|
{user.avatarUrl && <img src={user.avatarUrl} width={40} height={40} />}
|
||||||
<Image src={user.avatarUrl} width={40} height={40} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate text-left" style={{ maxWidth: 170 }}>
|
<div className="truncate text-left" style={{ maxWidth: 170 }}>
|
||||||
<div className="hidden sm:flex">{user.name}</div>
|
<div className="hidden sm:flex">{user.name}</div>
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import { useEffect, useState } from 'react'
|
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 = () => {
|
export const useUser = () => {
|
||||||
const [user, setUser] = useState<User | null | undefined>(undefined)
|
const [user, setUser] = useState<User | null | undefined>(undefined)
|
||||||
|
@ -14,3 +20,15 @@ export const useUser = () => {
|
||||||
|
|
||||||
return user
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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'
|
const CACHED_USER_KEY = 'CACHED_USER_KEY'
|
||||||
|
|
||||||
// used to avoid weird race condition
|
// used to avoid weird race condition
|
||||||
|
|
77
web/pages/profile.tsx
Normal file
77
web/pages/profile.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user