Explore groups page
This commit is contained in:
parent
5219a63667
commit
7f9925a8c4
|
@ -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 = () => {
|
||||
|
|
|
@ -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 ?? '')
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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')
|
||||
)
|
||||
|
|
55
web/pages/experimental/explore-groups.tsx
Normal file
55
web/pages/experimental/explore-groups.tsx
Normal 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>
|
||||
)
|
||||
}
|
|
@ -171,26 +171,34 @@ 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>
|
||||
<div>
|
||||
<Avatar
|
||||
className={'absolute top-2 right-2 z-10'}
|
||||
username={creator?.username}
|
||||
avatarUrl={creator?.avatarUrl}
|
||||
noLink={false}
|
||||
size={12}
|
||||
/>
|
||||
</div>
|
||||
{creator !== null && (
|
||||
<div>
|
||||
<Avatar
|
||||
className={'absolute top-2 right-2 z-10'}
|
||||
username={creator?.username}
|
||||
avatarUrl={creator?.avatarUrl}
|
||||
noLink={false}
|
||||
size={12}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Row className="items-center justify-between gap-2">
|
||||
<span className="text-xl">{group.name}</span>
|
||||
</Row>
|
||||
|
|
Loading…
Reference in New Issue
Block a user