Add folds links in market, hide tags behind Show tags toggle.
This commit is contained in:
parent
79c5472c11
commit
565ded1063
|
@ -18,16 +18,19 @@ import { ContractFeed } from './contract-feed'
|
||||||
import { TweetButton } from './tweet-button'
|
import { TweetButton } from './tweet-button'
|
||||||
import { Bet } from '../../common/bet'
|
import { Bet } from '../../common/bet'
|
||||||
import { Comment } from '../../common/comment'
|
import { Comment } from '../../common/comment'
|
||||||
import { TagsInput } from './tags-input'
|
import { RevealableTagsInput } from './tags-input'
|
||||||
import BetRow from './bet-row'
|
import BetRow from './bet-row'
|
||||||
|
import { Fold } from '../../common/fold'
|
||||||
|
import { FoldTagList } from './tags-list'
|
||||||
|
|
||||||
export const ContractOverview = (props: {
|
export const ContractOverview = (props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
comments: Comment[]
|
comments: Comment[]
|
||||||
|
folds: Fold[]
|
||||||
className?: string
|
className?: string
|
||||||
}) => {
|
}) => {
|
||||||
const { contract, bets, comments, className } = props
|
const { contract, bets, comments, folds, className } = props
|
||||||
const { resolution, creatorId, creatorName } = contract
|
const { resolution, creatorId, creatorName } = contract
|
||||||
const { probPercent, truePool } = contractMetrics(contract)
|
const { probPercent, truePool } = contractMetrics(contract)
|
||||||
|
|
||||||
|
@ -85,11 +88,21 @@ export const ContractOverview = (props: {
|
||||||
|
|
||||||
<ContractProbGraph contract={contract} />
|
<ContractProbGraph contract={contract} />
|
||||||
|
|
||||||
<Row className="justify-between mt-6 ml-4 gap-4">
|
<Row className="hidden sm:flex justify-between items-center mt-6 ml-4 gap-4">
|
||||||
<TagsInput contract={contract} />
|
<FoldTagList folds={folds} />
|
||||||
<TweetButton tweetText={tweetText} />
|
<TweetButton tweetText={tweetText} />
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
<Col className="sm:hidden mt-6 ml-4 gap-4">
|
||||||
|
<TweetButton className="self-end" tweetText={tweetText} />
|
||||||
|
<FoldTagList folds={folds} />
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<RevealableTagsInput
|
||||||
|
className={clsx('mx-4', folds.length > 0 && 'mt-4')}
|
||||||
|
contract={contract}
|
||||||
|
/>
|
||||||
|
|
||||||
<Spacer h={12} />
|
<Spacer h={12} />
|
||||||
|
|
||||||
{/* Show a delete button for contracts without any trading */}
|
{/* Show a delete button for contracts without any trading */}
|
||||||
|
|
17
web/components/fold-tag.tsx
Normal file
17
web/components/fold-tag.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import { Fold } from '../../common/fold'
|
||||||
|
|
||||||
|
export function FoldTag(props: { fold: Fold }) {
|
||||||
|
const { fold } = props
|
||||||
|
const { name } = fold
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'bg-white hover:bg-gray-100 px-4 py-2 rounded-full shadow-md',
|
||||||
|
'cursor-pointer'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="text-gray-500">{name}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { parseWordsAsTags } from '../../common/util/parse'
|
import { parseWordsAsTags } from '../../common/util/parse'
|
||||||
import { Contract, updateContract } from '../lib/firebase/contracts'
|
import { Contract, updateContract } from '../lib/firebase/contracts'
|
||||||
|
import { Col } from './layout/col'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { TagsList } from './tags-list'
|
import { CompactTagsList } from './tags-list'
|
||||||
|
|
||||||
export function TagsInput(props: { contract: Contract }) {
|
export function TagsInput(props: { contract: Contract; className?: string }) {
|
||||||
const { contract } = props
|
const { contract, className } = props
|
||||||
const { tags } = contract
|
const { tags } = contract
|
||||||
|
|
||||||
const [tagText, setTagText] = useState('')
|
const [tagText, setTagText] = useState('')
|
||||||
|
@ -24,8 +26,8 @@ export function TagsInput(props: { contract: Contract }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="flex-wrap gap-4">
|
<Col className={clsx('gap-4', className)}>
|
||||||
<TagsList tags={newTags.map((tag) => `#${tag}`)} />
|
<CompactTagsList tags={newTags.map((tag) => `#${tag}`)} />
|
||||||
|
|
||||||
<Row className="items-center gap-4">
|
<Row className="items-center gap-4">
|
||||||
<input
|
<input
|
||||||
|
@ -40,6 +42,28 @@ export function TagsInput(props: { contract: Contract }) {
|
||||||
Save tags
|
Save tags
|
||||||
</button>
|
</button>
|
||||||
</Row>
|
</Row>
|
||||||
</Row>
|
</Col>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function RevealableTagsInput(props: {
|
||||||
|
contract: Contract
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { contract, className } = props
|
||||||
|
const [hidden, setHidden] = useState(true)
|
||||||
|
|
||||||
|
if (hidden)
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'text-gray-500 cursor-pointer hover:underline hover:decoration-indigo-400 hover:decoration-2',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={() => setHidden((hidden) => !hidden)}
|
||||||
|
>
|
||||||
|
Show tags
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
return <TagsInput className={clsx('pt-2', className)} contract={contract} />
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import clsx from 'clsx'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { Linkify } from './linkify'
|
import { Linkify } from './linkify'
|
||||||
import { SiteLink } from './site-link'
|
import { SiteLink } from './site-link'
|
||||||
|
import { Fold } from '../../common/fold'
|
||||||
|
|
||||||
export function Hashtag(props: { tag: string; noLink?: boolean }) {
|
export function Hashtag(props: { tag: string; noLink?: boolean }) {
|
||||||
const { tag, noLink } = props
|
const { tag, noLink } = props
|
||||||
|
@ -51,3 +52,36 @@ export function CompactTagsList(props: { tags: string[] }) {
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FoldTag(props: { fold: Fold }) {
|
||||||
|
const { fold } = props
|
||||||
|
const { name } = fold
|
||||||
|
return (
|
||||||
|
<SiteLink href={`/fold/${fold.slug}`} className="flex items-center">
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'bg-indigo-50 px-4 py-2 rounded-full shadow-md',
|
||||||
|
'cursor-pointer'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="text-gray-500 text-sm">{name}</span>
|
||||||
|
</div>
|
||||||
|
</SiteLink>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FoldTagList(props: { folds: Fold[]; className?: string }) {
|
||||||
|
const { folds, className } = props
|
||||||
|
return (
|
||||||
|
<Row className={clsx('flex-wrap gap-2 items-center', className)}>
|
||||||
|
{folds.length > 0 && (
|
||||||
|
<>
|
||||||
|
<div className="text-gray-500 mr-1">Communities</div>
|
||||||
|
{folds.map((fold) => (
|
||||||
|
<FoldTag key={fold.id} fold={fold} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -131,3 +131,13 @@ export function listenForFollow(
|
||||||
setFollow(!!value)
|
setFollow(!!value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFoldsByTags(tags: string[]) {
|
||||||
|
if (tags.length === 0) return []
|
||||||
|
|
||||||
|
const lowercaseTags = tags.map((tag) => tag)
|
||||||
|
return getValues<Fold>(
|
||||||
|
// TODO: split into multiple queries if tags.length > 10.
|
||||||
|
query(foldCollection, where('tags', 'array-contains-any', lowercaseTags))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ import { contractTextDetails } from '../../components/contract-card'
|
||||||
import { Bet, listAllBets } from '../../lib/firebase/bets'
|
import { Bet, listAllBets } from '../../lib/firebase/bets'
|
||||||
import { Comment, listAllComments } from '../../lib/firebase/comments'
|
import { Comment, listAllComments } from '../../lib/firebase/comments'
|
||||||
import Custom404 from '../404'
|
import Custom404 from '../404'
|
||||||
|
import { getFoldsByTags } from '../../lib/firebase/folds'
|
||||||
|
import { Fold } from '../../../common/fold'
|
||||||
|
|
||||||
export async function getStaticProps(props: {
|
export async function getStaticProps(props: {
|
||||||
params: { username: string; contractSlug: string }
|
params: { username: string; contractSlug: string }
|
||||||
|
@ -31,18 +33,23 @@ export async function getStaticProps(props: {
|
||||||
const contract = (await getContractFromSlug(contractSlug)) || null
|
const contract = (await getContractFromSlug(contractSlug)) || null
|
||||||
const contractId = contract?.id
|
const contractId = contract?.id
|
||||||
|
|
||||||
|
const foldsPromise = getFoldsByTags(contract?.tags ?? [])
|
||||||
|
|
||||||
const [bets, comments] = await Promise.all([
|
const [bets, comments] = await Promise.all([
|
||||||
contractId ? listAllBets(contractId) : null,
|
contractId ? listAllBets(contractId) : [],
|
||||||
contractId ? listAllComments(contractId) : null,
|
contractId ? listAllComments(contractId) : [],
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const folds = await foldsPromise
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
contract,
|
||||||
username,
|
username,
|
||||||
slug: contractSlug,
|
slug: contractSlug,
|
||||||
contract,
|
|
||||||
bets,
|
bets,
|
||||||
comments,
|
comments,
|
||||||
|
folds,
|
||||||
},
|
},
|
||||||
|
|
||||||
revalidate: 60, // regenerate after a minute
|
revalidate: 60, // regenerate after a minute
|
||||||
|
@ -55,15 +62,16 @@ export async function getStaticPaths() {
|
||||||
|
|
||||||
export default function ContractPage(props: {
|
export default function ContractPage(props: {
|
||||||
contract: Contract | null
|
contract: Contract | null
|
||||||
bets: Bet[] | null
|
|
||||||
comments: Comment[] | null
|
|
||||||
slug: string
|
|
||||||
username: string
|
username: string
|
||||||
|
bets: Bet[]
|
||||||
|
comments: Comment[]
|
||||||
|
slug: string
|
||||||
|
folds: Fold[]
|
||||||
}) {
|
}) {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
|
||||||
const contract = useContractWithPreload(props.slug, props.contract)
|
const contract = useContractWithPreload(props.slug, props.contract)
|
||||||
const { bets, comments } = props
|
const { bets, comments, folds } = props
|
||||||
|
|
||||||
if (!contract) {
|
if (!contract) {
|
||||||
return <Custom404 />
|
return <Custom404 />
|
||||||
|
@ -103,6 +111,7 @@ export default function ContractPage(props: {
|
||||||
contract={contract}
|
contract={contract}
|
||||||
bets={bets ?? []}
|
bets={bets ?? []}
|
||||||
comments={comments ?? []}
|
comments={comments ?? []}
|
||||||
|
folds={folds}
|
||||||
/>
|
/>
|
||||||
<BetsSection contract={contract} user={user ?? null} />
|
<BetsSection contract={contract} user={user ?? null} />
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user