Show every user's bets on their profile (#170)
* Show user bets on their profile * Add an alert for current users * Replace `/portfolio` with `/Austin?tab=Bets` * Replace `/Austin?tab=Bets` with `/Austin/bets` * Use replaceState for better browser history * Remove two console.logs * Note a bug * Fix path * Write in description of why we're doing this
This commit is contained in:
parent
42c981a54d
commit
d50cc39c27
|
@ -19,7 +19,6 @@ export function ContractsGrid(props: {
|
||||||
const isBottomVisible = useIsVisible(elem)
|
const isBottomVisible = useIsVisible(elem)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log({ isBottomVisible, hasMore })
|
|
||||||
if (isBottomVisible) {
|
if (isBottomVisible) {
|
||||||
loadMore()
|
loadMore()
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ export function BottomNavBar() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{user !== null && (
|
{user !== null && (
|
||||||
<Link href="/portfolio">
|
<Link href={`${user}/bets`}>
|
||||||
<a className="block w-full py-1 px-3 text-center hover:bg-indigo-200 hover:text-indigo-700">
|
<a className="block w-full py-1 px-3 text-center hover:bg-indigo-200 hover:text-indigo-700">
|
||||||
<PresentationChartLineIcon
|
<PresentationChartLineIcon
|
||||||
className="my-1 mx-auto h-6 w-6"
|
className="my-1 mx-auto h-6 w-6"
|
||||||
|
|
|
@ -28,12 +28,18 @@ function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigation = [
|
function getNavigation(userName: string) {
|
||||||
|
return [
|
||||||
{ name: 'Home', href: '/home', icon: HomeIcon },
|
{ name: 'Home', href: '/home', icon: HomeIcon },
|
||||||
{ name: 'Activity', href: '/activity', icon: ChatAltIcon },
|
{ name: 'Activity', href: '/activity', icon: ChatAltIcon },
|
||||||
{ name: 'Portfolio', href: '/portfolio', icon: PresentationChartLineIcon },
|
{
|
||||||
|
name: 'Portfolio',
|
||||||
|
href: `/${userName}/bets`,
|
||||||
|
icon: PresentationChartLineIcon,
|
||||||
|
},
|
||||||
{ name: 'Charity', href: '/charity', icon: HeartIcon },
|
{ name: 'Charity', href: '/charity', icon: HeartIcon },
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const signedOutNavigation = [
|
const signedOutNavigation = [
|
||||||
{ name: 'Home', href: '/home', icon: HomeIcon },
|
{ name: 'Home', href: '/home', icon: HomeIcon },
|
||||||
|
@ -119,7 +125,8 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
folds = _.sortBy(folds, 'followCount').reverse()
|
folds = _.sortBy(folds, 'followCount').reverse()
|
||||||
const deservesDailyFreeMarket = !useHasCreatedContractToday(user)
|
const deservesDailyFreeMarket = !useHasCreatedContractToday(user)
|
||||||
|
|
||||||
const navigationOptions = user === null ? signedOutNavigation : navigation
|
const navigationOptions =
|
||||||
|
user === null ? signedOutNavigation : getNavigation(user?.name || 'error')
|
||||||
const mobileNavigationOptions =
|
const mobileNavigationOptions =
|
||||||
user === null ? signedOutMobileNavigation : mobileNavigation
|
user === null ? signedOutMobileNavigation : mobileNavigation
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ import { getContractFromId, listContracts } from 'web/lib/firebase/contracts'
|
||||||
import { LoadingIndicator } from './loading-indicator'
|
import { LoadingIndicator } from './loading-indicator'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import { BetsList } from './bets-list'
|
||||||
|
import { Bet } from 'common/bet'
|
||||||
|
import { getUserBets } from 'web/lib/firebase/bets'
|
||||||
|
|
||||||
export function UserLink(props: {
|
export function UserLink(props: {
|
||||||
name: string
|
name: string
|
||||||
|
@ -38,12 +41,13 @@ export function UserLink(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TAB_IDS = ['markets', 'comments', 'bets']
|
||||||
|
|
||||||
export function UserPage(props: {
|
export function UserPage(props: {
|
||||||
user: User
|
user: User
|
||||||
currentUser?: User
|
currentUser?: User
|
||||||
defaultTabTitle?: string
|
defaultTabTitle?: 'markets' | 'comments' | 'bets'
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
|
||||||
const { user, currentUser, defaultTabTitle } = props
|
const { user, currentUser, defaultTabTitle } = props
|
||||||
const isCurrentUser = user.id === currentUser?.id
|
const isCurrentUser = user.id === currentUser?.id
|
||||||
const bannerUrl = user.bannerUrl ?? defaultBannerUrl(user.id)
|
const bannerUrl = user.bannerUrl ?? defaultBannerUrl(user.id)
|
||||||
|
@ -51,6 +55,7 @@ export function UserPage(props: {
|
||||||
const [usersContracts, setUsersContracts] = useState<Contract[] | 'loading'>(
|
const [usersContracts, setUsersContracts] = useState<Contract[] | 'loading'>(
|
||||||
'loading'
|
'loading'
|
||||||
)
|
)
|
||||||
|
const [usersBets, setUsersBets] = useState<Bet[] | 'loading'>('loading')
|
||||||
const [commentsByContract, setCommentsByContract] = useState<
|
const [commentsByContract, setCommentsByContract] = useState<
|
||||||
Map<Contract, Comment[]> | 'loading'
|
Map<Contract, Comment[]> | 'loading'
|
||||||
>('loading')
|
>('loading')
|
||||||
|
@ -59,6 +64,7 @@ export function UserPage(props: {
|
||||||
if (!user) return
|
if (!user) return
|
||||||
getUsersComments(user.id).then(setUsersComments)
|
getUsersComments(user.id).then(setUsersComments)
|
||||||
listContracts(user.id).then(setUsersContracts)
|
listContracts(user.id).then(setUsersContracts)
|
||||||
|
getUserBets(user.id).then(setUsersBets)
|
||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -187,17 +193,14 @@ export function UserPage(props: {
|
||||||
{usersContracts !== 'loading' && commentsByContract != 'loading' ? (
|
{usersContracts !== 'loading' && commentsByContract != 'loading' ? (
|
||||||
<Tabs
|
<Tabs
|
||||||
className={'pb-2 pt-1 '}
|
className={'pb-2 pt-1 '}
|
||||||
defaultIndex={defaultTabTitle === 'Comments' ? 1 : 0}
|
defaultIndex={TAB_IDS.indexOf(defaultTabTitle || 'markets')}
|
||||||
onClick={(tabName) =>
|
onClick={(tabName) => {
|
||||||
router.push(
|
const tabId = tabName.toLowerCase()
|
||||||
{
|
const subpath = tabId === 'markets' ? '' : '/' + tabId
|
||||||
pathname: `/${user.username}`,
|
// BUG: if you start on `/Bob/bets`, then click on Markets, use-query-and-sort-params
|
||||||
query: { tab: tabName },
|
// rewrites the url incorrectly to `/Bob/bets` instead of `/Bob`
|
||||||
},
|
window.history.replaceState('', '', `/${user.username}${subpath}`)
|
||||||
undefined,
|
}}
|
||||||
{ shallow: true }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
title: 'Markets',
|
title: 'Markets',
|
||||||
|
@ -220,6 +223,24 @@ export function UserPage(props: {
|
||||||
<div className="px-0.5 font-bold">{usersComments.length}</div>
|
<div className="px-0.5 font-bold">{usersComments.length}</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Bets',
|
||||||
|
content: (
|
||||||
|
<div>
|
||||||
|
<AlertBox
|
||||||
|
title="Bets are becoming publicly visible on 2022-06-01"
|
||||||
|
text="Bettor identities have always been traceable through the Manifold API.
|
||||||
|
However, our interface implied that they were private.
|
||||||
|
As we develop new features such as leaderboards and bet history, it won't be technically feasible to keep this info private.
|
||||||
|
For more context, or if you'd like to wipe your bet history, see: https://manifold.markets/Austin/will-all-bets-on-manifold-be-public"
|
||||||
|
/>
|
||||||
|
{isCurrentUser && <BetsList user={user} />}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
tabIcon: (
|
||||||
|
<div className="px-0.5 font-bold">{usersBets.length}</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -242,3 +263,27 @@ export function defaultBannerUrl(userId: string) {
|
||||||
]
|
]
|
||||||
return defaultBanner[genHash(userId)() % defaultBanner.length]
|
return defaultBanner[genHash(userId)() % defaultBanner.length]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { ExclamationIcon } from '@heroicons/react/solid'
|
||||||
|
|
||||||
|
function AlertBox(props: { title: string; text: string }) {
|
||||||
|
const { title, text } = props
|
||||||
|
return (
|
||||||
|
<div className="rounded-md bg-yellow-50 p-4">
|
||||||
|
<div className="flex">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<ExclamationIcon
|
||||||
|
className="h-5 w-5 text-yellow-400"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3">
|
||||||
|
<h3 className="text-sm font-medium text-yellow-800">{title}</h3>
|
||||||
|
<div className="mt-2 text-sm text-yellow-700">
|
||||||
|
<Linkify text={text} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ export function useInitialQueryAndSort(options?: {
|
||||||
}
|
}
|
||||||
setInitialSort(localSort ?? defaultSort)
|
setInitialSort(localSort ?? defaultSort)
|
||||||
} else {
|
} else {
|
||||||
console.log('ready setting to ', sort ?? defaultSort)
|
|
||||||
setInitialSort(sort ?? defaultSort)
|
setInitialSort(sort ?? defaultSort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,12 @@ export function listenForBets(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUserBets(userId: string) {
|
||||||
|
return getValues<Bet>(
|
||||||
|
query(collectionGroup(db, 'bets'), where('userId', '==', userId))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function listenForUserBets(
|
export function listenForUserBets(
|
||||||
userId: string,
|
userId: string,
|
||||||
setBets: (bets: Bet[]) => void
|
setBets: (bets: Bet[]) => void
|
||||||
|
|
5
web/pages/[username]/bets.tsx
Normal file
5
web/pages/[username]/bets.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import UserProfile from '.'
|
||||||
|
|
||||||
|
export default function UserBets() {
|
||||||
|
return <UserProfile tab="bets" />
|
||||||
|
}
|
5
web/pages/[username]/comments.tsx
Normal file
5
web/pages/[username]/comments.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import UserProfile from '.'
|
||||||
|
|
||||||
|
export default function UserBets() {
|
||||||
|
return <UserProfile tab="comments" />
|
||||||
|
}
|
|
@ -6,10 +6,12 @@ import { UserPage } from 'web/components/user-page'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import Custom404 from '../404'
|
import Custom404 from '../404'
|
||||||
|
|
||||||
export default function UserProfile() {
|
export default function UserProfile(props: {
|
||||||
|
tab?: 'markets' | 'comments' | 'bets'
|
||||||
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [user, setUser] = useState<User | null | 'loading'>('loading')
|
const [user, setUser] = useState<User | null | 'loading'>('loading')
|
||||||
const { username, tab } = router.query as { username: string; tab: string }
|
const { username } = router.query as { username: string }
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (username) {
|
if (username) {
|
||||||
getUserByUsername(username).then(setUser)
|
getUserByUsername(username).then(setUser)
|
||||||
|
@ -24,7 +26,7 @@ export default function UserProfile() {
|
||||||
<UserPage
|
<UserPage
|
||||||
user={user}
|
user={user}
|
||||||
currentUser={currentUser || undefined}
|
currentUser={currentUser || undefined}
|
||||||
defaultTabTitle={tab}
|
defaultTabTitle={props.tab}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Custom404 />
|
<Custom404 />
|
||||||
|
|
Loading…
Reference in New Issue
Block a user