From ad6594f0bcf2627471df911ca2d3954fb218be92 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Tue, 7 Jun 2022 23:42:42 -0500 Subject: [PATCH] Add discover tab of users based on markets you have bet on --- web/components/following-button.tsx | 18 ++++++++++++++++- web/hooks/use-users.ts | 30 +++++++++++++++++++++++++++++ web/lib/firebase/bets.ts | 13 ++++++++++++- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/web/components/following-button.tsx b/web/components/following-button.tsx index 4e6a660b..32b1edf1 100644 --- a/web/components/following-button.tsx +++ b/web/components/following-button.tsx @@ -3,11 +3,12 @@ import { PencilIcon } from '@heroicons/react/outline' import { User } from 'common/user' import { useEffect, useState } from 'react' import { useFollowers, useFollows } from 'web/hooks/use-follows' -import { prefetchUsers } from 'web/hooks/use-user' +import { prefetchUsers, useUser } from 'web/hooks/use-user' import { FollowList } from './follow-list' import { Col } from './layout/col' import { Modal } from './layout/modal' import { Tabs } from './layout/tabs' +import { useDiscoverUsers } from 'web/hooks/use-users' export function FollowingButton(props: { user: User }) { const { user } = props @@ -114,6 +115,13 @@ function FollowingFollowersDialog(props: { prefetchUsers([...followingIds, ...followerIds]) }, [followingIds, followerIds]) + const currentUser = useUser() + + const discoverUserIds = useDiscoverUsers() + useEffect(() => { + prefetchUsers(discoverUserIds) + }, [discoverUserIds]) + return ( @@ -129,6 +137,14 @@ function FollowingFollowersDialog(props: { title: 'Followers', content: , }, + ...(currentUser + ? [ + { + title: 'Discover', + content: , + }, + ] + : []), ]} defaultIndex={defaultTab === 'following' ? 0 : 1} /> diff --git a/web/hooks/use-users.ts b/web/hooks/use-users.ts index 1b5656a0..428674ab 100644 --- a/web/hooks/use-users.ts +++ b/web/hooks/use-users.ts @@ -5,6 +5,10 @@ import { listenForAllUsers, listenForPrivateUsers, } from 'web/lib/firebase/users' +import { useUser } from './use-user' +import { groupBy, sortBy, difference } from 'lodash' +import { getContractsOfUserBets } from 'web/lib/firebase/bets' +import { useFollows } from './use-follows' export const useUsers = () => { const [users, setUsers] = useState([]) @@ -37,3 +41,29 @@ export const usePrivateUsers = () => { return users } + +export const useDiscoverUsers = () => { + const user = useUser() + + const [discoverUserIds, setDiscoverUserIds] = useState([]) + + useEffect(() => { + if (user) + getContractsOfUserBets(user.id).then((contracts) => { + const creatorCounts = Object.entries( + groupBy(contracts, 'creatorId') + ).map(([id, contracts]) => [id, contracts.length] as const) + + const topCreatorIds = sortBy(creatorCounts, ([_, i]) => i) + .map(([id]) => id) + .reverse() + + setDiscoverUserIds(topCreatorIds) + }) + }, [user]) + + const followedUserIds = useFollows(user?.id) + const nonSuggestions = [user?.id ?? '', ...(followedUserIds ?? [])] + + return difference(discoverUserIds, nonSuggestions).slice(0, 50) +} diff --git a/web/lib/firebase/bets.ts b/web/lib/firebase/bets.ts index 74f30957..5311cee4 100644 --- a/web/lib/firebase/bets.ts +++ b/web/lib/firebase/bets.ts @@ -5,12 +5,14 @@ import { where, orderBy, } from 'firebase/firestore' -import { range } from 'lodash' +import { range, uniq } from 'lodash' import { db } from './init' import { Bet } from 'common/bet' import { Contract } from 'common/contract' import { getValues, listenForValues } from './utils' +import { getContractFromId } from './contracts' +import { filterDefined } from 'common/util/array' export type { Bet } function getBetsCollection(contractId: string) { @@ -76,6 +78,15 @@ export async function getUserBets( .catch((reason) => reason) } +export async function getContractsOfUserBets(userId: string) { + const bets: Bet[] = await getUserBets(userId, { includeRedemptions: false }) + const contractIds = uniq(bets.map((bet) => bet.contractId)) + const contracts = await Promise.all( + contractIds.map((contractId) => getContractFromId(contractId)) + ) + return filterDefined(contracts) +} + export function listenForUserBets( userId: string, setBets: (bets: Bet[]) => void,