Group leaderboards show members only by default

This commit is contained in:
Austin Chen 2022-06-24 18:38:39 -05:00
parent 8357361038
commit da81035e58
3 changed files with 148 additions and 61 deletions

View File

@ -0,0 +1,38 @@
/* This example requires Tailwind CSS v2.0+ */
import { Switch } from '@headlessui/react'
import clsx from 'clsx'
export default function ShortToggle(props: {
enabled: boolean
setEnabled: (enabled: boolean) => void
}) {
const { enabled, setEnabled } = props
return (
<Switch
checked={enabled}
onChange={setEnabled}
className="group relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
<span className="sr-only">Use setting</span>
<span
aria-hidden="true"
className="pointer-events-none absolute h-full w-full rounded-md bg-white"
/>
<span
aria-hidden="true"
className={clsx(
enabled ? 'bg-indigo-600' : 'bg-gray-200',
'pointer-events-none absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out'
)}
/>
<span
aria-hidden="true"
className={clsx(
enabled ? 'translate-x-5' : 'translate-x-0',
'pointer-events-none absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow ring-0 transition-transform duration-200 ease-in-out'
)}
/>
</Switch>
)
}

View File

@ -65,19 +65,18 @@ export const useMemberGroupIds = (user: User | null | undefined) => {
export function useMembers(group: Group) {
const [members, setMembers] = useState<User[]>([])
useEffect(() => {
const { memberIds, creatorId } = group
if (memberIds.length > 1)
// get users via their user ids:
Promise.all(
memberIds.filter((mId) => mId !== creatorId).map(getUser)
).then((users) => {
const members = users.filter((user) => user)
setMembers(members)
})
const { memberIds } = group
if (memberIds.length > 0) {
listMembers(group).then((members) => setMembers(members))
}
}, [group])
return members
}
export async function listMembers(group: Group) {
return await Promise.all(group.memberIds.map(getUser))
}
export const useGroupsWithContract = (contractId: string | undefined) => {
const [groups, setGroups] = useState<Group[] | null | undefined>()

View File

@ -17,7 +17,7 @@ import { firebaseLogin, getUser, User } from 'web/lib/firebase/users'
import { Spacer } from 'web/components/layout/spacer'
import { Col } from 'web/components/layout/col'
import { useUser } from 'web/hooks/use-user'
import { useGroup, useMembers } from 'web/hooks/use-group'
import { listMembers, useGroup, useMembers } from 'web/hooks/use-group'
import { useRouter } from 'next/router'
import { scoreCreators, scoreTraders } from 'common/scoring'
import { Leaderboard } from 'web/components/leaderboard'
@ -39,12 +39,14 @@ import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params'
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
import { toast } from 'react-hot-toast'
import { useCommentsOnGroup } from 'web/hooks/use-comments'
import ShortToggle from 'web/components/widgets/short-toggle'
export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: { params: { slugs: string[] } }) {
const { slugs } = props.params
const group = await getGroupBySlug(slugs[0])
const members = group ? await listMembers(group) : []
const creatorPromise = group ? getUser(group.creatorId) : null
const contracts = group ? await getGroupContracts(group).catch((_) => []) : []
@ -65,6 +67,7 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) {
return {
props: {
group,
members,
creator,
traderScores,
topTraders,
@ -95,6 +98,7 @@ const groupSubpages = [undefined, 'chat', 'questions', 'details'] as const
export default function GroupPage(props: {
group: Group | null
members: User[]
creator: User
traderScores: { [userId: string]: number }
topTraders: User[]
@ -103,14 +107,21 @@ export default function GroupPage(props: {
}) {
props = usePropz(props, getStaticPropz) ?? {
group: null,
members: [],
creator: null,
traderScores: {},
topTraders: [],
creatorScores: {},
topCreators: [],
}
const { creator, traderScores, topTraders, creatorScores, topCreators } =
props
const {
creator,
members,
traderScores,
topTraders,
creatorScores,
topCreators,
} = props
const router = useRouter()
const { slugs } = router.query as { slugs: string[] }
@ -175,15 +186,15 @@ export default function GroupPage(props: {
user={user}
/>
<Spacer h={8} />
<Col className="mt-4 gap-8 px-4 md:flex-row">
<GroupLeaderboards
traderScores={traderScores}
creatorScores={creatorScores}
topTraders={topTraders}
topCreators={topCreators}
user={user}
/>
</Col>
<GroupLeaderboards
traderScores={traderScores}
creatorScores={creatorScores}
topTraders={topTraders}
topCreators={topCreators}
members={members}
user={user}
/>
</Col>
)
return (
@ -312,7 +323,7 @@ function GroupOverview(props: {
return (
<Col>
<Row className="items-center justify-end rounded-t bg-indigo-500 px-4 py-3 text-sm text-white">
<Row className="flex-1 justify-start">About group</Row>
<Row className="flex-1 justify-start">About {group.name}</Row>
{isCreator && <EditGroupButton className={'ml-1'} group={group} />}
</Row>
<Col className="gap-2 rounded-b bg-white p-4">
@ -324,7 +335,6 @@ function GroupOverview(props: {
username={creator.username}
/>
</Row>
<GroupMembersList group={group} />
<Row className={'items-center gap-1'}>
<span className={'text-gray-500'}>Membership</span>
{user && user.id === creator.id ? (
@ -343,14 +353,6 @@ function GroupOverview(props: {
</span>
)}
</Row>
{about && (
<>
<Spacer h={2} />
<div className="text-gray-500">
<Linkify text={about} />
</div>
</>
)}
</Col>
</Col>
)
@ -381,46 +383,94 @@ export function GroupMembersList(props: { group: Group }) {
)
}
function SortedLeaderboard(props: {
users: User[]
scoreFunction: (user: User) => number
title: string
header: string
}) {
const { users, scoreFunction, title, header } = props
const sortedUsers = users.sort((a, b) => scoreFunction(b) - scoreFunction(a))
return (
<Leaderboard
className="max-w-xl"
users={sortedUsers}
title={title}
columns={[
{ header, renderCell: (user) => formatMoney(scoreFunction(user)) },
]}
/>
)
}
function GroupLeaderboards(props: {
traderScores: { [userId: string]: number }
creatorScores: { [userId: string]: number }
topTraders: User[]
topCreators: User[]
members: User[]
user: User | null | undefined
}) {
const { traderScores, creatorScores, topTraders, topCreators } = props
const topTraderScores = topTraders.map((user) => traderScores[user.id])
const topCreatorScores = topCreators.map((user) => creatorScores[user.id])
const { traderScores, creatorScores, members, topTraders, topCreators } =
props
const [includeOutsiders, setIncludeOutsiders] = useState(false)
// Consider hiding M$0
return (
<>
<Leaderboard
className="max-w-xl"
title="🏅 Top bettors"
users={topTraders}
columns={[
{
header: 'Profit',
renderCell: (user) =>
formatMoney(topTraderScores[topTraders.indexOf(user)]),
},
]}
/>
<Col>
<Row className="items-center justify-end gap-4 text-gray-500">
Include all users
<ShortToggle
enabled={includeOutsiders}
setEnabled={setIncludeOutsiders}
/>
</Row>
<Leaderboard
className="max-w-xl"
title="🏅 Top creators"
users={topCreators}
columns={[
{
header: 'Market volume',
renderCell: (user) =>
formatMoney(topCreatorScores[topCreators.indexOf(user)]),
},
]}
/>
</>
<div className="mt-4 flex flex-col gap-8 px-4 md:flex-row">
{!includeOutsiders ? (
<>
<SortedLeaderboard
users={members}
scoreFunction={(user) => traderScores[user.id] ?? 0}
title="🏅 Top bettors"
header="Profit"
/>
<SortedLeaderboard
users={members}
scoreFunction={(user) => creatorScores[user.id] ?? 0}
title="🏅 Top creators"
header="Market volume"
/>
</>
) : (
<>
<Leaderboard
className="max-w-xl"
title="🏅 Top bettors"
users={topTraders}
columns={[
{
header: 'Profit',
renderCell: (user) => formatMoney(traderScores[user.id] ?? 0),
},
]}
/>
<Leaderboard
className="max-w-xl"
title="🏅 Top creators"
users={topCreators}
columns={[
{
header: 'Market volume',
renderCell: (user) =>
formatMoney(creatorScores[user.id] ?? 0),
},
]}
/>
</>
)}
</div>
</Col>
)
}