Show a mini-feed under each FR answer
This commit is contained in:
parent
7dcae2a141
commit
0fdcde058d
|
@ -15,6 +15,7 @@ import { formatPercent } from '../../../common/util/format'
|
||||||
import { getOutcomeProbability } from '../../../common/calculate'
|
import { getOutcomeProbability } from '../../../common/calculate'
|
||||||
import { tradingAllowed } from '../../lib/firebase/contracts'
|
import { tradingAllowed } from '../../lib/firebase/contracts'
|
||||||
import { AnswerBetPanel } from './answer-bet-panel'
|
import { AnswerBetPanel } from './answer-bet-panel'
|
||||||
|
import { ContractFeed } from '../contract-feed'
|
||||||
|
|
||||||
export function AnswerItem(props: {
|
export function AnswerItem(props: {
|
||||||
answer: Answer
|
answer: Answer
|
||||||
|
@ -50,11 +51,11 @@ export function AnswerItem(props: {
|
||||||
return (
|
return (
|
||||||
<Col
|
<Col
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'p-4 sm:flex-row rounded gap-4',
|
'gap-4 rounded p-4 sm:flex-row',
|
||||||
wasResolvedTo
|
wasResolvedTo
|
||||||
? resolution === 'MKT'
|
? resolution === 'MKT'
|
||||||
? 'bg-blue-50 mb-2'
|
? 'mb-2 bg-blue-50'
|
||||||
: 'bg-green-50 mb-8'
|
: 'mb-8 bg-green-50'
|
||||||
: chosenProb === undefined
|
: chosenProb === undefined
|
||||||
? 'bg-gray-50'
|
? 'bg-gray-50'
|
||||||
: showChoice === 'radio'
|
: showChoice === 'radio'
|
||||||
|
@ -62,27 +63,28 @@ export function AnswerItem(props: {
|
||||||
: 'bg-blue-50'
|
: 'bg-blue-50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Col className="gap-3 flex-1">
|
<Col className="flex-1 gap-3">
|
||||||
<div className="whitespace-pre-line break-words">{text}</div>
|
<div className="whitespace-pre-line break-words">{text}</div>
|
||||||
|
|
||||||
<Row className="text-gray-500 text-sm gap-2 items-center">
|
{/* TODO: replace with ContractDetails */}
|
||||||
|
<Row className="items-center gap-2 text-sm text-gray-500">
|
||||||
<SiteLink className="relative" href={`/${username}`}>
|
<SiteLink className="relative" href={`/${username}`}>
|
||||||
<Row className="items-center gap-2">
|
<Row className="items-center gap-2">
|
||||||
<Avatar avatarUrl={avatarUrl} size={6} />
|
<Avatar avatarUrl={avatarUrl} size={6} />
|
||||||
<div className="truncate">{name}</div>
|
<div className="truncate">{name}</div>
|
||||||
</Row>
|
</Row>
|
||||||
</SiteLink>
|
</SiteLink>
|
||||||
|
|
||||||
<div className="">•</div>
|
|
||||||
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
<DateTimeTooltip text="" time={contract.createdTime}>
|
|
||||||
{createdDate}
|
|
||||||
</DateTimeTooltip>
|
|
||||||
</div>
|
|
||||||
<div className="">•</div>
|
|
||||||
<div className="text-base">#{number}</div>
|
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
{isBetting && (
|
||||||
|
<ContractFeed
|
||||||
|
contract={contract}
|
||||||
|
bets={[]}
|
||||||
|
comments={[]}
|
||||||
|
feedType="multi"
|
||||||
|
outcome={answer.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
{isBetting ? (
|
{isBetting ? (
|
||||||
|
@ -92,11 +94,11 @@ export function AnswerItem(props: {
|
||||||
closePanel={() => setIsBetting(false)}
|
closePanel={() => setIsBetting(false)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Row className="self-end sm:self-start items-center gap-4 justify-end">
|
<Row className="items-center justify-end gap-4 self-end sm:self-start">
|
||||||
{!wasResolvedTo &&
|
{!wasResolvedTo &&
|
||||||
(showChoice === 'checkbox' ? (
|
(showChoice === 'checkbox' ? (
|
||||||
<input
|
<input
|
||||||
className="input input-bordered text-2xl justify-self-end w-24"
|
className="input input-bordered w-24 justify-self-end text-2xl"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder={`${roundedProb}`}
|
placeholder={`${roundedProb}`}
|
||||||
maxLength={9}
|
maxLength={9}
|
||||||
|
@ -121,7 +123,7 @@ export function AnswerItem(props: {
|
||||||
))}
|
))}
|
||||||
{showChoice ? (
|
{showChoice ? (
|
||||||
<div className="form-control py-1">
|
<div className="form-control py-1">
|
||||||
<label className="cursor-pointer label gap-3">
|
<label className="label cursor-pointer gap-3">
|
||||||
<span className="">Choose this answer</span>
|
<span className="">Choose this answer</span>
|
||||||
{showChoice === 'radio' && (
|
{showChoice === 'radio' && (
|
||||||
<input
|
<input
|
||||||
|
@ -162,7 +164,7 @@ export function AnswerItem(props: {
|
||||||
<>
|
<>
|
||||||
{tradingAllowed(contract) && (
|
{tradingAllowed(contract) && (
|
||||||
<BuyButton
|
<BuyButton
|
||||||
className="justify-end self-end flex-initial btn-md !px-8"
|
className="btn-md flex-initial justify-end self-end !px-8"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsBetting(true)
|
setIsBetting(true)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -46,7 +46,7 @@ import { useAdmin } from '../hooks/use-admin'
|
||||||
function FeedComment(props: {
|
function FeedComment(props: {
|
||||||
activityItem: any
|
activityItem: any
|
||||||
moreHref: string
|
moreHref: string
|
||||||
feedType: 'activity' | 'market'
|
feedType: FeedType
|
||||||
}) {
|
}) {
|
||||||
const { activityItem, moreHref, feedType } = props
|
const { activityItem, moreHref, feedType } = props
|
||||||
const { person, text, amount, outcome, createdTime } = activityItem
|
const { person, text, amount, outcome, createdTime } = activityItem
|
||||||
|
@ -65,7 +65,8 @@ function FeedComment(props: {
|
||||||
username={person.username}
|
username={person.username}
|
||||||
name={person.name}
|
name={person.name}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{bought} {money} of <OutcomeLabel outcome={outcome} />{' '}
|
{bought} {money}
|
||||||
|
<MaybeOutcomeLabel outcome={outcome} feedType={feedType} />
|
||||||
<Timestamp time={createdTime} />
|
<Timestamp time={createdTime} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -90,8 +91,8 @@ function Timestamp(props: { time: number }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeedBet(props: { activityItem: any }) {
|
function FeedBet(props: { activityItem: any; feedType: FeedType }) {
|
||||||
const { activityItem } = props
|
const { activityItem, feedType } = props
|
||||||
const { id, contractId, amount, outcome, createdTime } = activityItem
|
const { id, contractId, amount, outcome, createdTime } = activityItem
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const isSelf = user?.id == activityItem.userId
|
const isSelf = user?.id == activityItem.userId
|
||||||
|
@ -122,8 +123,9 @@ function FeedBet(props: { activityItem: any }) {
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1 py-1.5">
|
<div className="min-w-0 flex-1 py-1.5">
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
<span>{isSelf ? 'You' : 'A trader'}</span> {bought} {money} of{' '}
|
<span>{isSelf ? 'You' : 'A trader'}</span> {bought} {money}
|
||||||
<OutcomeLabel outcome={outcome} /> <Timestamp time={createdTime} />
|
<MaybeOutcomeLabel outcome={outcome} feedType={feedType} />
|
||||||
|
<Timestamp time={createdTime} />
|
||||||
{canComment && (
|
{canComment && (
|
||||||
// Allow user to comment in an textarea if they are the creator
|
// Allow user to comment in an textarea if they are the creator
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
|
@ -538,8 +540,12 @@ function groupBets(
|
||||||
return items as ActivityItem[]
|
return items as ActivityItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function BetGroupSpan(props: { bets: Bet[]; outcome: string }) {
|
function BetGroupSpan(props: {
|
||||||
const { bets, outcome } = props
|
bets: Bet[]
|
||||||
|
outcome: string
|
||||||
|
feedType: FeedType
|
||||||
|
}) {
|
||||||
|
const { bets, outcome, feedType } = props
|
||||||
|
|
||||||
const numberTraders = _.uniqBy(bets, (b) => b.userId).length
|
const numberTraders = _.uniqBy(bets, (b) => b.userId).length
|
||||||
|
|
||||||
|
@ -554,14 +560,14 @@ function BetGroupSpan(props: { bets: Bet[]; outcome: string }) {
|
||||||
{buyTotal > 0 && <>bought {formatMoney(buyTotal)} </>}
|
{buyTotal > 0 && <>bought {formatMoney(buyTotal)} </>}
|
||||||
{sellTotal > 0 && <>sold {formatMoney(sellTotal)} </>}
|
{sellTotal > 0 && <>sold {formatMoney(sellTotal)} </>}
|
||||||
</JoinSpans>
|
</JoinSpans>
|
||||||
of <OutcomeLabel outcome={outcome} />
|
<MaybeOutcomeLabel outcome={outcome} feedType={feedType} />{' '}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this expandable to show all grouped bets?
|
// TODO: Make this expandable to show all grouped bets?
|
||||||
function FeedBetGroup(props: { activityItem: any }) {
|
function FeedBetGroup(props: { activityItem: any; feedType: FeedType }) {
|
||||||
const { activityItem } = props
|
const { activityItem, feedType } = props
|
||||||
const bets: Bet[] = activityItem.bets
|
const bets: Bet[] = activityItem.bets
|
||||||
|
|
||||||
const betGroups = _.groupBy(bets, (bet) => bet.outcome)
|
const betGroups = _.groupBy(bets, (bet) => bet.outcome)
|
||||||
|
@ -583,7 +589,11 @@ function FeedBetGroup(props: { activityItem: any }) {
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
{outcomes.map((outcome, index) => (
|
{outcomes.map((outcome, index) => (
|
||||||
<Fragment key={outcome}>
|
<Fragment key={outcome}>
|
||||||
<BetGroupSpan outcome={outcome} bets={betGroups[outcome]} />
|
<BetGroupSpan
|
||||||
|
outcome={outcome}
|
||||||
|
bets={betGroups[outcome]}
|
||||||
|
feedType={feedType}
|
||||||
|
/>
|
||||||
{index !== outcomes.length - 1 && <br />}
|
{index !== outcomes.length - 1 && <br />}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -621,6 +631,17 @@ function FeedExpand(props: { setExpanded: (expanded: boolean) => void }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On 'multi' feeds, the outcome is redundant, so we hide it
|
||||||
|
function MaybeOutcomeLabel(props: { outcome: string; feedType: FeedType }) {
|
||||||
|
const { outcome, feedType } = props
|
||||||
|
return feedType === 'multi' ? null : (
|
||||||
|
<span>
|
||||||
|
{' '}
|
||||||
|
of <OutcomeLabel outcome={outcome} />
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Missing feed items:
|
// Missing feed items:
|
||||||
// - Bet sold?
|
// - Bet sold?
|
||||||
type ActivityItem = {
|
type ActivityItem = {
|
||||||
|
@ -635,15 +656,23 @@ type ActivityItem = {
|
||||||
| 'expand'
|
| 'expand'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FeedType =
|
||||||
|
// Main homepage/fold feed,
|
||||||
|
| 'activity'
|
||||||
|
// Comments feed on a market
|
||||||
|
| 'market'
|
||||||
|
// Grouped for a multi-category outcome
|
||||||
|
| 'multi'
|
||||||
|
|
||||||
export function ContractFeed(props: {
|
export function ContractFeed(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
comments: Comment[]
|
comments: Comment[]
|
||||||
// Feed types: 'activity' = Activity feed, 'market' = Comments feed on a market
|
feedType: FeedType
|
||||||
feedType: 'activity' | 'market'
|
outcome?: string // Which multi-category outcome to filter
|
||||||
betRowClassName?: string
|
betRowClassName?: string
|
||||||
}) {
|
}) {
|
||||||
const { contract, feedType, betRowClassName } = props
|
const { contract, feedType, outcome, betRowClassName } = props
|
||||||
const { id, outcomeType } = contract
|
const { id, outcomeType } = contract
|
||||||
const isBinary = outcomeType === 'BINARY'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
|
|
||||||
|
@ -655,6 +684,10 @@ export function ContractFeed(props: {
|
||||||
? bets.filter((bet) => !bet.isAnte)
|
? bets.filter((bet) => !bet.isAnte)
|
||||||
: bets.filter((bet) => !(bet.isAnte && (bet.outcome as string) === '0'))
|
: bets.filter((bet) => !(bet.isAnte && (bet.outcome as string) === '0'))
|
||||||
|
|
||||||
|
if (feedType == 'multi') {
|
||||||
|
bets = bets.filter((bet) => bet.outcome === outcome)
|
||||||
|
}
|
||||||
|
|
||||||
const comments = useComments(id) ?? props.comments
|
const comments = useComments(id) ?? props.comments
|
||||||
|
|
||||||
const groupWindow = feedType == 'activity' ? 10 * DAY_IN_MS : DAY_IN_MS
|
const groupWindow = feedType == 'activity' ? 10 * DAY_IN_MS : DAY_IN_MS
|
||||||
|
@ -693,11 +726,11 @@ export function ContractFeed(props: {
|
||||||
) : null}
|
) : null}
|
||||||
<div className="relative flex items-start space-x-3">
|
<div className="relative flex items-start space-x-3">
|
||||||
{activityItem.type === 'start' ? (
|
{activityItem.type === 'start' ? (
|
||||||
feedType == 'activity' ? (
|
feedType === 'activity' ? (
|
||||||
<FeedQuestion contract={contract} />
|
<FeedQuestion contract={contract} />
|
||||||
) : (
|
) : feedType === 'market' ? (
|
||||||
<FeedDescription contract={contract} />
|
<FeedDescription contract={contract} />
|
||||||
)
|
) : null
|
||||||
) : activityItem.type === 'comment' ? (
|
) : activityItem.type === 'comment' ? (
|
||||||
<FeedComment
|
<FeedComment
|
||||||
activityItem={activityItem}
|
activityItem={activityItem}
|
||||||
|
@ -705,9 +738,9 @@ export function ContractFeed(props: {
|
||||||
feedType={feedType}
|
feedType={feedType}
|
||||||
/>
|
/>
|
||||||
) : activityItem.type === 'bet' ? (
|
) : activityItem.type === 'bet' ? (
|
||||||
<FeedBet activityItem={activityItem} />
|
<FeedBet activityItem={activityItem} feedType={feedType} />
|
||||||
) : activityItem.type === 'betgroup' ? (
|
) : activityItem.type === 'betgroup' ? (
|
||||||
<FeedBetGroup activityItem={activityItem} />
|
<FeedBetGroup activityItem={activityItem} feedType={feedType} />
|
||||||
) : activityItem.type === 'close' ? (
|
) : activityItem.type === 'close' ? (
|
||||||
<FeedClose contract={contract} />
|
<FeedClose contract={contract} />
|
||||||
) : activityItem.type === 'resolve' ? (
|
) : activityItem.type === 'resolve' ? (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user