diff --git a/web/hooks/use-group.ts b/web/hooks/use-group.ts index be0c92f6..c3098ba4 100644 --- a/web/hooks/use-group.ts +++ b/web/hooks/use-group.ts @@ -7,7 +7,7 @@ import { listenForGroups, listenForMemberGroups, } from 'web/lib/firebase/groups' -import { getUser } from 'web/lib/firebase/users' +import { getUser, getUsers } from 'web/lib/firebase/users' import { filterDefined } from 'common/util/array' export const useGroup = (groupId: string | undefined) => { @@ -85,9 +85,14 @@ export function useMembers(group: Group, max?: number) { } export async function listMembers(group: Group, max?: number) { - return await Promise.all( - group.memberIds.slice(0, max ? max : group.memberIds.length).map(getUser) - ) + const { memberIds } = group + const numToRetrieve = max ?? memberIds.length + if (memberIds.length === 0) return [] + if (numToRetrieve) + return (await getUsers()).filter((user) => + group.memberIds.includes(user.id) + ) + return await Promise.all(group.memberIds.slice(0, numToRetrieve).map(getUser)) } export const useGroupsWithContract = (contractId: string | undefined) => { diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts index d5fb85cb..63efa53b 100644 --- a/web/lib/firebase/contracts.ts +++ b/web/lib/firebase/contracts.ts @@ -124,6 +124,14 @@ export async function listContracts(creatorId: string): Promise { return snapshot.docs.map((doc) => doc.data()) } +export async function listContractsByGroupSlug( + slug: string +): Promise { + const q = query(contracts, where('groupSlugs', 'array-contains', slug)) + const snapshot = await getDocs(q) + return snapshot.docs.map((doc) => doc.data()) +} + export async function listTaggedContractsCaseInsensitive( tag: string ): Promise { diff --git a/web/lib/firebase/groups.ts b/web/lib/firebase/groups.ts index ec152792..762bfab1 100644 --- a/web/lib/firebase/groups.ts +++ b/web/lib/firebase/groups.ts @@ -8,7 +8,7 @@ import { } from 'firebase/firestore' import { sortBy, uniq } from 'lodash' import { Group } from 'common/group' -import { getContractFromId, updateContract } from './contracts' +import { updateContract } from './contracts' import { coll, getValue, @@ -16,7 +16,6 @@ import { listenForValue, listenForValues, } from './utils' -import { filterDefined } from 'common/util/array' import { Contract } from 'common/contract' export const groups = coll('groups') @@ -54,21 +53,6 @@ export async function getGroupBySlug(slug: string) { return docs.length === 0 ? null : docs[0].data() } -export async function getGroupContracts(group: Group) { - const { contractIds } = group - - const contracts = - filterDefined( - await Promise.all( - contractIds.map(async (contractId) => { - return await getContractFromId(contractId) - }) - ) - ) ?? [] - - return [...contracts] -} - export function listenForGroup( groupId: string, setGroup: (group: Group | null) => void diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index 43647cdb..c00a8e29 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -3,14 +3,13 @@ import { take, sortBy, debounce } from 'lodash' import { Group } from 'common/group' import { Page } from 'web/components/page' import { listAllBets } from 'web/lib/firebase/bets' -import { Contract } from 'web/lib/firebase/contracts' +import { Contract, listContractsByGroupSlug } from 'web/lib/firebase/contracts' import { groupPath, getGroupBySlug, updateGroup, addUserToGroup, addContractToGroup, - getGroupContracts, } from 'web/lib/firebase/groups' import { Row } from 'web/components/layout/row' import { UserLink } from 'web/components/user-page' @@ -22,7 +21,7 @@ import { } from 'web/lib/firebase/users' import { Col } from 'web/components/layout/col' import { useUser } from 'web/hooks/use-user' -import { listMembers, useGroup, useMembers } from 'web/hooks/use-group' +import { listMembers, useGroup } from 'web/hooks/use-group' import { useRouter } from 'next/router' import { scoreCreators, scoreTraders } from 'common/scoring' import { Leaderboard } from 'web/components/leaderboard' @@ -62,14 +61,11 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { const { slugs } = props.params const group = await getGroupBySlug(slugs[0]) - const members = - group && group.memberIds.length < 100 ? await listMembers(group) : [] + const members = group && (await listMembers(group)) const creatorPromise = group ? getUser(group.creatorId) : null const contracts = - group && group.contractIds.length < 100 - ? await getGroupContracts(group).catch((_) => []) - : [] + (group && (await listContractsByGroupSlug(group.slug))) ?? [] const bets = await Promise.all( contracts.map((contract: Contract) => listAllBets(contract.id)) @@ -77,10 +73,12 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { const creatorScores = scoreCreators(contracts) const traderScores = scoreTraders(contracts, bets) - const [topCreators, topTraders] = await Promise.all([ - toTopUsers(creatorScores), - toTopUsers(traderScores), - ]) + const [topCreators, topTraders] = + (members && [ + toTopUsers(creatorScores, members), + toTopUsers(traderScores, members), + ]) ?? + [] const creator = await creatorPromise @@ -99,14 +97,14 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { } } -async function toTopUsers(userScores: { [userId: string]: number }) { +function toTopUsers(userScores: { [userId: string]: number }, users: User[]) { const topUserPairs = take( sortBy(Object.entries(userScores), ([_, score]) => -1 * score), 10 ).filter(([_, score]) => score >= 0.5) - const topUsers = await Promise.all( - topUserPairs.map(([userId]) => getUser(userId)) + const topUsers = topUserPairs.map( + ([userId]) => users.filter((user) => user.id === userId)[0] ) return topUsers.filter((user) => user) } @@ -199,6 +197,7 @@ export default function GroupPage(props: { creator={creator} isCreator={!!isCreator} user={user} + members={members} /> ) @@ -327,8 +326,9 @@ function GroupOverview(props: { creator: User user: User | null | undefined isCreator: boolean + members: User[] }) { - const { group, creator, isCreator, user } = props + const { group, creator, isCreator, user, members } = props const anyoneCanJoinChoices: { [key: string]: string } = { Closed: 'false', Open: 'true', @@ -403,7 +403,7 @@ function GroupOverview(props: { )} - + @@ -426,10 +426,9 @@ function SearchBar(props: { setQuery: (query: string) => void }) { ) } -function GroupMemberSearch(props: { group: Group }) { +function GroupMemberSearch(props: { members: User[] }) { const [query, setQuery] = useState('') - const { group } = props - const members = useMembers(group, 100) + const { members } = props // TODO use find-active-contracts to sort by? const matches = sortBy(members, [(member) => member.name]).filter( @@ -455,29 +454,6 @@ function GroupMemberSearch(props: { group: Group }) { ) } -export function GroupMembersList(props: { group: Group }) { - const { group } = props - const maxMembersToShow = 3 - const members = useMembers(group, maxMembersToShow).filter( - (m) => m.id !== group.creatorId - ) - if (group.memberIds.length === 1) return
- return ( -
- Other members - {members.slice(0, maxMembersToShow).map((member, i) => ( -
- - {members.length > 1 && i !== members.length - 1 && ,} -
- ))} - {group.memberIds.length > maxMembersToShow && ( - & {group.memberIds.length - maxMembersToShow} more - )} -
- ) -} - function SortedLeaderboard(props: { users: User[] scoreFunction: (user: User) => number diff --git a/web/pages/groups.tsx b/web/pages/groups.tsx index 9192a094..2523b789 100644 --- a/web/pages/groups.tsx +++ b/web/pages/groups.tsx @@ -1,23 +1,23 @@ import { sortBy, debounce } from 'lodash' import Link from 'next/link' -import { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react' import { Group } from 'common/group' import { CreateGroupButton } from 'web/components/groups/create-group-button' import { Col } from 'web/components/layout/col' import { Row } from 'web/components/layout/row' import { Page } from 'web/components/page' import { Title } from 'web/components/title' -import { useGroups, useMemberGroupIds } from 'web/hooks/use-group' +import { useGroups, useMemberGroupIds, useMembers } from 'web/hooks/use-group' import { useUser } from 'web/hooks/use-user' import { groupPath, listAllGroups } from 'web/lib/firebase/groups' import { getUser, User } from 'web/lib/firebase/users' import { Tabs } from 'web/components/layout/tabs' -import { GroupMembersList } from 'web/pages/group/[...slugs]' import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params' import { SiteLink } from 'web/components/site-link' import clsx from 'clsx' import { Avatar } from 'web/components/avatar' import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button' +import { UserLink } from 'web/components/user-page' export async function getStaticProps() { const groups = await listAllGroups().catch((_) => []) @@ -201,6 +201,29 @@ export function GroupCard(props: { group: Group; creator: User | undefined }) { ) } +function GroupMembersList(props: { group: Group }) { + const { group } = props + const maxMembersToShow = 3 + const members = useMembers(group, maxMembersToShow).filter( + (m) => m.id !== group.creatorId + ) + if (group.memberIds.length === 1) return
+ return ( +
+ Other members + {members.slice(0, maxMembersToShow).map((member, i) => ( +
+ + {members.length > 1 && i !== members.length - 1 && ,} +
+ ))} + {group.memberIds.length > maxMembersToShow && ( + & {group.memberIds.length - maxMembersToShow} more + )} +
+ ) +} + export function GroupLink(props: { group: Group; className?: string }) { const { group, className } = props