Home: Prob change cards. Sort by daily score. (#925)
* Add dailyScore: product of unique bettors (3 days) and probChanges.day * Increase memory and duration of scoreContracts * Home: Smaller prob change card for groups. Use dailyScore for sort order (algolia) * Add back hover
This commit is contained in:
parent
eaaa46294a
commit
c6d034545a
|
@ -57,6 +57,7 @@ export type Contract<T extends AnyContractType = AnyContractType> = {
|
|||
uniqueBettorIds?: string[]
|
||||
uniqueBettorCount?: number
|
||||
popularityScore?: number
|
||||
dailyScore?: number
|
||||
followerCount?: number
|
||||
featuredOnHomeRank?: number
|
||||
likedByUserIds?: string[]
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import * as functions from 'firebase-functions'
|
||||
import * as admin from 'firebase-admin'
|
||||
import { Bet } from 'common/bet'
|
||||
import { uniq } from 'lodash'
|
||||
import { Contract } from 'common/contract'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { log } from './utils'
|
||||
import { removeUndefinedProps } from '../../common/util/object'
|
||||
|
||||
export const scoreContracts = functions.pubsub
|
||||
.schedule('every 1 hours')
|
||||
export const scoreContracts = functions
|
||||
.runWith({ memory: '4GB', timeoutSeconds: 540 })
|
||||
.pubsub.schedule('every 1 hours')
|
||||
.onRun(async () => {
|
||||
await scoreContractsInternal()
|
||||
})
|
||||
|
@ -44,11 +46,22 @@ async function scoreContractsInternal() {
|
|||
const bettors = bets.docs
|
||||
.map((doc) => doc.data() as Bet)
|
||||
.map((bet) => bet.userId)
|
||||
const score = uniq(bettors).length
|
||||
if (contract.popularityScore !== score)
|
||||
const popularityScore = uniq(bettors).length
|
||||
|
||||
let dailyScore: number | undefined
|
||||
if (contract.outcomeType === 'BINARY' && contract.mechanism === 'cpmm-1') {
|
||||
const percentChange = Math.abs(contract.probChanges.day)
|
||||
dailyScore = popularityScore * percentChange
|
||||
}
|
||||
|
||||
if (
|
||||
contract.popularityScore !== popularityScore ||
|
||||
contract.dailyScore !== dailyScore
|
||||
) {
|
||||
await firestore
|
||||
.collection('contracts')
|
||||
.doc(contract.id)
|
||||
.update({ popularityScore: score })
|
||||
.update(removeUndefinedProps({ popularityScore, dailyScore }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Col } from '../layout/col'
|
|||
import {
|
||||
BinaryContract,
|
||||
Contract,
|
||||
CPMMBinaryContract,
|
||||
FreeResponseContract,
|
||||
MultipleChoiceContract,
|
||||
NumericContract,
|
||||
|
@ -32,6 +33,8 @@ import { track } from '@amplitude/analytics-browser'
|
|||
import { trackCallback } from 'web/lib/service/analytics'
|
||||
import { getMappedValue } from 'common/pseudo-numeric'
|
||||
import { Tooltip } from '../tooltip'
|
||||
import { SiteLink } from '../site-link'
|
||||
import { ProbChange } from './prob-change-table'
|
||||
|
||||
export function ContractCard(props: {
|
||||
contract: Contract
|
||||
|
@ -379,3 +382,34 @@ export function PseudoNumericResolutionOrExpectation(props: {
|
|||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
export function ContractCardProbChange(props: {
|
||||
contract: CPMMBinaryContract
|
||||
noLinkAvatar?: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const { contract, noLinkAvatar, className } = props
|
||||
return (
|
||||
<Col
|
||||
className={clsx(
|
||||
className,
|
||||
'mb-4 rounded-lg bg-white shadow hover:bg-gray-100 hover:shadow-lg'
|
||||
)}
|
||||
>
|
||||
<AvatarDetails
|
||||
contract={contract}
|
||||
className={'px-6 pt-4'}
|
||||
noLink={noLinkAvatar}
|
||||
/>
|
||||
<Row className={clsx('items-start justify-between gap-4 ', className)}>
|
||||
<SiteLink
|
||||
className="pl-6 pr-0 pt-2 pb-4 font-semibold text-indigo-700"
|
||||
href={contractPath(contract)}
|
||||
>
|
||||
<span className="line-clamp-3">{contract.question}</span>
|
||||
</SiteLink>
|
||||
<ProbChange className="py-2 pr-4" contract={contract} />
|
||||
</Row>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Contract } from 'web/lib/firebase/contracts'
|
|||
import { User } from 'web/lib/firebase/users'
|
||||
import { Col } from '../layout/col'
|
||||
import { SiteLink } from '../site-link'
|
||||
import { ContractCard } from './contract-card'
|
||||
import { ContractCard, ContractCardProbChange } from './contract-card'
|
||||
import { ShowTime } from './contract-details'
|
||||
import { ContractSearch } from '../contract-search'
|
||||
import { useCallback } from 'react'
|
||||
|
@ -10,6 +10,7 @@ import clsx from 'clsx'
|
|||
import { LoadingIndicator } from '../loading-indicator'
|
||||
import { VisibilityObserver } from '../visibility-observer'
|
||||
import Masonry from 'react-masonry-css'
|
||||
import { CPMMBinaryContract} from 'common/contract'
|
||||
|
||||
export type ContractHighlightOptions = {
|
||||
contractIds?: string[]
|
||||
|
@ -25,6 +26,7 @@ export function ContractsGrid(props: {
|
|||
hideQuickBet?: boolean
|
||||
hideGroupLink?: boolean
|
||||
noLinkAvatar?: boolean
|
||||
showProbChange?: boolean
|
||||
}
|
||||
highlightOptions?: ContractHighlightOptions
|
||||
trackingPostfix?: string
|
||||
|
@ -39,7 +41,8 @@ export function ContractsGrid(props: {
|
|||
highlightOptions,
|
||||
trackingPostfix,
|
||||
} = props
|
||||
const { hideQuickBet, hideGroupLink, noLinkAvatar } = cardUIOptions || {}
|
||||
const { hideQuickBet, hideGroupLink, noLinkAvatar, showProbChange } =
|
||||
cardUIOptions || {}
|
||||
const { contractIds, highlightClassName } = highlightOptions || {}
|
||||
const onVisibilityUpdated = useCallback(
|
||||
(visible) => {
|
||||
|
@ -73,7 +76,13 @@ export function ContractsGrid(props: {
|
|||
className="-ml-4 flex w-auto"
|
||||
columnClassName="pl-4 bg-clip-padding"
|
||||
>
|
||||
{contracts.map((contract) => (
|
||||
{contracts.map((contract) =>
|
||||
showProbChange && contract.mechanism === 'cpmm-1' ? (
|
||||
<ContractCardProbChange
|
||||
key={contract.id}
|
||||
contract={contract as CPMMBinaryContract}
|
||||
/>
|
||||
) : (
|
||||
<ContractCard
|
||||
contract={contract}
|
||||
key={contract.id}
|
||||
|
@ -90,7 +99,8 @@ export function ContractsGrid(props: {
|
|||
contractIds?.includes(contract.id) && highlightClassName
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</Masonry>
|
||||
{loadMore && (
|
||||
<VisibilityObserver
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx'
|
||||
import { partition } from 'lodash'
|
||||
import { contractPath } from 'web/lib/firebase/contracts'
|
||||
import { CPMMContract } from 'common/contract'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
|
@ -8,16 +9,17 @@ import { Row } from '../layout/row'
|
|||
import { LoadingIndicator } from '../loading-indicator'
|
||||
|
||||
export function ProbChangeTable(props: {
|
||||
changes:
|
||||
| { positiveChanges: CPMMContract[]; negativeChanges: CPMMContract[] }
|
||||
| undefined
|
||||
changes: CPMMContract[] | undefined
|
||||
full?: boolean
|
||||
}) {
|
||||
const { changes, full } = props
|
||||
|
||||
if (!changes) return <LoadingIndicator />
|
||||
|
||||
const { positiveChanges, negativeChanges } = changes
|
||||
const [positiveChanges, negativeChanges] = partition(
|
||||
changes,
|
||||
(c) => c.probChanges.day > 0
|
||||
)
|
||||
|
||||
const threshold = 0.01
|
||||
const positiveAboveThreshold = positiveChanges.filter(
|
||||
|
@ -53,10 +55,18 @@ export function ProbChangeTable(props: {
|
|||
)
|
||||
}
|
||||
|
||||
function ProbChangeRow(props: { contract: CPMMContract }) {
|
||||
const { contract } = props
|
||||
export function ProbChangeRow(props: {
|
||||
contract: CPMMContract
|
||||
className?: string
|
||||
}) {
|
||||
const { contract, className } = props
|
||||
return (
|
||||
<Row className="items-center justify-between gap-4 hover:bg-gray-100">
|
||||
<Row
|
||||
className={clsx(
|
||||
'items-center justify-between gap-4 hover:bg-gray-100',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<SiteLink
|
||||
className="p-4 pr-0 font-semibold text-indigo-700"
|
||||
href={contractPath(contract)}
|
||||
|
|
|
@ -11,10 +11,13 @@ import {
|
|||
trendingContractsQuery,
|
||||
getContractsQuery,
|
||||
} from 'web/lib/firebase/contracts'
|
||||
import { QueryClient, useQueryClient } from 'react-query'
|
||||
import { QueryClient, useQuery, useQueryClient } from 'react-query'
|
||||
import { MINUTE_MS } from 'common/util/time'
|
||||
import { query, limit } from 'firebase/firestore'
|
||||
import { Sort } from 'web/components/contract-search'
|
||||
import { dailyScoreIndex } from 'web/lib/service/algolia'
|
||||
import { CPMMBinaryContract } from 'common/contract'
|
||||
import { zipObject } from 'lodash'
|
||||
|
||||
export const useContracts = () => {
|
||||
const [contracts, setContracts] = useState<Contract[] | undefined>()
|
||||
|
@ -26,6 +29,29 @@ export const useContracts = () => {
|
|||
return contracts
|
||||
}
|
||||
|
||||
export const useContractsByDailyScoreGroups = (
|
||||
groupSlugs: string[] | undefined
|
||||
) => {
|
||||
const facetFilters = ['isResolved:false']
|
||||
|
||||
const { data } = useQuery(['daily-score', groupSlugs], () =>
|
||||
Promise.all(
|
||||
(groupSlugs ?? []).map((slug) =>
|
||||
dailyScoreIndex.search<CPMMBinaryContract>('', {
|
||||
facetFilters: [...facetFilters, `groupLinks.slug:${slug}`],
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
if (!groupSlugs || !data || data.length !== groupSlugs.length)
|
||||
return undefined
|
||||
|
||||
return zipObject(
|
||||
groupSlugs,
|
||||
data.map((d) => d.hits.filter((c) => c.dailyScore))
|
||||
)
|
||||
}
|
||||
|
||||
const q = new QueryClient()
|
||||
export const getCachedContracts = async () =>
|
||||
q.fetchQuery(['contracts'], () => listAllContracts(1000), {
|
||||
|
|
|
@ -104,7 +104,7 @@ export const useMemberGroupIds = (user: User | null | undefined) => {
|
|||
}
|
||||
|
||||
export function useMemberGroupsSubscription(user: User | null | undefined) {
|
||||
const cachedGroups = useMemberGroups(user?.id) ?? []
|
||||
const cachedGroups = useMemberGroups(user?.id)
|
||||
const [groups, setGroups] = useState(cachedGroups)
|
||||
|
||||
const userId = user?.id
|
||||
|
|
|
@ -1,75 +1,47 @@
|
|||
import { useFirestoreQueryData } from '@react-query-firebase/firestore'
|
||||
import { CPMMContract } from 'common/contract'
|
||||
import { MINUTE_MS } from 'common/util/time'
|
||||
import { useQuery, useQueryClient } from 'react-query'
|
||||
import { CPMMBinaryContract } from 'common/contract'
|
||||
import { sortBy, uniqBy } from 'lodash'
|
||||
import { useQuery } from 'react-query'
|
||||
import {
|
||||
getProbChangesNegative,
|
||||
getProbChangesPositive,
|
||||
} from 'web/lib/firebase/contracts'
|
||||
import { getValues } from 'web/lib/firebase/utils'
|
||||
import { getIndexName, searchClient } from 'web/lib/service/algolia'
|
||||
probChangeAscendingIndex,
|
||||
probChangeDescendingIndex,
|
||||
} from 'web/lib/service/algolia'
|
||||
|
||||
export const useProbChangesAlgolia = (userId: string) => {
|
||||
const { data: positiveData } = useQuery(['prob-change-day', userId], () =>
|
||||
searchClient
|
||||
.initIndex(getIndexName('prob-change-day'))
|
||||
.search<CPMMContract>('', {
|
||||
facetFilters: ['uniqueBettorIds:' + userId, 'isResolved:false'],
|
||||
})
|
||||
)
|
||||
const { data: negativeData } = useQuery(
|
||||
['prob-change-day-ascending', userId],
|
||||
() =>
|
||||
searchClient
|
||||
.initIndex(getIndexName('prob-change-day-ascending'))
|
||||
.search<CPMMContract>('', {
|
||||
facetFilters: ['uniqueBettorIds:' + userId, 'isResolved:false'],
|
||||
})
|
||||
)
|
||||
export const useProbChanges = (
|
||||
filters: { bettorId?: string; groupSlugs?: string[] } = {}
|
||||
) => {
|
||||
const { bettorId, groupSlugs } = filters
|
||||
|
||||
if (!positiveData || !negativeData) {
|
||||
return undefined
|
||||
const bettorFilter = bettorId ? `uniqueBettorIds:${bettorId}` : ''
|
||||
const groupFilters = groupSlugs
|
||||
? groupSlugs.map((slug) => `groupLinks.slug:${slug}`)
|
||||
: []
|
||||
|
||||
const facetFilters = [
|
||||
'isResolved:false',
|
||||
'outcomeType:BINARY',
|
||||
bettorFilter,
|
||||
groupFilters,
|
||||
]
|
||||
const searchParams = {
|
||||
facetFilters,
|
||||
hitsPerPage: 50,
|
||||
}
|
||||
|
||||
return {
|
||||
positiveChanges: positiveData.hits
|
||||
.filter((c) => c.probChanges && c.probChanges.day > 0)
|
||||
.filter((c) => c.outcomeType === 'BINARY'),
|
||||
negativeChanges: negativeData.hits
|
||||
.filter((c) => c.probChanges && c.probChanges.day < 0)
|
||||
.filter((c) => c.outcomeType === 'BINARY'),
|
||||
}
|
||||
}
|
||||
|
||||
export const useProbChanges = (userId: string) => {
|
||||
const { data: positiveChanges } = useFirestoreQueryData(
|
||||
['prob-changes-day-positive', userId],
|
||||
getProbChangesPositive(userId)
|
||||
)
|
||||
const { data: negativeChanges } = useFirestoreQueryData(
|
||||
['prob-changes-day-negative', userId],
|
||||
getProbChangesNegative(userId)
|
||||
)
|
||||
|
||||
if (!positiveChanges || !negativeChanges) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return { positiveChanges, negativeChanges }
|
||||
}
|
||||
|
||||
export const usePrefetchProbChanges = (userId: string | undefined) => {
|
||||
const queryClient = useQueryClient()
|
||||
if (userId) {
|
||||
queryClient.prefetchQuery(
|
||||
['prob-changes-day-positive', userId],
|
||||
() => getValues(getProbChangesPositive(userId)),
|
||||
{ staleTime: MINUTE_MS }
|
||||
)
|
||||
queryClient.prefetchQuery(
|
||||
['prob-changes-day-negative', userId],
|
||||
() => getValues(getProbChangesNegative(userId)),
|
||||
{ staleTime: MINUTE_MS }
|
||||
)
|
||||
}
|
||||
const { data: positiveChanges } = useQuery(
|
||||
['prob-change-day', groupSlugs],
|
||||
() => probChangeDescendingIndex.search<CPMMBinaryContract>('', searchParams)
|
||||
)
|
||||
const { data: negativeChanges } = useQuery(
|
||||
['prob-change-day-ascending', groupSlugs],
|
||||
() => probChangeAscendingIndex.search<CPMMBinaryContract>('', searchParams)
|
||||
)
|
||||
|
||||
if (!positiveChanges || !negativeChanges) return undefined
|
||||
|
||||
const hits = uniqBy(
|
||||
[...positiveChanges.hits, ...negativeChanges.hits],
|
||||
(c) => c.id
|
||||
)
|
||||
|
||||
return sortBy(hits, (c) => Math.abs(c.probChanges.day)).reverse()
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { partition, sortBy, sum, uniqBy } from 'lodash'
|
||||
|
||||
import { coll, getValues, listenForValue, listenForValues } from './utils'
|
||||
import { BinaryContract, Contract, CPMMContract } from 'common/contract'
|
||||
import { BinaryContract, Contract } from 'common/contract'
|
||||
import { chooseRandomSubset } from 'common/util/random'
|
||||
import { formatMoney, formatPercent } from 'common/util/format'
|
||||
import { DAY_MS } from 'common/util/time'
|
||||
|
@ -426,21 +426,3 @@ export async function getRecentBetsAndComments(contract: Contract) {
|
|||
recentComments,
|
||||
}
|
||||
}
|
||||
|
||||
export const getProbChangesPositive = (userId: string) =>
|
||||
query(
|
||||
contracts,
|
||||
where('uniqueBettorIds', 'array-contains', userId),
|
||||
where('probChanges.day', '>', 0),
|
||||
orderBy('probChanges.day', 'desc'),
|
||||
limit(10)
|
||||
) as Query<CPMMContract>
|
||||
|
||||
export const getProbChangesNegative = (userId: string) =>
|
||||
query(
|
||||
contracts,
|
||||
where('uniqueBettorIds', 'array-contains', userId),
|
||||
where('probChanges.day', '<', 0),
|
||||
orderBy('probChanges.day', 'asc'),
|
||||
limit(10)
|
||||
) as Query<CPMMContract>
|
||||
|
|
|
@ -13,3 +13,13 @@ export const searchIndexName =
|
|||
export const getIndexName = (sort: string) => {
|
||||
return `${indexPrefix}contracts-${sort}`
|
||||
}
|
||||
|
||||
export const probChangeDescendingIndex = searchClient.initIndex(
|
||||
getIndexName('prob-change-day')
|
||||
)
|
||||
export const probChangeAscendingIndex = searchClient.initIndex(
|
||||
getIndexName('prob-change-day-ascending')
|
||||
)
|
||||
export const dailyScoreIndex = searchClient.initIndex(
|
||||
getIndexName('daily-score')
|
||||
)
|
||||
|
|
|
@ -2,14 +2,17 @@ import { ProbChangeTable } from 'web/components/contract/prob-change-table'
|
|||
import { Col } from 'web/components/layout/col'
|
||||
import { Page } from 'web/components/page'
|
||||
import { Title } from 'web/components/title'
|
||||
import { useProbChangesAlgolia } from 'web/hooks/use-prob-changes'
|
||||
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
||||
import { useTracking } from 'web/hooks/use-tracking'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
|
||||
export default function DailyMovers() {
|
||||
const user = useUser()
|
||||
const bettorId = user?.id ?? undefined
|
||||
|
||||
const changes = useProbChangesAlgolia(user?.id ?? '')
|
||||
const changes = useProbChanges({ bettorId })?.filter(
|
||||
(c) => Math.abs(c.probChanges.day) >= 0.01
|
||||
)
|
||||
|
||||
useTracking('view daily movers')
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export default function Home() {
|
|||
}
|
||||
|
||||
const groups = useMemberGroupsSubscription(user)
|
||||
const { sections } = getHomeItems(groups, homeSections)
|
||||
const { sections } = getHomeItems(groups ?? [], homeSections)
|
||||
|
||||
return (
|
||||
<Page>
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
import { PlusCircleIcon, XCircleIcon } from '@heroicons/react/outline'
|
||||
import clsx from 'clsx'
|
||||
import { toast, Toaster } from 'react-hot-toast'
|
||||
import { Dictionary } from 'lodash'
|
||||
|
||||
import { Page } from 'web/components/page'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
|
@ -31,11 +32,10 @@ import { ProbChangeTable } from 'web/components/contract/prob-change-table'
|
|||
import { groupPath, joinGroup, leaveGroup } from 'web/lib/firebase/groups'
|
||||
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { useProbChangesAlgolia } from 'web/hooks/use-prob-changes'
|
||||
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
||||
import { ProfitBadge } from 'web/components/bets-list'
|
||||
import { calculatePortfolioProfit } from 'common/calculate-metrics'
|
||||
import { hasCompletedStreakToday } from 'web/components/profile/betting-streak-modal'
|
||||
import { useContractsQuery } from 'web/hooks/use-contracts'
|
||||
import { ContractsGrid } from 'web/components/contract/contracts-grid'
|
||||
import { PillButton } from 'web/components/buttons/pill-button'
|
||||
import { filterDefined } from 'common/util/array'
|
||||
|
@ -43,6 +43,8 @@ import { updateUser } from 'web/lib/firebase/users'
|
|||
import { isArray, keyBy } from 'lodash'
|
||||
import { usePrefetch } from 'web/hooks/use-prefetch'
|
||||
import { Title } from 'web/components/title'
|
||||
import { CPMMBinaryContract } from 'common/contract'
|
||||
import { useContractsByDailyScoreGroups } from 'web/hooks/use-contracts'
|
||||
|
||||
export default function Home() {
|
||||
const user = useUser()
|
||||
|
@ -54,20 +56,19 @@ export default function Home() {
|
|||
|
||||
const groups = useMemberGroupsSubscription(user)
|
||||
|
||||
const { sections } = getHomeItems(groups, user?.homeSections ?? [])
|
||||
const { sections } = getHomeItems(groups ?? [], user?.homeSections ?? [])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
user &&
|
||||
!user.homeSections &&
|
||||
sections.length > 0 &&
|
||||
groups.length > 0
|
||||
) {
|
||||
if (user && !user.homeSections && sections.length > 0 && groups) {
|
||||
// Save initial home sections.
|
||||
updateUser(user.id, { homeSections: sections.map((s) => s.id) })
|
||||
}
|
||||
}, [user, sections, groups])
|
||||
|
||||
const groupContracts = useContractsByDailyScoreGroups(
|
||||
groups?.map((g) => g.slug)
|
||||
)
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Toaster />
|
||||
|
@ -81,9 +82,13 @@ export default function Home() {
|
|||
<DailyStats user={user} />
|
||||
</Row>
|
||||
|
||||
{sections.map((section) => renderSection(section, user, groups))}
|
||||
<>
|
||||
{sections.map((section) =>
|
||||
renderSection(section, user, groups, groupContracts)
|
||||
)}
|
||||
|
||||
<TrendingGroupsSection user={user} />
|
||||
</>
|
||||
</Col>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -134,7 +139,8 @@ export const getHomeItems = (groups: Group[], sections: string[]) => {
|
|||
function renderSection(
|
||||
section: { id: string; label: string },
|
||||
user: User | null | undefined,
|
||||
groups: Group[]
|
||||
groups: Group[] | undefined,
|
||||
groupContracts: Dictionary<CPMMBinaryContract[]> | undefined
|
||||
) {
|
||||
const { id, label } = section
|
||||
if (id === 'daily-movers') {
|
||||
|
@ -156,8 +162,23 @@ function renderSection(
|
|||
<SearchSection key={id} label={label} sort={sort.value} user={user} />
|
||||
)
|
||||
|
||||
if (groups && groupContracts) {
|
||||
const group = groups.find((g) => g.id === id)
|
||||
if (group) return <GroupSection key={id} group={group} user={user} />
|
||||
if (group) {
|
||||
const contracts = groupContracts[group.slug].filter(
|
||||
(c) => Math.abs(c.probChanges.day) >= 0.01
|
||||
)
|
||||
if (contracts.length === 0) return null
|
||||
return (
|
||||
<GroupSection
|
||||
key={id}
|
||||
group={group}
|
||||
user={user}
|
||||
contracts={contracts}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
@ -207,7 +228,6 @@ function SearchSection(props: {
|
|||
defaultPill={pill}
|
||||
noControls
|
||||
maxResults={6}
|
||||
headerClassName="sticky"
|
||||
persistPrefix={`home-${sort}`}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -217,10 +237,9 @@ function SearchSection(props: {
|
|||
function GroupSection(props: {
|
||||
group: Group
|
||||
user: User | null | undefined | undefined
|
||||
contracts: CPMMBinaryContract[]
|
||||
}) {
|
||||
const { group, user } = props
|
||||
|
||||
const contracts = useContractsQuery('score', 4, { groupSlug: group.slug })
|
||||
const { group, user, contracts } = props
|
||||
|
||||
return (
|
||||
<Col>
|
||||
|
@ -245,21 +264,21 @@ function GroupSection(props: {
|
|||
<XCircleIcon className={'h-5 w-5 flex-shrink-0'} aria-hidden="true" />
|
||||
</Button>
|
||||
</SectionHeader>
|
||||
<ContractsGrid contracts={contracts} />
|
||||
<ContractsGrid
|
||||
contracts={contracts.slice(0, 4)}
|
||||
cardUIOptions={{ showProbChange: true }}
|
||||
/>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function DailyMoversSection(props: { userId: string | null | undefined }) {
|
||||
const { userId } = props
|
||||
const changes = useProbChangesAlgolia(userId ?? '')
|
||||
|
||||
if (changes) {
|
||||
const { positiveChanges, negativeChanges } = changes
|
||||
if (
|
||||
!positiveChanges.find((c) => c.probChanges.day >= 0.01) ||
|
||||
!negativeChanges.find((c) => c.probChanges.day <= -0.01)
|
||||
const changes = useProbChanges({ bettorId: userId ?? undefined })?.filter(
|
||||
(c) => Math.abs(c.probChanges.day) >= 0.01
|
||||
)
|
||||
|
||||
if (changes && changes.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -332,6 +351,10 @@ export function TrendingGroupsSection(props: {
|
|||
const count = full ? 100 : 25
|
||||
const chosenGroups = groups.slice(0, count)
|
||||
|
||||
if (chosenGroups.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Col className={className}>
|
||||
<SectionHeader label="Trending groups" href="/explore-groups">
|
||||
|
|
Loading…
Reference in New Issue
Block a user