Add links to /edit and /leaderboards for fold
This commit is contained in:
parent
9c5463235a
commit
9bac47e9f3
58
web/components/leaderboard.tsx
Normal file
58
web/components/leaderboard.tsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import Image from 'next/image'
|
||||||
|
import { User } from '../../common/user'
|
||||||
|
import { Row } from './layout/row'
|
||||||
|
import { SiteLink } from './site-link'
|
||||||
|
import { Title } from './title'
|
||||||
|
|
||||||
|
export function Leaderboard(props: {
|
||||||
|
title: string
|
||||||
|
users: User[]
|
||||||
|
columns: {
|
||||||
|
header: string
|
||||||
|
renderCell: (user: User) => any
|
||||||
|
}[]
|
||||||
|
}) {
|
||||||
|
const { title, users, columns } = props
|
||||||
|
return (
|
||||||
|
<div className="max-w-xl w-full px-1">
|
||||||
|
<Title text={title} />
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="table table-zebra table-compact text-gray-500 w-full">
|
||||||
|
<thead>
|
||||||
|
<tr className="p-2">
|
||||||
|
<th>#</th>
|
||||||
|
<th>Name</th>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<th key={column.header}>{column.header}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{users.map((user, index) => (
|
||||||
|
<tr key={user.id}>
|
||||||
|
<td>{index + 1}</td>
|
||||||
|
<td>
|
||||||
|
<SiteLink className="relative" href={`/${user.username}`}>
|
||||||
|
<Row className="items-center gap-4">
|
||||||
|
<Image
|
||||||
|
className="rounded-full bg-gray-400 flex-shrink-0 ring-8 ring-gray-50"
|
||||||
|
src={user.avatarUrl || ''}
|
||||||
|
alt=""
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
|
/>
|
||||||
|
<div>{user.name}</div>
|
||||||
|
</Row>
|
||||||
|
</SiteLink>
|
||||||
|
</td>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<td key={column.header}>{column.renderCell(user)}</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import { Linkify } from './linkify'
|
||||||
export function TagsList(props: { tags: string[] }) {
|
export function TagsList(props: { tags: string[] }) {
|
||||||
const { tags } = props
|
const { tags } = props
|
||||||
return (
|
return (
|
||||||
<Row className="gap-2 flex-wrap">
|
<Row className="gap-2 flex-wrap text-sm text-gray-500">
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<div key={tag} className="bg-gray-100 px-1">
|
<div key={tag} className="bg-gray-100 px-1">
|
||||||
<Linkify text={tag} gray />
|
<Linkify text={tag} gray />
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import _ from 'lodash'
|
|
||||||
import { Fold } from '../../../common/fold'
|
|
||||||
import { Comment } from '../../../common/comment'
|
|
||||||
import { Page } from '../../components/page'
|
|
||||||
import { Title } from '../../components/title'
|
|
||||||
import { Bet, listAllBets } from '../../lib/firebase/bets'
|
|
||||||
import { listAllComments } from '../../lib/firebase/comments'
|
|
||||||
import { Contract } from '../../lib/firebase/contracts'
|
|
||||||
import { getFoldBySlug, getFoldContracts } from '../../lib/firebase/folds'
|
|
||||||
import { ActivityFeed, findActiveContracts } from '../activity'
|
|
||||||
import { TagsList } from '../../components/tags-list'
|
|
||||||
|
|
||||||
export async function getStaticProps(props: { params: { foldSlug: string } }) {
|
|
||||||
const { foldSlug } = props.params
|
|
||||||
|
|
||||||
const fold = await getFoldBySlug(foldSlug)
|
|
||||||
const contracts = fold ? await getFoldContracts(fold) : []
|
|
||||||
const contractComments = await Promise.all(
|
|
||||||
contracts.map((contract) => listAllComments(contract.id))
|
|
||||||
)
|
|
||||||
|
|
||||||
const activeContracts = findActiveContracts(
|
|
||||||
contracts,
|
|
||||||
_.flatten(contractComments)
|
|
||||||
)
|
|
||||||
const activeContractBets = await Promise.all(
|
|
||||||
activeContracts.map((contract) => listAllBets(contract.id))
|
|
||||||
)
|
|
||||||
const activeContractComments = activeContracts.map(
|
|
||||||
(contract) =>
|
|
||||||
contractComments[contracts.findIndex((c) => c.id === contract.id)]
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
fold,
|
|
||||||
activeContracts,
|
|
||||||
activeContractBets,
|
|
||||||
activeContractComments,
|
|
||||||
},
|
|
||||||
|
|
||||||
revalidate: 60, // regenerate after a minute
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
|
||||||
return { paths: [], fallback: 'blocking' }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function FoldPage(props: {
|
|
||||||
fold: Fold
|
|
||||||
activeContracts: Contract[]
|
|
||||||
activeContractBets: Bet[][]
|
|
||||||
activeContractComments: Comment[][]
|
|
||||||
}) {
|
|
||||||
const { fold, activeContracts, activeContractBets, activeContractComments } =
|
|
||||||
props
|
|
||||||
|
|
||||||
const { tags } = fold
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Page>
|
|
||||||
<Title text={fold.name} />
|
|
||||||
|
|
||||||
<TagsList tags={tags} />
|
|
||||||
|
|
||||||
<ActivityFeed
|
|
||||||
contracts={activeContracts}
|
|
||||||
contractBets={activeContractBets}
|
|
||||||
contractComments={activeContractComments}
|
|
||||||
/>
|
|
||||||
</Page>
|
|
||||||
)
|
|
||||||
}
|
|
18
web/pages/fold/[foldSlug]/edit.tsx
Normal file
18
web/pages/fold/[foldSlug]/edit.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Page } from '../../../components/page'
|
||||||
|
|
||||||
|
export async function getStaticProps() {
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
|
||||||
|
revalidate: 60, // regenerate after a minute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
return { paths: [], fallback: 'blocking' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Leaderboards(props: {}) {
|
||||||
|
return <Page>Edit fold</Page>
|
||||||
|
}
|
125
web/pages/fold/[foldSlug]/index.tsx
Normal file
125
web/pages/fold/[foldSlug]/index.tsx
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Fold } from '../../../../common/fold'
|
||||||
|
import { Comment } from '../../../../common/comment'
|
||||||
|
import { Page } from '../../../components/page'
|
||||||
|
import { Title } from '../../../components/title'
|
||||||
|
import { Bet, listAllBets } from '../../../lib/firebase/bets'
|
||||||
|
import { listAllComments } from '../../../lib/firebase/comments'
|
||||||
|
import { Contract } from '../../../lib/firebase/contracts'
|
||||||
|
import { getFoldBySlug, getFoldContracts } from '../../../lib/firebase/folds'
|
||||||
|
import { ActivityFeed, findActiveContracts } from '../../activity'
|
||||||
|
import { TagsList } from '../../../components/tags-list'
|
||||||
|
import { Row } from '../../../components/layout/row'
|
||||||
|
import { UserLink } from '../../../components/user-page'
|
||||||
|
import { getUser, User } from '../../../lib/firebase/users'
|
||||||
|
import { Spacer } from '../../../components/layout/spacer'
|
||||||
|
import { Col } from '../../../components/layout/col'
|
||||||
|
import { SiteLink } from '../../../components/site-link'
|
||||||
|
import { useUser } from '../../../hooks/use-user'
|
||||||
|
|
||||||
|
export async function getStaticProps(props: { params: { foldSlug: string } }) {
|
||||||
|
const { foldSlug } = props.params
|
||||||
|
|
||||||
|
const fold = await getFoldBySlug(foldSlug)
|
||||||
|
const curatorPromise = fold ? getUser(fold.curatorId) : null
|
||||||
|
|
||||||
|
const contracts = fold ? await getFoldContracts(fold) : []
|
||||||
|
const contractComments = await Promise.all(
|
||||||
|
contracts.map((contract) => listAllComments(contract.id))
|
||||||
|
)
|
||||||
|
|
||||||
|
const activeContracts = findActiveContracts(
|
||||||
|
contracts,
|
||||||
|
_.flatten(contractComments)
|
||||||
|
)
|
||||||
|
const activeContractBets = await Promise.all(
|
||||||
|
activeContracts.map((contract) => listAllBets(contract.id))
|
||||||
|
)
|
||||||
|
const activeContractComments = activeContracts.map(
|
||||||
|
(contract) =>
|
||||||
|
contractComments[contracts.findIndex((c) => c.id === contract.id)]
|
||||||
|
)
|
||||||
|
|
||||||
|
const curator = await curatorPromise
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
fold,
|
||||||
|
curator,
|
||||||
|
activeContracts,
|
||||||
|
activeContractBets,
|
||||||
|
activeContractComments,
|
||||||
|
},
|
||||||
|
|
||||||
|
revalidate: 60, // regenerate after a minute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
return { paths: [], fallback: 'blocking' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FoldPage(props: {
|
||||||
|
fold: Fold
|
||||||
|
curator: User
|
||||||
|
activeContracts: Contract[]
|
||||||
|
activeContractBets: Bet[][]
|
||||||
|
activeContractComments: Comment[][]
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
fold,
|
||||||
|
curator,
|
||||||
|
activeContracts,
|
||||||
|
activeContractBets,
|
||||||
|
activeContractComments,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const { tags, curatorId } = fold
|
||||||
|
|
||||||
|
const user = useUser()
|
||||||
|
const isCurator = user?.id === curatorId
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<Col className="items-center">
|
||||||
|
<Col className="max-w-3xl w-full">
|
||||||
|
<Title text={fold.name} />
|
||||||
|
|
||||||
|
<Row className="items-center mb-2">
|
||||||
|
{isCurator && (
|
||||||
|
<>
|
||||||
|
<SiteLink className="text-sm " href={`/fold/${fold.slug}/edit`}>
|
||||||
|
Edit
|
||||||
|
</SiteLink>
|
||||||
|
<div className="ml-2 mr-2">•</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<SiteLink
|
||||||
|
className="text-sm"
|
||||||
|
href={`/fold/${fold.slug}/leaderboards`}
|
||||||
|
>
|
||||||
|
Leaderboards
|
||||||
|
</SiteLink>
|
||||||
|
<div className="ml-2 mr-2">•</div>
|
||||||
|
<div className="text-sm text-gray-500 mr-1">Curated by</div>
|
||||||
|
<UserLink
|
||||||
|
className="text-sm text-neutral"
|
||||||
|
name={curator.name}
|
||||||
|
username={curator.username}
|
||||||
|
/>
|
||||||
|
<div className="ml-2 mr-2">•</div>
|
||||||
|
<TagsList tags={tags.map((tag) => `#${tag}`)} />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Spacer h={4} />
|
||||||
|
|
||||||
|
<ActivityFeed
|
||||||
|
contracts={activeContracts}
|
||||||
|
contractBets={activeContractBets}
|
||||||
|
contractComments={activeContractComments}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Col>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
46
web/pages/fold/[foldSlug]/leaderboards.tsx
Normal file
46
web/pages/fold/[foldSlug]/leaderboards.tsx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Col } from '../../../components/layout/col'
|
||||||
|
import { Leaderboard } from '../../../components/leaderboard'
|
||||||
|
import { Page } from '../../../components/page'
|
||||||
|
import { formatMoney } from '../../../lib/util/format'
|
||||||
|
|
||||||
|
export async function getStaticProps() {
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
|
||||||
|
revalidate: 60, // regenerate after a minute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
return { paths: [], fallback: 'blocking' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Leaderboards(props: {}) {
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<Col className="items-center lg:flex-row gap-10">
|
||||||
|
<Leaderboard
|
||||||
|
title="🏅 Top traders"
|
||||||
|
users={[]}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
header: 'Total profit',
|
||||||
|
renderCell: (user) => formatMoney(user.totalPnLCached),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Leaderboard
|
||||||
|
title="🏅 Top creators"
|
||||||
|
users={[]}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
header: 'Market volume',
|
||||||
|
renderCell: (user) => formatMoney(user.creatorVolumeCached),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,10 +1,7 @@
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import Image from 'next/image'
|
|
||||||
import { Col } from '../components/layout/col'
|
import { Col } from '../components/layout/col'
|
||||||
import { Row } from '../components/layout/row'
|
import { Leaderboard } from '../components/leaderboard'
|
||||||
import { Page } from '../components/page'
|
import { Page } from '../components/page'
|
||||||
import { SiteLink } from '../components/site-link'
|
|
||||||
import { Title } from '../components/title'
|
|
||||||
import { getTopCreators, getTopTraders, User } from '../lib/firebase/users'
|
import { getTopCreators, getTopTraders, User } from '../lib/firebase/users'
|
||||||
import { formatMoney } from '../lib/util/format'
|
import { formatMoney } from '../lib/util/format'
|
||||||
|
|
||||||
|
@ -57,56 +54,3 @@ export default function Leaderboards(props: {
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Leaderboard(props: {
|
|
||||||
title: string
|
|
||||||
users: User[]
|
|
||||||
columns: {
|
|
||||||
header: string
|
|
||||||
renderCell: (user: User) => any
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
const { title, users, columns } = props
|
|
||||||
return (
|
|
||||||
<div className="max-w-xl w-full px-1">
|
|
||||||
<Title text={title} />
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="table table-zebra table-compact text-gray-500 w-full">
|
|
||||||
<thead>
|
|
||||||
<tr className="p-2">
|
|
||||||
<th>#</th>
|
|
||||||
<th>Name</th>
|
|
||||||
{columns.map((column) => (
|
|
||||||
<th key={column.header}>{column.header}</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{users.map((user, index) => (
|
|
||||||
<tr key={user.id}>
|
|
||||||
<td>{index + 1}</td>
|
|
||||||
<td>
|
|
||||||
<SiteLink className="relative" href={`/${user.username}`}>
|
|
||||||
<Row className="items-center gap-4">
|
|
||||||
<Image
|
|
||||||
className="rounded-full bg-gray-400 flex-shrink-0 ring-8 ring-gray-50"
|
|
||||||
src={user.avatarUrl || ''}
|
|
||||||
alt=""
|
|
||||||
width={32}
|
|
||||||
height={32}
|
|
||||||
/>
|
|
||||||
<div>{user.name}</div>
|
|
||||||
</Row>
|
|
||||||
</SiteLink>
|
|
||||||
</td>
|
|
||||||
{columns.map((column) => (
|
|
||||||
<td key={column.header}>{column.renderCell(user)}</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user