Explore groups page

This commit is contained in:
James Grugett 2022-09-13 23:40:56 -05:00
parent 5219a63667
commit 7f9925a8c4
6 changed files with 106 additions and 53 deletions

View File

@ -2,16 +2,16 @@ import { useFirestoreQueryData } from '@react-query-firebase/firestore'
import { useEffect, useState } from 'react'
import {
Contract,
listenForActiveContracts,
listenForContracts,
listenForHotContracts,
listenForInactiveContracts,
listenForNewContracts,
getUserBetContracts,
getUserBetContractsQuery,
trendingContractsQuery,
} from 'web/lib/firebase/contracts'
import { useQueryClient } from 'react-query'
import { MINUTE_MS } from 'common/util/time'
import { query, limit } from 'firebase/firestore'
export const useContracts = () => {
const [contracts, setContracts] = useState<Contract[] | undefined>()
@ -23,23 +23,12 @@ export const useContracts = () => {
return contracts
}
export const useActiveContracts = () => {
const [activeContracts, setActiveContracts] = useState<
Contract[] | undefined
>()
const [newContracts, setNewContracts] = useState<Contract[] | undefined>()
useEffect(() => {
return listenForActiveContracts(setActiveContracts)
}, [])
useEffect(() => {
return listenForNewContracts(setNewContracts)
}, [])
if (!activeContracts || !newContracts) return undefined
return [...activeContracts, ...newContracts]
export const useTrendingContracts = (maxContracts: number) => {
const result = useFirestoreQueryData(
['trending-contracts', maxContracts],
query(trendingContractsQuery, limit(maxContracts))
)
return result.data
}
export const useInactiveContracts = () => {

View File

@ -11,6 +11,7 @@ import {
listenForMemberGroupIds,
listenForOpenGroups,
listGroups,
topFollowedGroupsQuery,
} from 'web/lib/firebase/groups'
import { getUser } from 'web/lib/firebase/users'
import { filterDefined } from 'common/util/array'
@ -18,6 +19,8 @@ import { Contract } from 'common/contract'
import { uniq } from 'lodash'
import { listenForValues } from 'web/lib/firebase/utils'
import { useQuery } from 'react-query'
import { useFirestoreQueryData } from '@react-query-firebase/firestore'
import { limit, query } from 'firebase/firestore'
export const useGroup = (groupId: string | undefined) => {
const [group, setGroup] = useState<Group | null | undefined>()
@ -49,6 +52,14 @@ export const useOpenGroups = () => {
return groups
}
export const useTopFollowedGroups = (count: number) => {
const result = useFirestoreQueryData(
['top-followed-contracts', count],
query(topFollowedGroupsQuery, limit(count))
)
return result.data
}
export const useMemberGroups = (userId: string | null | undefined) => {
const result = useQuery(['member-groups', userId ?? ''], () =>
getMemberGroups(userId ?? '')

View File

@ -176,23 +176,6 @@ export function getUserBetContractsQuery(userId: string) {
) as Query<Contract>
}
const activeContractsQuery = query(
contracts,
where('isResolved', '==', false),
where('visibility', '==', 'public'),
where('volume7Days', '>', 0)
)
export function getActiveContracts() {
return getValues<Contract>(activeContractsQuery)
}
export function listenForActiveContracts(
setContracts: (contracts: Contract[]) => void
) {
return listenForValues<Contract>(activeContractsQuery, setContracts)
}
const inactiveContractsQuery = query(
contracts,
where('isResolved', '==', false),
@ -282,16 +265,17 @@ export function listenForHotContracts(
})
}
const trendingContractsQuery = query(
export const trendingContractsQuery = query(
contracts,
where('isResolved', '==', false),
where('visibility', '==', 'public'),
orderBy('popularityScore', 'desc'),
limit(10)
orderBy('popularityScore', 'desc')
)
export async function getTrendingContracts() {
return await getValues<Contract>(trendingContractsQuery)
export async function getTrendingContracts(maxContracts = 10) {
return await getValues<Contract>(
query(trendingContractsQuery, limit(maxContracts))
)
}
export async function getContractsBySlugs(slugs: string[]) {

View File

@ -6,6 +6,7 @@ import {
doc,
getDocs,
onSnapshot,
orderBy,
query,
setDoc,
updateDoc,
@ -257,3 +258,8 @@ export async function listMembers(group: Group) {
const members = await getValues<GroupMemberDoc>(groupMembers(group.id))
return await Promise.all(members.map((m) => m.userId).map(getUser))
}
export const topFollowedGroupsQuery = query(
groups,
orderBy('totalMembers', 'desc')
)

View File

@ -0,0 +1,55 @@
import Masonry from 'react-masonry-css'
import { filterDefined } from 'common/util/array'
import { keyBy, uniqBy } from 'lodash'
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 { useTrendingContracts } from 'web/hooks/use-contracts'
import { useTopFollowedGroups } from 'web/hooks/use-group'
import { useUser } from 'web/hooks/use-user'
import { GroupCard } from '../groups'
export default function Explore() {
const user = useUser()
const topGroups = useTopFollowedGroups(200)
const groupsById = keyBy(topGroups, 'id')
const trendingContracts = useTrendingContracts(200)
const groupLinks = uniqBy(
(trendingContracts ?? []).map((c) => c.groupLinks ?? []).flat(),
(link) => link.groupId
)
const groups = filterDefined(
groupLinks.map((link) => groupsById[link.groupId])
).filter((group) => group.totalMembers >= 3)
return (
<Page>
<Col className="pm:mx-10 gap-4 px-4 pb-12 xl:w-[115%]">
<Row className={'w-full items-center justify-between'}>
<Title className="!mb-0" text="Explore" />
</Row>
<Masonry
// Show only 1 column on tailwind's md breakpoint (768px)
breakpointCols={{ default: 3, 1200: 2, 570: 1 }}
className="-ml-4 flex w-auto self-center"
columnClassName="pl-4 bg-clip-padding"
>
{groups.map((g) => (
<GroupCard
className="mb-4 !min-w-[250px]"
group={g}
creator={null}
user={user}
isMember={false}
/>
))}
</Masonry>
</Col>
</Page>
)
}

View File

@ -171,17 +171,24 @@ export default function Groups(props: {
export function GroupCard(props: {
group: Group
creator: User | undefined
creator: User | null | undefined
user: User | undefined | null
isMember: boolean
className?: string
}) {
const { group, creator, user, isMember } = props
const { group, creator, user, isMember, className } = props
const { totalContracts } = group
return (
<Col className="relative min-w-[20rem] max-w-xs gap-1 rounded-xl bg-white p-8 shadow-md hover:bg-gray-100">
<Col
className={clsx(
'relative min-w-[20rem] max-w-xs gap-1 rounded-xl bg-white p-6 shadow-md hover:bg-gray-100',
className
)}
>
<Link href={groupPath(group.slug)}>
<a className="absolute left-0 right-0 top-0 bottom-0 z-0" />
</Link>
{creator !== null && (
<div>
<Avatar
className={'absolute top-2 right-2 z-10'}
@ -191,6 +198,7 @@ export function GroupCard(props: {
size={12}
/>
</div>
)}
<Row className="items-center justify-between gap-2">
<span className="text-xl">{group.name}</span>
</Row>