2022-07-22 02:39:06 +00:00
|
|
|
import { Bet } from 'common/bet'
|
|
|
|
import { resolvedPayout } from 'common/calculate'
|
|
|
|
import { Contract } from 'common/contract'
|
|
|
|
import { formatMoney } from 'common/util/format'
|
|
|
|
import { groupBy, mapValues, sumBy, sortBy, keyBy } from 'lodash'
|
2022-09-22 19:40:27 +00:00
|
|
|
import { memo } from 'react'
|
2022-09-21 07:02:10 +00:00
|
|
|
import { useComments } from 'web/hooks/use-comments'
|
2022-07-22 02:39:06 +00:00
|
|
|
import { FeedBet } from '../feed/feed-bets'
|
|
|
|
import { FeedComment } from '../feed/feed-comments'
|
|
|
|
import { Spacer } from '../layout/spacer'
|
|
|
|
import { Leaderboard } from '../leaderboard'
|
|
|
|
import { Title } from '../title'
|
2022-09-15 15:12:56 +00:00
|
|
|
import { BETTORS } from 'common/user'
|
2022-07-22 02:39:06 +00:00
|
|
|
|
2022-09-22 19:40:27 +00:00
|
|
|
export const ContractLeaderboard = memo(function ContractLeaderboard(props: {
|
2022-07-22 02:39:06 +00:00
|
|
|
contract: Contract
|
|
|
|
bets: Bet[]
|
|
|
|
}) {
|
|
|
|
const { contract, bets } = props
|
|
|
|
|
2022-09-22 19:40:27 +00:00
|
|
|
// Create a map of userIds to total profits (including sales)
|
|
|
|
const openBets = bets.filter((bet) => !bet.isSold && !bet.sale)
|
|
|
|
const betsByUser = groupBy(openBets, 'userId')
|
|
|
|
const userProfits = mapValues(betsByUser, (bets) => {
|
|
|
|
return {
|
|
|
|
name: bets[0].userName,
|
|
|
|
username: bets[0].userUsername,
|
|
|
|
avatarUrl: bets[0].userAvatarUrl,
|
|
|
|
total: sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount),
|
2022-07-22 02:39:06 +00:00
|
|
|
}
|
2022-09-22 19:40:27 +00:00
|
|
|
})
|
|
|
|
// Find the 5 users with the most profits
|
|
|
|
const top5 = Object.values(userProfits)
|
|
|
|
.sort((p1, p2) => p2.total - p1.total)
|
|
|
|
.filter((p) => p.total > 0)
|
|
|
|
.slice(0, 5)
|
2022-07-22 02:39:06 +00:00
|
|
|
|
2022-09-22 19:40:27 +00:00
|
|
|
return top5 && top5.length > 0 ? (
|
2022-07-22 02:39:06 +00:00
|
|
|
<Leaderboard
|
2022-09-15 15:12:56 +00:00
|
|
|
title={`🏅 Top ${BETTORS}`}
|
2022-09-22 19:40:27 +00:00
|
|
|
entries={top5 || []}
|
2022-07-22 02:39:06 +00:00
|
|
|
columns={[
|
|
|
|
{
|
|
|
|
header: 'Total profit',
|
2022-09-22 19:40:27 +00:00
|
|
|
renderCell: (entry) => formatMoney(entry.total),
|
2022-07-22 02:39:06 +00:00
|
|
|
},
|
|
|
|
]}
|
|
|
|
className="mt-12 max-w-sm"
|
|
|
|
/>
|
|
|
|
) : null
|
2022-09-22 19:40:27 +00:00
|
|
|
})
|
2022-07-22 02:39:06 +00:00
|
|
|
|
2022-09-21 07:02:10 +00:00
|
|
|
export function ContractTopTrades(props: { contract: Contract; bets: Bet[] }) {
|
|
|
|
const { contract, bets } = props
|
|
|
|
// todo: this stuff should be calced in DB at resolve time
|
|
|
|
const comments = useComments(contract.id)
|
2022-07-22 02:39:06 +00:00
|
|
|
const betsById = keyBy(bets, 'id')
|
|
|
|
|
|
|
|
// If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
|
|
|
|
// Otherwise, we record the profit at resolution time
|
|
|
|
const profitById: Record<string, number> = {}
|
|
|
|
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
|
2022-09-14 08:33:59 +00:00
|
|
|
const topBettor = betsById[topBetId]?.userName
|
2022-07-22 02:39:06 +00:00
|
|
|
|
2022-09-22 19:40:27 +00:00
|
|
|
// And also the comment with the highest profit
|
|
|
|
const topComment = sortBy(comments, (c) => c.betId && -profitById[c.betId])[0]
|
2022-07-22 02:39:06 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="mt-12 max-w-sm">
|
2022-09-22 19:40:27 +00:00
|
|
|
{topComment && profitById[topComment.id] > 0 && (
|
2022-07-22 02:39:06 +00:00
|
|
|
<>
|
|
|
|
<Title text="💬 Proven correct" className="!mt-0" />
|
|
|
|
<div className="relative flex items-start space-x-3 rounded-md bg-gray-50 px-2 py-4">
|
2022-09-22 19:40:27 +00:00
|
|
|
<FeedComment contract={contract} comment={topComment} />
|
2022-07-22 02:39:06 +00:00
|
|
|
</div>
|
|
|
|
<Spacer h={16} />
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{/* If they're the same, only show the comment; otherwise show both */}
|
2022-09-22 19:40:27 +00:00
|
|
|
{topBettor && topBetId !== topComment?.betId && profitById[topBetId] > 0 && (
|
2022-07-22 02:39:06 +00:00
|
|
|
<>
|
2022-09-04 19:06:29 +00:00
|
|
|
<Title text="💸 Best bet" className="!mt-0" />
|
2022-07-22 02:39:06 +00:00
|
|
|
<div className="relative flex items-start space-x-3 rounded-md bg-gray-50 px-2 py-4">
|
2022-08-30 09:41:47 +00:00
|
|
|
<FeedBet contract={contract} bet={betsById[topBetId]} />
|
2022-07-22 02:39:06 +00:00
|
|
|
</div>
|
2022-09-04 19:06:29 +00:00
|
|
|
<div className="mt-2 ml-2 text-sm text-gray-500">
|
2022-09-14 08:33:59 +00:00
|
|
|
{topBettor} made {formatMoney(profitById[topBetId] || 0)}!
|
2022-07-22 02:39:06 +00:00
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|