From 5c3d571cc44424f262c7b2115bc6e93834abe006 Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Sun, 20 Mar 2022 10:53:16 -0700 Subject: [PATCH] Also show the top comment and trade --- web/components/feed/feed-items.tsx | 18 ++++-- web/hooks/use-users.ts | 16 ++++- web/pages/[username]/[contractSlug].tsx | 84 ++++++++++++++++++++++++- 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/web/components/feed/feed-items.tsx b/web/components/feed/feed-items.tsx index db526741..c248a8ca 100644 --- a/web/components/feed/feed-items.tsx +++ b/web/components/feed/feed-items.tsx @@ -47,6 +47,7 @@ import { BuyButton } from '../yes-no-selector' import { getDpmOutcomeProbability } from '../../../common/calculate-dpm' import { AnswerBetPanel } from '../answers/answer-bet-panel' import { useSaveSeenContract } from '../../hooks/use-seen-contracts' +import { User } from '../../../common/user' export function FeedItems(props: { contract: Contract @@ -109,7 +110,7 @@ function FeedItem(props: { item: ActivityItem }) { } } -function FeedComment(props: { +export function FeedComment(props: { contract: Contract comment: Comment bet: Bet @@ -171,13 +172,14 @@ function RelativeTimestamp(props: { time: number }) { ) } -function FeedBet(props: { +export function FeedBet(props: { contract: Contract bet: Bet hideOutcome: boolean smallAvatar: boolean + bettor?: User // If set: reveal bettor identity }) { - const { contract, bet, hideOutcome, smallAvatar } = props + const { contract, bet, hideOutcome, smallAvatar, bettor } = props const { id, amount, outcome, createdTime, userId } = bet const user = useUser() const isSelf = user?.id === userId @@ -204,6 +206,13 @@ function FeedBet(props: { avatarUrl={user.avatarUrl} username={user.username} /> + ) : bettor ? ( + ) : (
@@ -214,7 +223,8 @@ function FeedBet(props: {
- {isSelf ? 'You' : 'A trader'} {bought} {money} + {isSelf ? 'You' : bettor ? bettor.name : 'A trader'}{' '} + {bought} {money} {!hideOutcome && ( <> {' '} diff --git a/web/hooks/use-users.ts b/web/hooks/use-users.ts index fbf5feaf..35244d73 100644 --- a/web/hooks/use-users.ts +++ b/web/hooks/use-users.ts @@ -1,6 +1,10 @@ import { useState, useEffect } from 'react' import { PrivateUser, User } from '../../common/user' -import { listenForAllUsers, listenForPrivateUsers } from '../lib/firebase/users' +import { + getUser, + listenForAllUsers, + listenForPrivateUsers, +} from '../lib/firebase/users' export const useUsers = () => { const [users, setUsers] = useState([]) @@ -12,6 +16,16 @@ export const useUsers = () => { return users } +export const useUserById = (userId: string) => { + const [user, setUser] = useState(undefined) + + useEffect(() => { + getUser(userId).then(setUser) + }, [userId]) + + return user +} + export const usePrivateUsers = () => { const [users, setUsers] = useState([]) diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx index 7bb12fd3..bb656225 100644 --- a/web/pages/[username]/[contractSlug].tsx +++ b/web/pages/[username]/[contractSlug].tsx @@ -34,6 +34,8 @@ import { Leaderboard } from '../../components/leaderboard' import _ from 'lodash' import { calculatePayout, resolvedPayout } from '../../../common/calculate' import { formatMoney } from '../../../common/util/format' +import { FeedBet, FeedComment } from '../../components/feed/feed-items' +import { useUserById } from '../../hooks/use-users' export const getStaticProps = fromPropz(getStaticPropz) export async function getStaticPropz(props: { @@ -152,7 +154,17 @@ export default function ContractPage(props: { {contract.isResolved && ( - + <> +
+ + +
+ + )}
@@ -243,6 +255,76 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) { ) : null } +function ContractTopTrades(props: { + contract: Contract + bets: Bet[] + comments: Comment[] +}) { + const { contract, bets, comments } = props + + // Also find the highest-profit comment (by profit? by percent?). + // This could be a resolved buy, + // Harder: a sold bet (have to link sold ) + // (not sure how to account for CFMM share redemptions...?) + + // If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit + // Otherwise, we record the profit at resolution time + const commentsById = _.keyBy(comments, 'id') + const betsById = _.keyBy(bets, 'id') + const profitById: Record = {} + for (const bet of bets) { + if (bet.sale) { + const originalBet = betsById[bet.sale.betId] + const profit = bet.sale.amount - originalBet.amount + profitById[bet.id] = profit + profitById[originalBet.id] = profit + } else { + profitById[bet.id] = resolvedPayout(contract, bet) - bet.amount + } + } + + // Now find the betId with the highest profit + const topBetId = _.sortBy(bets, (b) => -profitById[b.id])[0].id + const topBettor = useUserById(betsById[topBetId].userId) + + // And also the betId of the comment with the highest profit + const topCommentId = _.sortBy(comments, (c) => -profitById[c.id])[0].id + + // TODO: If they're the same, only show the comment; otherwise show both + return ( +
+ + <div className="relative flex items-start space-x-3"> + <FeedComment + contract={contract} + comment={commentsById[topCommentId]} + bet={betsById[topCommentId]} + hideOutcome={false} + truncate={false} + smallAvatar={false} + /> + </div> + <div className="mt-4 text-sm text-gray-600"> + (And made {formatMoney(profitById[topCommentId] || 0)}) + </div> + <Spacer h={16} /> + <Title text="💸 Top trade" className="!mt-0" /> + <div className="relative flex items-start space-x-3"> + <FeedBet + contract={contract} + bet={betsById[topBetId]} + hideOutcome={false} + smallAvatar={false} + bettor={topBettor} + /> + </div> + <div className="mt-4 text-sm text-gray-600"> + (And made {formatMoney(profitById[topBetId] || 0)}) + </div> + </div> + ) +} + const getOpenGraphProps = (contract: Contract) => { const { resolution, question, creatorName, creatorUsername, outcomeType } = contract