Try new way of calculating rankings for large groups
This commit is contained in:
parent
eb6b1b9f89
commit
4eba3c8124
|
@ -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) => {
|
||||
|
|
|
@ -124,6 +124,14 @@ export async function listContracts(creatorId: string): Promise<Contract[]> {
|
|||
return snapshot.docs.map((doc) => doc.data())
|
||||
}
|
||||
|
||||
export async function listContractsByGroupSlug(
|
||||
slug: string
|
||||
): Promise<Contract[]> {
|
||||
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<Contract[]> {
|
||||
|
|
|
@ -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<Group>('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
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</Col>
|
||||
)
|
||||
|
@ -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: {
|
|||
</Row>
|
||||
)}
|
||||
<Col className={'mt-2'}>
|
||||
<GroupMemberSearch group={group} />
|
||||
<GroupMemberSearch members={members} />
|
||||
</Col>
|
||||
</Col>
|
||||
</>
|
||||
|
@ -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 <div />
|
||||
return (
|
||||
<div className="text-neutral flex flex-wrap gap-1">
|
||||
<span className={'text-gray-500'}>Other members</span>
|
||||
{members.slice(0, maxMembersToShow).map((member, i) => (
|
||||
<div key={member.id} className={'flex-shrink'}>
|
||||
<UserLink name={member.name} username={member.username} />
|
||||
{members.length > 1 && i !== members.length - 1 && <span>,</span>}
|
||||
</div>
|
||||
))}
|
||||
{group.memberIds.length > maxMembersToShow && (
|
||||
<span> & {group.memberIds.length - maxMembersToShow} more</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SortedLeaderboard(props: {
|
||||
users: User[]
|
||||
scoreFunction: (user: User) => number
|
||||
|
|
|
@ -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 <div />
|
||||
return (
|
||||
<div className="text-neutral flex flex-wrap gap-1">
|
||||
<span className={'text-gray-500'}>Other members</span>
|
||||
{members.slice(0, maxMembersToShow).map((member, i) => (
|
||||
<div key={member.id} className={'flex-shrink'}>
|
||||
<UserLink name={member.name} username={member.username} />
|
||||
{members.length > 1 && i !== members.length - 1 && <span>,</span>}
|
||||
</div>
|
||||
))}
|
||||
{group.memberIds.length > maxMembersToShow && (
|
||||
<span> & {group.memberIds.length - maxMembersToShow} more</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function GroupLink(props: { group: Group; className?: string }) {
|
||||
const { group, className } = props
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user