diff --git a/common/comment.ts b/common/comment.ts index c7f9b855..3a4bd9ac 100644 --- a/common/comment.ts +++ b/common/comment.ts @@ -23,10 +23,16 @@ export type Comment = { type OnContract = { commentType: 'contract' contractId: string - contractSlug: string - contractQuestion: string answerOutcome?: string betId?: string + + // denormalized from contract + contractSlug: string + contractQuestion: string + + // denormalized from bet + betAmount?: number + betOutcome?: string } type OnGroup = { diff --git a/functions/src/on-create-comment-on-contract.ts b/functions/src/on-create-comment-on-contract.ts index 663a7977..a36a8bca 100644 --- a/functions/src/on-create-comment-on-contract.ts +++ b/functions/src/on-create-comment-on-contract.ts @@ -63,11 +63,15 @@ export const onCreateCommentOnContract = functions .doc(comment.betId) .get() bet = betSnapshot.data() as Bet - answer = contract.outcomeType === 'FREE_RESPONSE' && contract.answers ? contract.answers.find((answer) => answer.id === bet?.outcome) : undefined + + await change.ref.update({ + betOutcome: bet.outcome, + betAmount: bet.amount, + }) } const comments = await getValues( diff --git a/functions/src/scripts/denormalize-comment-bet-data.ts b/functions/src/scripts/denormalize-comment-bet-data.ts new file mode 100644 index 00000000..929626c3 --- /dev/null +++ b/functions/src/scripts/denormalize-comment-bet-data.ts @@ -0,0 +1,69 @@ +// Filling in the bet-based fields on comments. + +import * as admin from 'firebase-admin' +import { zip } from 'lodash' +import { initAdmin } from './script-init' +import { + DocumentCorrespondence, + findDiffs, + describeDiff, + applyDiff, +} from './denormalize' +import { log } from '../utils' +import { Transaction } from 'firebase-admin/firestore' + +initAdmin() +const firestore = admin.firestore() + +async function getBetComments(transaction: Transaction) { + const allComments = await transaction.get( + firestore.collectionGroup('comments') + ) + const betComments = allComments.docs.filter((d) => d.get('betId')) + log(`Found ${betComments.length} comments associated with bets.`) + return betComments +} + +async function denormalize() { + let hasMore = true + while (hasMore) { + hasMore = await admin.firestore().runTransaction(async (trans) => { + const betComments = await getBetComments(trans) + const bets = await Promise.all( + betComments.map((doc) => + trans.get( + firestore + .collection('contracts') + .doc(doc.get('contractId')) + .collection('bets') + .doc(doc.get('betId')) + ) + ) + ) + log(`Found ${bets.length} bets associated with comments.`) + const mapping = zip(bets, betComments) + .map(([bet, comment]): DocumentCorrespondence => { + return [bet!, [comment!]] // eslint-disable-line + }) + .filter(([bet, _]) => bet.exists) // dev DB has some invalid bet IDs + + const amountDiffs = findDiffs(mapping, 'amount', 'betAmount') + const outcomeDiffs = findDiffs(mapping, 'outcome', 'betOutcome') + log(`Found ${amountDiffs.length} comments with mismatched amounts.`) + log(`Found ${outcomeDiffs.length} comments with mismatched outcomes.`) + const diffs = amountDiffs.concat(outcomeDiffs) + diffs.slice(0, 500).forEach((d) => { + log(describeDiff(d)) + applyDiff(trans, d) + }) + if (diffs.length > 500) { + console.log(`Applying first 500 because of Firestore limit...`) + } + return diffs.length > 500 + }) + } +} + +if (require.main === module) { + denormalize().catch((e) => console.error(e)) +} diff --git a/web/components/feed/feed-comments.tsx b/web/components/feed/feed-comments.tsx index 1aebb27b..fa2cc6f5 100644 --- a/web/components/feed/feed-comments.tsx +++ b/web/components/feed/feed-comments.tsx @@ -125,15 +125,12 @@ export function FeedComment(props: { } = props const { text, content, userUsername, userName, userAvatarUrl, createdTime } = comment - let betOutcome: string | undefined, - bought: string | undefined, - money: string | undefined - - const matchedBet = betsBySameUser.find((bet) => bet.id === comment.betId) - if (matchedBet) { - betOutcome = matchedBet.outcome - bought = matchedBet.amount >= 0 ? 'bought' : 'sold' - money = formatMoney(Math.abs(matchedBet.amount)) + const betOutcome = comment.betOutcome + let bought: string | undefined + let money: string | undefined + if (comment.betAmount != null) { + bought = comment.betAmount >= 0 ? 'bought' : 'sold' + money = formatMoney(Math.abs(comment.betAmount)) } const [highlighted, setHighlighted] = useState(false) @@ -148,7 +145,7 @@ export function FeedComment(props: { const { userPosition, outcome } = getBettorsLargestPositionBeforeTime( contract, comment.createdTime, - matchedBet ? [] : betsBySameUser + comment.betId ? [] : betsBySameUser ) return ( @@ -175,7 +172,7 @@ export function FeedComment(props: { username={userUsername} name={userName} />{' '} - {!matchedBet && + {!comment.betId != null && userPosition > 0 && contract.outcomeType !== 'NUMERIC' && ( <> @@ -194,7 +191,6 @@ export function FeedComment(props: { of{' '}