Show a mini-feed under each FR answer

This commit is contained in:
Austin Chen 2022-02-23 14:52:14 -08:00
parent 7dcae2a141
commit 0fdcde058d
2 changed files with 74 additions and 39 deletions

View File

@ -15,6 +15,7 @@ import { formatPercent } from '../../../common/util/format'
import { getOutcomeProbability } from '../../../common/calculate'
import { tradingAllowed } from '../../lib/firebase/contracts'
import { AnswerBetPanel } from './answer-bet-panel'
import { ContractFeed } from '../contract-feed'
export function AnswerItem(props: {
answer: Answer
@ -50,11 +51,11 @@ export function AnswerItem(props: {
return (
<Col
className={clsx(
'p-4 sm:flex-row rounded gap-4',
'gap-4 rounded p-4 sm:flex-row',
wasResolvedTo
? resolution === 'MKT'
? 'bg-blue-50 mb-2'
: 'bg-green-50 mb-8'
? 'mb-2 bg-blue-50'
: 'mb-8 bg-green-50'
: chosenProb === undefined
? 'bg-gray-50'
: showChoice === 'radio'
@ -62,27 +63,28 @@ export function AnswerItem(props: {
: 'bg-blue-50'
)}
>
<Col className="gap-3 flex-1">
<Col className="flex-1 gap-3">
<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}`}>
<Row className="items-center gap-2">
<Avatar avatarUrl={avatarUrl} size={6} />
<div className="truncate">{name}</div>
</Row>
</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>
{isBetting && (
<ContractFeed
contract={contract}
bets={[]}
comments={[]}
feedType="multi"
outcome={answer.id}
/>
)}
</Col>
{isBetting ? (
@ -92,11 +94,11 @@ export function AnswerItem(props: {
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 &&
(showChoice === 'checkbox' ? (
<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"
placeholder={`${roundedProb}`}
maxLength={9}
@ -121,7 +123,7 @@ export function AnswerItem(props: {
))}
{showChoice ? (
<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>
{showChoice === 'radio' && (
<input
@ -162,7 +164,7 @@ export function AnswerItem(props: {
<>
{tradingAllowed(contract) && (
<BuyButton
className="justify-end self-end flex-initial btn-md !px-8"
className="btn-md flex-initial justify-end self-end !px-8"
onClick={() => {
setIsBetting(true)
}}

View File

@ -46,7 +46,7 @@ import { useAdmin } from '../hooks/use-admin'
function FeedComment(props: {
activityItem: any
moreHref: string
feedType: 'activity' | 'market'
feedType: FeedType
}) {
const { activityItem, moreHref, feedType } = props
const { person, text, amount, outcome, createdTime } = activityItem
@ -65,7 +65,8 @@ function FeedComment(props: {
username={person.username}
name={person.name}
/>{' '}
{bought} {money} of <OutcomeLabel outcome={outcome} />{' '}
{bought} {money}
<MaybeOutcomeLabel outcome={outcome} feedType={feedType} />
<Timestamp time={createdTime} />
</p>
</div>
@ -90,8 +91,8 @@ function Timestamp(props: { time: number }) {
)
}
function FeedBet(props: { activityItem: any }) {
const { activityItem } = props
function FeedBet(props: { activityItem: any; feedType: FeedType }) {
const { activityItem, feedType } = props
const { id, contractId, amount, outcome, createdTime } = activityItem
const user = useUser()
const isSelf = user?.id == activityItem.userId
@ -122,8 +123,9 @@ function FeedBet(props: { activityItem: any }) {
</div>
<div className="min-w-0 flex-1 py-1.5">
<div className="text-sm text-gray-500">
<span>{isSelf ? 'You' : 'A trader'}</span> {bought} {money} of{' '}
<OutcomeLabel outcome={outcome} /> <Timestamp time={createdTime} />
<span>{isSelf ? 'You' : 'A trader'}</span> {bought} {money}
<MaybeOutcomeLabel outcome={outcome} feedType={feedType} />
<Timestamp time={createdTime} />
{canComment && (
// Allow user to comment in an textarea if they are the creator
<div className="mt-2">
@ -538,8 +540,12 @@ function groupBets(
return items as ActivityItem[]
}
function BetGroupSpan(props: { bets: Bet[]; outcome: string }) {
const { bets, outcome } = props
function BetGroupSpan(props: {
bets: Bet[]
outcome: string
feedType: FeedType
}) {
const { bets, outcome, feedType } = props
const numberTraders = _.uniqBy(bets, (b) => b.userId).length
@ -554,14 +560,14 @@ function BetGroupSpan(props: { bets: Bet[]; outcome: string }) {
{buyTotal > 0 && <>bought {formatMoney(buyTotal)} </>}
{sellTotal > 0 && <>sold {formatMoney(sellTotal)} </>}
</JoinSpans>
of <OutcomeLabel outcome={outcome} />
<MaybeOutcomeLabel outcome={outcome} feedType={feedType} />{' '}
</span>
)
}
// TODO: Make this expandable to show all grouped bets?
function FeedBetGroup(props: { activityItem: any }) {
const { activityItem } = props
function FeedBetGroup(props: { activityItem: any; feedType: FeedType }) {
const { activityItem, feedType } = props
const bets: Bet[] = activityItem.bets
const betGroups = _.groupBy(bets, (bet) => bet.outcome)
@ -583,7 +589,11 @@ function FeedBetGroup(props: { activityItem: any }) {
<div className="text-sm text-gray-500">
{outcomes.map((outcome, index) => (
<Fragment key={outcome}>
<BetGroupSpan outcome={outcome} bets={betGroups[outcome]} />
<BetGroupSpan
outcome={outcome}
bets={betGroups[outcome]}
feedType={feedType}
/>
{index !== outcomes.length - 1 && <br />}
</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:
// - Bet sold?
type ActivityItem = {
@ -635,15 +656,23 @@ type ActivityItem = {
| '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: {
contract: Contract
bets: Bet[]
comments: Comment[]
// Feed types: 'activity' = Activity feed, 'market' = Comments feed on a market
feedType: 'activity' | 'market'
feedType: FeedType
outcome?: string // Which multi-category outcome to filter
betRowClassName?: string
}) {
const { contract, feedType, betRowClassName } = props
const { contract, feedType, outcome, betRowClassName } = props
const { id, outcomeType } = contract
const isBinary = outcomeType === 'BINARY'
@ -655,6 +684,10 @@ export function ContractFeed(props: {
? bets.filter((bet) => !bet.isAnte)
: 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 groupWindow = feedType == 'activity' ? 10 * DAY_IN_MS : DAY_IN_MS
@ -693,11 +726,11 @@ export function ContractFeed(props: {
) : null}
<div className="relative flex items-start space-x-3">
{activityItem.type === 'start' ? (
feedType == 'activity' ? (
feedType === 'activity' ? (
<FeedQuestion contract={contract} />
) : (
) : feedType === 'market' ? (
<FeedDescription contract={contract} />
)
) : null
) : activityItem.type === 'comment' ? (
<FeedComment
activityItem={activityItem}
@ -705,9 +738,9 @@ export function ContractFeed(props: {
feedType={feedType}
/>
) : activityItem.type === 'bet' ? (
<FeedBet activityItem={activityItem} />
<FeedBet activityItem={activityItem} feedType={feedType} />
) : activityItem.type === 'betgroup' ? (
<FeedBetGroup activityItem={activityItem} />
<FeedBetGroup activityItem={activityItem} feedType={feedType} />
) : activityItem.type === 'close' ? (
<FeedClose contract={contract} />
) : activityItem.type === 'resolve' ? (