diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts index 2751e9bb..0fea53a0 100644 --- a/web/lib/firebase/contracts.ts +++ b/web/lib/firebase/contracts.ts @@ -13,7 +13,7 @@ import { updateDoc, where, } from 'firebase/firestore' -import { sortBy, sum, uniqBy } from 'lodash' +import { partition, sortBy, sum, uniqBy } from 'lodash' import { coll, getValues, listenForValue, listenForValues } from './utils' import { BinaryContract, Contract } from 'common/contract' @@ -303,62 +303,63 @@ export async function getClosingSoonContracts() { return sortBy(chooseRandomSubset(data, 2), (contract) => contract.closeTime) } -export const getRandTopCreatorContracts = async ( +export const getTopCreatorContracts = async ( creatorId: string, - count: number, - excluding: string[] = [] + count: number ) => { const creatorContractsQuery = query( contracts, where('isResolved', '==', false), where('creatorId', '==', creatorId), orderBy('popularityScore', 'desc'), - limit(count * 2) + limit(count) ) - const data = await getValues(creatorContractsQuery) - const open = data - .filter((c) => c.closeTime && c.closeTime > Date.now()) - .filter((c) => !excluding.includes(c.id)) - - return chooseRandomSubset(open, count) + return await getValues(creatorContractsQuery) } -export const getRandTopGroupContracts = async ( +export const getTopGroupContracts = async ( groupSlug: string, - count: number, - excluding: string[] = [] + count: number ) => { const creatorContractsQuery = query( contracts, where('groupSlugs', 'array-contains', groupSlug), where('isResolved', '==', false), orderBy('popularityScore', 'desc'), - limit(count * 2) + limit(count) ) - const data = await getValues(creatorContractsQuery) - const open = data - .filter((c) => c.closeTime && c.closeTime > Date.now()) - .filter((c) => !excluding.includes(c.id)) - - return chooseRandomSubset(open, count) + return await getValues(creatorContractsQuery) } export const getRecommendedContracts = async ( contract: Contract, + excludeBettorId: string, count: number ) => { const { creatorId, groupSlugs, id } = contract const [userContracts, groupContracts] = await Promise.all([ - getRandTopCreatorContracts(creatorId, count, [id]), + getTopCreatorContracts(creatorId, count * 2), groupSlugs && groupSlugs[0] - ? getRandTopGroupContracts(groupSlugs[0], count, [id]) + ? getTopGroupContracts(groupSlugs[0], count * 2) : [], ]) const combined = uniqBy([...userContracts, ...groupContracts], (c) => c.id) - return chooseRandomSubset(combined, count) + const open = combined + .filter((c) => c.closeTime && c.closeTime > Date.now()) + .filter((c) => c.id !== id) + + const [betOnContracts, nonBetOnContracts] = partition( + open, + (c) => c.uniqueBettorIds && c.uniqueBettorIds.includes(excludeBettorId) + ) + const chosen = chooseRandomSubset(nonBetOnContracts, count) + if (chosen.length < count) + chosen.push(...chooseRandomSubset(betOnContracts, count - chosen.length)) + + return chosen } export async function getRecentBetsAndComments(contract: Contract) { diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx index f9f45144..f7a5c5c5 100644 --- a/web/pages/[username]/[contractSlug].tsx +++ b/web/pages/[username]/[contractSlug].tsx @@ -52,10 +52,9 @@ export async function getStaticPropz(props: { const contract = (await getContractFromSlug(contractSlug)) || null const contractId = contract?.id - const [bets, comments, recommendedContracts] = await Promise.all([ + const [bets, comments] = await Promise.all([ contractId ? listAllBets(contractId) : [], contractId ? listAllComments(contractId) : [], - contract ? getRecommendedContracts(contract, 6) : [], ]) return { @@ -66,7 +65,6 @@ export async function getStaticPropz(props: { // Limit the data sent to the client. Client will still load all bets and comments directly. bets: bets.slice(0, 5000), comments: comments.slice(0, 1000), - recommendedContracts, }, revalidate: 60, // regenerate after a minute @@ -83,7 +81,6 @@ export default function ContractPage(props: { bets: Bet[] comments: ContractComment[] slug: string - recommendedContracts: Contract[] backToHome?: () => void }) { props = usePropz(props, getStaticPropz) ?? { @@ -91,7 +88,6 @@ export default function ContractPage(props: { username: '', comments: [], bets: [], - recommendedContracts: [], slug: '', } @@ -188,15 +184,17 @@ export function ContractPageContent( setShowConfetti(shouldSeeConfetti) }, [contract, user]) - const [recommendedContracts, setRecommendedMarkets] = useState( - props.recommendedContracts + const [recommendedContracts, setRecommendedContracts] = useState( + [] ) useEffect(() => { - if (contract && recommendedContracts.length === 0) { - getRecommendedContracts(contract, 6).then(setRecommendedMarkets) + if (contract && user) { + getRecommendedContracts(contract, user.id, 6).then( + setRecommendedContracts + ) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [contract.id, recommendedContracts]) + }, [contract.id, user?.id]) const { isResolved, question, outcomeType } = contract