FR: Use nested comments/bets under answers for contract page. filter more items out of FR feed.

This commit is contained in:
James Grugett 2022-03-14 01:37:54 -05:00
parent 328593ecb0
commit f8c4bfe5de
3 changed files with 102 additions and 61 deletions

View File

@ -2,11 +2,12 @@ import _ from 'lodash'
import { Answer } from '../../../common/answer' import { Answer } from '../../../common/answer'
import { Bet } from '../../../common/bet' import { Bet } from '../../../common/bet'
import { getOutcomeProbability } from '../../../common/calculate'
import { Comment } from '../../../common/comment' import { Comment } from '../../../common/comment'
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
import { User } from '../../../common/user' import { User } from '../../../common/user'
import { filterDefined } from '../../../common/util/array' import { filterDefined } from '../../../common/util/array'
import { canAddComment, mapCommentsByBetId } from '../../lib/firebase/comments' import { mapCommentsByBetId } from '../../lib/firebase/comments'
export type ActivityItem = export type ActivityItem =
| DescriptionItem | DescriptionItem
@ -43,7 +44,7 @@ export type CommentItem = BaseActivityItem & {
type: 'comment' type: 'comment'
comment: Comment comment: Comment
bet: Bet bet: Bet
showOutcomeLabel: boolean hideOutcome: boolean
truncate: boolean truncate: boolean
} }
@ -85,8 +86,13 @@ function groupBets(
windowMs: number, windowMs: number,
contract: Contract, contract: Contract,
userId: string | undefined, userId: string | undefined,
hideOutcome: boolean options: {
hideOutcome: boolean
truncateComments: boolean
}
) { ) {
const { hideOutcome, truncateComments } = options
const commentsMap = mapCommentsByBetId(comments) const commentsMap = mapCommentsByBetId(comments)
const items: ActivityItem[] = [] const items: ActivityItem[] = []
let group: Bet[] = [] let group: Bet[] = []
@ -107,7 +113,7 @@ function groupBets(
group = [] group = []
} }
function toActivityItem(bet: Bet) { function toActivityItem(bet: Bet): ActivityItem {
const comment = commentsMap[bet.id] const comment = commentsMap[bet.id]
return comment return comment
? { ? {
@ -116,10 +122,16 @@ function groupBets(
comment, comment,
bet, bet,
contract, contract,
showOutcomeLabel: !hideOutcome, hideOutcome,
truncate: true, truncate: truncateComments,
}
: {
type: 'bet' as const,
id: bet.id,
bet,
contract,
hideOutcome,
} }
: { type: 'bet' as const, id: bet.id, bet, contract, hideOutcome }
} }
for (const bet of bets) { for (const bet of bets) {
@ -150,24 +162,23 @@ function getAnswerGroups(
contract: Contract, contract: Contract,
bets: Bet[], bets: Bet[],
comments: Comment[], comments: Comment[],
user: User | undefined | null user: User | undefined | null,
options: {
truncateComments: boolean
sortByProb: boolean
}
) { ) {
// Keep last two comments. const { truncateComments, sortByProb } = options
comments = comments.slice(-2)
const lastBet = bets[bets.length - 1]
// Include up to 2 outcomes from comments and last bet. let outcomes = _.uniq(bets.map((bet) => bet.outcome)).filter(
const outcomes = filterDefined( (outcome) => getOutcomeProbability(contract.totalShares, outcome) > 0.01
_.uniq([ )
...comments.map( if (sortByProb) {
(comment) => bets.find((bet) => bet.id === comment.betId)?.outcome outcomes = _.sortBy(
), outcomes,
lastBet?.outcome, (outcome) => -1 * getOutcomeProbability(contract.totalShares, outcome)
]) )
).slice(0, 2) }
// Keep bets on selected outcomes.
bets = bets.filter((bet) => outcomes.includes(bet.outcome))
const answerGroups = outcomes.map((outcome) => { const answerGroups = outcomes.map((outcome) => {
const answerBets = bets.filter((bet) => bet.outcome === outcome) const answerBets = bets.filter((bet) => bet.outcome === outcome)
@ -178,13 +189,13 @@ function getAnswerGroups(
(answer) => answer.id === outcome (answer) => answer.id === outcome
) as Answer ) as Answer
const answerItems = groupBets( const items = groupBets(
answerBets, answerBets,
answerComments, answerComments,
DAY_IN_MS, DAY_IN_MS,
contract, contract,
user?.id, user?.id,
true { hideOutcome: true, truncateComments }
) )
return { return {
@ -192,7 +203,7 @@ function getAnswerGroups(
type: 'answergroup' as const, type: 'answergroup' as const,
contract, contract,
answer, answer,
items: answerItems, items,
user, user,
} }
}) })
@ -208,24 +219,16 @@ export function getAllContractActivityItems(
outcome?: string outcome?: string
) { ) {
const { outcomeType } = contract const { outcomeType } = contract
const isBinary = outcomeType === 'BINARY'
bets = isBinary bets =
? bets.filter((bet) => !bet.isAnte) outcomeType === 'BINARY'
: bets.filter((bet) => !(bet.isAnte && (bet.outcome as string) === '0')) ? bets.filter((bet) => !bet.isAnte)
: bets.filter((bet) => !(bet.isAnte && (bet.outcome as string) === '0'))
let answer: Answer | undefined let answer: Answer | undefined
if (outcome) { if (outcome) {
bets = bets.filter((bet) => bet.outcome === outcome) bets = bets.filter((bet) => bet.outcome === outcome)
answer = contract.answers?.find((answer) => answer.id === outcome) answer = contract.answers?.find((answer) => answer.id === outcome)
} else if (outcomeType === 'FREE_RESPONSE') {
// Keep bets on comments or your bets where you can comment.
const commentBetIds = new Set(comments.map((comment) => comment.betId))
bets = bets.filter(
(bet) =>
commentBetIds.has(bet.id) ||
canAddComment(bet.createdTime, user?.id === bet.userId)
)
} }
const items: ActivityItem[] = const items: ActivityItem[] =
@ -234,7 +237,15 @@ export function getAllContractActivityItems(
: [{ type: 'description', id: '0', contract }] : [{ type: 'description', id: '0', contract }]
items.push( items.push(
...groupBets(bets, comments, DAY_IN_MS, contract, user?.id, !!outcome) ...(outcomeType === 'FREE_RESPONSE' && !outcome
? getAnswerGroups(contract, bets, comments, user, {
truncateComments: false,
sortByProb: true,
})
: groupBets(bets, comments, DAY_IN_MS, contract, user?.id, {
hideOutcome: !!outcome,
truncateComments: false,
}))
) )
if (contract.closeTime && contract.closeTime <= Date.now()) { if (contract.closeTime && contract.closeTime <= Date.now()) {
@ -256,18 +267,6 @@ export function getRecentContractActivityItems(
bets = bets.sort((b1, b2) => b1.createdTime - b2.createdTime) bets = bets.sort((b1, b2) => b1.createdTime - b2.createdTime)
comments = comments.sort((c1, c2) => c1.createdTime - c2.createdTime) comments = comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
const items: ActivityItem[] =
contract.outcomeType === 'FREE_RESPONSE'
? getAnswerGroups(contract, bets, comments, user)
: groupBets(bets, comments, DAY_IN_MS, contract, user?.id, false)
// Remove all but last bet group.
const betGroups = items.filter((item) => item.type === 'betgroup')
const lastBetGroup = betGroups[betGroups.length - 1]
const filtered = items.filter(
(item) => item.type !== 'betgroup' || item.id === lastBetGroup?.id
)
const questionItem: QuestionItem = { const questionItem: QuestionItem = {
type: 'question', type: 'question',
id: '0', id: '0',
@ -275,9 +274,52 @@ export function getRecentContractActivityItems(
showDescription: false, showDescription: false,
} }
return [ let items: ActivityItem[] = []
questionItem,
// Only take the last three items. if (contract.outcomeType === 'FREE_RESPONSE') {
...filtered.slice(-3), // Keep last two comments.
] comments = comments.slice(-2)
const lastBet = bets[bets.length - 1]
const outcomeToComments = _.groupBy(comments, (c) => {
const bet = bets.find((bet) => bet.id === c.betId)
return bet?.outcome
})
delete outcomeToComments['undefined']
// Include up to 2 outcomes from comments and last bet.
const outcomes = filterDefined(
_.uniq([...Object.keys(outcomeToComments), lastBet?.outcome])
).slice(-2)
// Keep bets on selected outcomes.
bets = bets.filter((bet) => outcomes.includes(bet.outcome))
// Filter out bets before comments.
bets = bets.filter((bet) => {
const comments = outcomeToComments[bet.outcome]
if (
!comments ||
comments.length === 0 ||
comments.some((c) => c.betId === bet.id)
)
return true
return comments.every((c) => c.createdTime <= bet.createdTime)
})
items.push(
...getAnswerGroups(contract, bets, comments, user, {
truncateComments: true,
sortByProb: false,
})
)
} else {
items.push(
...groupBets(bets, comments, DAY_IN_MS, contract, user?.id, {
hideOutcome: false,
truncateComments: true,
})
)
}
return [questionItem, ...items.slice(-3)]
} }

View File

@ -1,4 +1,4 @@
import _, { update } from 'lodash' import _ from 'lodash'
import { Contract } from '../../lib/firebase/contracts' import { Contract } from '../../lib/firebase/contracts'
import { Comment } from '../../lib/firebase/comments' import { Comment } from '../../lib/firebase/comments'

View File

@ -110,10 +110,10 @@ function FeedComment(props: {
contract: Contract contract: Contract
comment: Comment comment: Comment
bet: Bet bet: Bet
showOutcomeLabel: boolean hideOutcome: boolean
truncate: boolean truncate: boolean
}) { }) {
const { contract, comment, bet, showOutcomeLabel, truncate } = props const { contract, comment, bet, hideOutcome, truncate } = props
const { amount, outcome } = bet const { amount, outcome } = bet
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
@ -132,7 +132,7 @@ function FeedComment(props: {
name={userName} name={userName}
/>{' '} />{' '}
{bought} {money} {bought} {money}
{showOutcomeLabel && ( {!hideOutcome && (
<> <>
{' '} {' '}
of <OutcomeLabel outcome={outcome} /> of <OutcomeLabel outcome={outcome} />
@ -615,7 +615,6 @@ function BetGroupSpan(props: { bets: Bet[]; outcome?: string }) {
) )
} }
// TODO: Make this expandable to show all grouped bets?
function FeedBetGroup(props: { function FeedBetGroup(props: {
contract: Contract contract: Contract
bets: Bet[] bets: Bet[]