Add market liquidity addition events to bets feed (#578)

* Add liquidity events to bets feed

* Use larger avatar for liquidity feed items
This commit is contained in:
Ben Congdon 2022-06-26 17:00:02 -07:00 committed by GitHub
parent 0067bee94b
commit 3b6ba76db6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 132 additions and 3 deletions

View File

@ -8,15 +8,17 @@ import { Spacer } from '../layout/spacer'
import { Tabs } from '../layout/tabs' import { Tabs } from '../layout/tabs'
import { Col } from '../layout/col' import { Col } from '../layout/col'
import { CommentTipMap } from 'web/hooks/use-tip-txns' import { CommentTipMap } from 'web/hooks/use-tip-txns'
import { LiquidityProvision } from 'common/liquidity-provision'
export function ContractTabs(props: { export function ContractTabs(props: {
contract: Contract contract: Contract
user: User | null | undefined user: User | null | undefined
bets: Bet[] bets: Bet[]
liquidityProvisions: LiquidityProvision[]
comments: Comment[] comments: Comment[]
tips: CommentTipMap tips: CommentTipMap
}) { }) {
const { contract, user, bets, comments, tips } = props const { contract, user, bets, comments, tips, liquidityProvisions } = props
const { outcomeType } = contract const { outcomeType } = contract
const userBets = user && bets.filter((bet) => bet.userId === user.id) const userBets = user && bets.filter((bet) => bet.userId === user.id)
@ -25,6 +27,7 @@ export function ContractTabs(props: {
<ContractActivity <ContractActivity
contract={contract} contract={contract}
bets={bets} bets={bets}
liquidityProvisions={liquidityProvisions}
comments={comments} comments={comments}
tips={tips} tips={tips}
user={user} user={user}
@ -38,6 +41,7 @@ export function ContractTabs(props: {
<ContractActivity <ContractActivity
contract={contract} contract={contract}
bets={bets} bets={bets}
liquidityProvisions={liquidityProvisions}
comments={comments} comments={comments}
tips={tips} tips={tips}
user={user} user={user}
@ -55,6 +59,7 @@ export function ContractTabs(props: {
<ContractActivity <ContractActivity
contract={contract} contract={contract}
bets={bets} bets={bets}
liquidityProvisions={liquidityProvisions}
comments={comments} comments={comments}
tips={tips} tips={tips}
user={user} user={user}

View File

@ -7,6 +7,7 @@ import { Comment } from 'common/comment'
import { Contract, FreeResponseContract } from 'common/contract' import { Contract, FreeResponseContract } from 'common/contract'
import { User } from 'common/user' import { User } from 'common/user'
import { CommentTipMap } from 'web/hooks/use-tip-txns' import { CommentTipMap } from 'web/hooks/use-tip-txns'
import { LiquidityProvision } from 'common/liquidity-provision'
export type ActivityItem = export type ActivityItem =
| DescriptionItem | DescriptionItem
@ -17,6 +18,7 @@ export type ActivityItem =
| ResolveItem | ResolveItem
| CommentInputItem | CommentInputItem
| CommentThreadItem | CommentThreadItem
| LiquidityItem
type BaseActivityItem = { type BaseActivityItem = {
id: string id: string
@ -72,6 +74,14 @@ export type ResolveItem = BaseActivityItem & {
type: 'resolve' type: 'resolve'
} }
export type LiquidityItem = BaseActivityItem & {
type: 'liquidity'
liquidity: LiquidityProvision
hideOutcome: boolean
smallAvatar: boolean
hideComment?: boolean
}
function getAnswerAndCommentInputGroups( function getAnswerAndCommentInputGroups(
contract: FreeResponseContract, contract: FreeResponseContract,
bets: Bet[], bets: Bet[],
@ -139,6 +149,7 @@ export function getSpecificContractActivityItems(
contract: Contract, contract: Contract,
bets: Bet[], bets: Bet[],
comments: Comment[], comments: Comment[],
liquidityProvisions: LiquidityProvision[],
tips: CommentTipMap, tips: CommentTipMap,
user: User | null | undefined, user: User | null | undefined,
options: { options: {
@ -146,7 +157,7 @@ export function getSpecificContractActivityItems(
} }
) { ) {
const { mode } = options const { mode } = options
const items = [] as ActivityItem[] let items = [] as ActivityItem[]
switch (mode) { switch (mode) {
case 'bets': case 'bets':
@ -163,6 +174,23 @@ export function getSpecificContractActivityItems(
hideComment: true, hideComment: true,
})) }))
) )
items.push(
...liquidityProvisions.map((liquidity) => ({
type: 'liquidity' as const,
id: liquidity.id,
contract,
liquidity,
hideOutcome: false,
smallAvatar: false,
}))
)
items = sortBy(items, (item) =>
item.type === 'bet'
? item.bet.createdTime
: item.type === 'liquidity'
? item.liquidity.createdTime
: undefined
)
break break
case 'comments': { case 'comments': {

View File

@ -8,11 +8,13 @@ import { FeedItems } from './feed-items'
import { User } from 'common/user' import { User } from 'common/user'
import { useContractWithPreload } from 'web/hooks/use-contract' import { useContractWithPreload } from 'web/hooks/use-contract'
import { CommentTipMap } from 'web/hooks/use-tip-txns' import { CommentTipMap } from 'web/hooks/use-tip-txns'
import { LiquidityProvision } from 'common/liquidity-provision'
export function ContractActivity(props: { export function ContractActivity(props: {
contract: Contract contract: Contract
bets: Bet[] bets: Bet[]
comments: Comment[] comments: Comment[]
liquidityProvisions: LiquidityProvision[]
tips: CommentTipMap tips: CommentTipMap
user: User | null | undefined user: User | null | undefined
mode: 'comments' | 'bets' | 'free-response-comment-answer-groups' mode: 'comments' | 'bets' | 'free-response-comment-answer-groups'
@ -20,7 +22,8 @@ export function ContractActivity(props: {
className?: string className?: string
betRowClassName?: string betRowClassName?: string
}) { }) {
const { user, mode, tips, className, betRowClassName } = props const { user, mode, tips, className, betRowClassName, liquidityProvisions } =
props
const contract = useContractWithPreload(props.contract) ?? props.contract const contract = useContractWithPreload(props.contract) ?? props.contract
@ -33,6 +36,7 @@ export function ContractActivity(props: {
contract, contract,
bets, bets,
comments, comments,
liquidityProvisions,
tips, tips,
user, user,
{ mode } { mode }

View File

@ -35,6 +35,7 @@ import {
} from 'web/components/feed/feed-comments' } from 'web/components/feed/feed-comments'
import { FeedBet } from 'web/components/feed/feed-bets' import { FeedBet } from 'web/components/feed/feed-bets'
import { NumericContract } from 'common/contract' import { NumericContract } from 'common/contract'
import { FeedLiquidity } from './feed-liquidity'
export function FeedItems(props: { export function FeedItems(props: {
contract: Contract contract: Contract
@ -83,6 +84,8 @@ export function FeedItem(props: { item: ActivityItem }) {
return <FeedDescription {...item} /> return <FeedDescription {...item} />
case 'bet': case 'bet':
return <FeedBet {...item} /> return <FeedBet {...item} />
case 'liquidity':
return <FeedLiquidity {...item} />
case 'answergroup': case 'answergroup':
return <FeedAnswerCommentGroup {...item} /> return <FeedAnswerCommentGroup {...item} />
case 'close': case 'close':

View File

@ -0,0 +1,85 @@
import dayjs from 'dayjs'
import { User } from 'common/user'
import { useUser, useUserById } from 'web/hooks/use-user'
import { Row } from 'web/components/layout/row'
import { Avatar, EmptyAvatar } from 'web/components/avatar'
import clsx from 'clsx'
import { formatMoney } from 'common/util/format'
import { RelativeTimestamp } from 'web/components/relative-timestamp'
import React from 'react'
import { UserLink } from '../user-page'
import { LiquidityProvision } from 'common/liquidity-provision'
export function FeedLiquidity(props: {
liquidity: LiquidityProvision
smallAvatar: boolean
}) {
const { liquidity, smallAvatar } = props
const { userId, createdTime } = liquidity
const isBeforeJune2022 = dayjs(createdTime).isBefore('2022-06-01')
// eslint-disable-next-line react-hooks/rules-of-hooks
const bettor = isBeforeJune2022 ? undefined : useUserById(userId)
const user = useUser()
const isSelf = user?.id === userId
return (
<>
<Row className={'flex w-full gap-2 pt-3'}>
{isSelf ? (
<Avatar
className={clsx(smallAvatar && 'ml-1')}
size={smallAvatar ? 'sm' : undefined}
avatarUrl={user.avatarUrl}
username={user.username}
/>
) : bettor ? (
<Avatar
className={clsx(smallAvatar && 'ml-1')}
size={smallAvatar ? 'sm' : undefined}
avatarUrl={bettor.avatarUrl}
username={bettor.username}
/>
) : (
<div className="relative px-1">
<EmptyAvatar />
</div>
)}
<div className={'min-w-0 flex-1 py-1.5'}>
<LiquidityStatusText
liquidity={liquidity}
isSelf={isSelf}
bettor={bettor}
/>
</div>
</Row>
</>
)
}
export function LiquidityStatusText(props: {
liquidity: LiquidityProvision
isSelf: boolean
bettor?: User
}) {
const { liquidity, bettor, isSelf } = props
const { amount, createdTime } = liquidity
// TODO: Withdrawn liquidity will never be shown, since liquidity amounts currently are zeroed out upon withdrawal.
const bought = amount >= 0 ? 'added' : 'withdrew'
const money = formatMoney(Math.abs(amount))
return (
<div className="text-sm text-gray-500">
{bettor ? (
<UserLink name={bettor.name} username={bettor.username} />
) : (
<span>{isSelf ? 'You' : 'A trader'}</span>
)}{' '}
{bought} {money}
{' of liquidity'}
<RelativeTimestamp time={createdTime} />
</div>
)
}

View File

@ -42,6 +42,7 @@ import { useBets } from 'web/hooks/use-bets'
import { AlertBox } from 'web/components/alert-box' import { AlertBox } from 'web/components/alert-box'
import { useTracking } from 'web/hooks/use-tracking' import { useTracking } from 'web/hooks/use-tracking'
import { CommentTipMap, useTipTxns } from 'web/hooks/use-tip-txns' import { CommentTipMap, useTipTxns } from 'web/hooks/use-tip-txns'
import { useLiquidity } from 'web/hooks/use-liquidity'
export const getStaticProps = fromPropz(getStaticPropz) export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: { export async function getStaticPropz(props: {
@ -117,6 +118,8 @@ export function ContractPageContent(
}) })
const bets = useBets(contract.id) ?? props.bets const bets = useBets(contract.id) ?? props.bets
const liquidityProvisions =
useLiquidity(contract.id)?.filter((l) => !l.isAnte && l.amount > 0) ?? []
// Sort for now to see if bug is fixed. // Sort for now to see if bug is fixed.
comments.sort((c1, c2) => c1.createdTime - c2.createdTime) comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
@ -233,6 +236,7 @@ export function ContractPageContent(
<ContractTabs <ContractTabs
contract={contract} contract={contract}
user={user} user={user}
liquidityProvisions={liquidityProvisions}
bets={bets} bets={bets}
tips={tips} tips={tips}
comments={comments} comments={comments}