From 0ccba20d5c624417d85b0d38c1a7fc76cca46710 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Sun, 27 Feb 2022 13:37:04 -0800 Subject: [PATCH] Add explore tab on home feed that shows random inactive markets. --- web/components/contract-feed.tsx | 24 +++++ web/components/fast-fold-following.tsx | 2 +- web/hooks/use-contracts.ts | 11 +++ ...tracts.ts => use-find-active-contracts.ts} | 64 ++++++++----- web/lib/firebase/contracts.ts | 24 ++++- web/pages/activity.tsx | 20 +++- web/pages/home.tsx | 94 ++++++++++++++----- 7 files changed, 187 insertions(+), 52 deletions(-) rename web/hooks/{use-active-contracts.ts => use-find-active-contracts.ts} (66%) diff --git a/web/components/contract-feed.tsx b/web/components/contract-feed.tsx index 58dd5f01..c582a043 100644 --- a/web/components/contract-feed.tsx +++ b/web/components/contract-feed.tsx @@ -790,3 +790,27 @@ export function ContractFeed(props: { ) } + +export function ContractSummaryFeed(props: { + contract: Contract + betRowClassName?: string +}) { + const { contract, betRowClassName } = props + const { outcomeType } = contract + const isBinary = outcomeType === 'BINARY' + + return ( +
+
+
+
+ +
+
+
+ {isBinary && tradingAllowed(contract) && ( + + )} +
+ ) +} diff --git a/web/components/fast-fold-following.tsx b/web/components/fast-fold-following.tsx index d4d350ee..db0bd105 100644 --- a/web/components/fast-fold-following.tsx +++ b/web/components/fast-fold-following.tsx @@ -101,7 +101,7 @@ export const FastFoldFollowing = (props: { ]} /> - + ) } diff --git a/web/hooks/use-contracts.ts b/web/hooks/use-contracts.ts index 60744c33..af36cd82 100644 --- a/web/hooks/use-contracts.ts +++ b/web/hooks/use-contracts.ts @@ -5,6 +5,7 @@ import { listenForActiveContracts, listenForContracts, listenForHotContracts, + listenForInactiveContracts, } from '../lib/firebase/contracts' import { listenForTaggedContracts } from '../lib/firebase/folds' @@ -28,6 +29,16 @@ export const useActiveContracts = () => { return contracts } +export const useInactiveContracts = () => { + const [contracts, setContracts] = useState() + + useEffect(() => { + return listenForInactiveContracts(setContracts) + }, []) + + return contracts +} + export const useUpdatedContracts = (initialContracts: Contract[]) => { const [contracts, setContracts] = useState(initialContracts) diff --git a/web/hooks/use-active-contracts.ts b/web/hooks/use-find-active-contracts.ts similarity index 66% rename from web/hooks/use-active-contracts.ts rename to web/hooks/use-find-active-contracts.ts index 1c4c1b91..f8aa5627 100644 --- a/web/hooks/use-active-contracts.ts +++ b/web/hooks/use-find-active-contracts.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { useRef } from 'react' +import { useMemo, useRef } from 'react' import { Fold } from '../../common/fold' import { User } from '../../common/user' @@ -9,7 +9,7 @@ import { Comment, getRecentComments } from '../lib/firebase/comments' import { Contract, getActiveContracts } from '../lib/firebase/contracts' import { listAllFolds } from '../lib/firebase/folds' import { findActiveContracts } from '../pages/activity' -import { useActiveContracts } from './use-contracts' +import { useInactiveContracts } from './use-contracts' import { useFollowedFolds } from './use-fold' import { useUserBetContracts } from './use-user-bets' @@ -28,24 +28,15 @@ export const getAllContractInfo = async () => { return { contracts, recentBets, recentComments, folds } } -export const useFindActiveContracts = ( - props: { - contracts: Contract[] - folds: Fold[] - recentBets: Bet[] - recentComments: Comment[] - }, - user: User | undefined | null +export const useFilterYourContracts = ( + user: User | undefined | null, + folds: Fold[], + contracts: Contract[] ) => { - const { recentBets, recentComments } = props - const contracts = useActiveContracts() ?? props.contracts - const followedFoldIds = useFollowedFolds(user) const followedFolds = filterDefined( - (followedFoldIds ?? []).map((id) => - props.folds.find((fold) => fold.id === id) - ) + (followedFoldIds ?? []).map((id) => folds.find((fold) => fold.id === id)) ) // Save the initial followed fold slugs. @@ -64,20 +55,33 @@ export const useFindActiveContracts = ( : undefined // Show no contracts before your info is loaded. - let feedContracts: Contract[] = [] + let yourContracts: Contract[] = [] if (yourBetContracts && followedFoldIds) { // Show all contracts if no folds are followed. - if (followedFoldIds.length === 0) feedContracts = contracts + if (followedFoldIds.length === 0) yourContracts = contracts else - feedContracts = contracts.filter( + yourContracts = contracts.filter( (contract) => contract.lowercaseTags.some((tag) => tagSet.has(tag)) || yourBetContracts.has(contract.id) ) } + return { + yourContracts, + initialFollowedFoldSlugs, + } +} + +export const useFindActiveContracts = (props: { + contracts: Contract[] + recentBets: Bet[] + recentComments: Comment[] +}) => { + const { contracts, recentBets, recentComments } = props + const activeContracts = findActiveContracts( - feedContracts, + contracts, recentComments, recentBets ) @@ -101,6 +105,24 @@ export const useFindActiveContracts = ( activeContracts, activeBets, activeComments, - initialFollowedFoldSlugs, } } + +export const useExploreContracts = (maxContracts = 75) => { + const inactiveContracts = useInactiveContracts() + + const contractsDict = _.fromPairs( + (inactiveContracts ?? []).map((c) => [c.id, c]) + ) + + // Preserve random ordering once inactiveContracts loaded. + const exploreContractIds = useMemo( + () => _.shuffle(Object.keys(contractsDict)), + // eslint-disable-next-line react-hooks/exhaustive-deps + [!!inactiveContracts] + ).slice(0, maxContracts) + + if (!inactiveContracts) return undefined + + return filterDefined(exploreContractIds.map((id) => contractsDict[id])) +} diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts index b22a547f..eb1b65e1 100644 --- a/web/lib/firebase/contracts.ts +++ b/web/lib/firebase/contracts.ts @@ -115,7 +115,7 @@ export function listenForContracts( return listenForValues(q, setContracts) } -const activeContracts = query( +const activeContractsQuery = query( contractCollection, where('isResolved', '==', false), where('visibility', '==', 'public'), @@ -123,13 +123,31 @@ const activeContracts = query( ) export function getActiveContracts() { - return getValues(activeContracts) + return getValues(activeContractsQuery) } export function listenForActiveContracts( setContracts: (contracts: Contract[]) => void ) { - return listenForValues(activeContracts, setContracts) + return listenForValues(activeContractsQuery, setContracts) +} + +const inactiveContractsQuery = query( + contractCollection, + where('isResolved', '==', false), + where('closeTime', '>', Date.now()), + where('visibility', '==', 'public'), + where('volume24Hours', '==', 0) +) + +export function getInactiveContracts() { + return getValues(inactiveContractsQuery) +} + +export function listenForInactiveContracts( + setContracts: (contracts: Contract[]) => void +) { + return listenForValues(inactiveContractsQuery, setContracts) } export function listenForContract( diff --git a/web/pages/activity.tsx b/web/pages/activity.tsx index e546e1d1..bab58328 100644 --- a/web/pages/activity.tsx +++ b/web/pages/activity.tsx @@ -1,5 +1,5 @@ import _ from 'lodash' -import { ContractFeed } from '../components/contract-feed' +import { ContractFeed, ContractSummaryFeed } from '../components/contract-feed' import { Page } from '../components/page' import { Contract } from '../lib/firebase/contracts' import { Comment } from '../lib/firebase/comments' @@ -99,6 +99,24 @@ export function ActivityFeed(props: { ) } +export function SummaryActivityFeed(props: { contracts: Contract[] }) { + const { contracts } = props + + return ( + + + + {contracts.map((contract) => ( +
+ +
+ ))} + + + + ) +} + export default function ActivityPage() { return ( diff --git a/web/pages/home.tsx b/web/pages/home.tsx index 7bd305df..f5c8bf69 100644 --- a/web/pages/home.tsx +++ b/web/pages/home.tsx @@ -1,9 +1,12 @@ -import React from 'react' +import React, { useState } from 'react' import Router from 'next/router' +import { SparklesIcon, GlobeAltIcon } from '@heroicons/react/solid' +import clsx from 'clsx' +import _ from 'lodash' import { Contract } from '../lib/firebase/contracts' import { Page } from '../components/page' -import { ActivityFeed } from './activity' +import { ActivityFeed, SummaryActivityFeed } from './activity' import { Comment } from '../lib/firebase/comments' import { Bet } from '../lib/firebase/bets' import FeedCreate from '../components/feed-create' @@ -13,13 +16,15 @@ import { useUser } from '../hooks/use-user' import { Fold } from '../../common/fold' import { LoadingIndicator } from '../components/loading-indicator' import { Row } from '../components/layout/row' -import { SparklesIcon } from '@heroicons/react/solid' import { FastFoldFollowing } from '../components/fast-fold-following' import { getAllContractInfo, + useExploreContracts, + useFilterYourContracts, useFindActiveContracts, -} from '../hooks/use-active-contracts' +} from '../hooks/use-find-active-contracts' import { useGetRecentBets } from '../hooks/use-bets' +import { useActiveContracts } from '../hooks/use-contracts' export async function getStaticProps() { const contractInfo = await getAllContractInfo() @@ -36,21 +41,28 @@ const Home = (props: { recentBets: Bet[] recentComments: Comment[] }) => { - const { contracts, folds, recentComments } = props + const { folds, recentComments } = props const user = useUser() - const recentBets = useGetRecentBets() - - const { - activeContracts, - activeBets, - activeComments, - initialFollowedFoldSlugs, - } = useFindActiveContracts( - { contracts, folds, recentBets: recentBets ?? [], recentComments }, - user + const contracts = useActiveContracts() ?? props.contracts + const { yourContracts, initialFollowedFoldSlugs } = useFilterYourContracts( + user, + folds, + contracts ) + const recentBets = useGetRecentBets() + const { activeContracts, activeBets, activeComments } = + useFindActiveContracts({ + contracts: yourContracts, + recentBets: recentBets ?? [], + recentComments, + }) + + const exploreContracts = useExploreContracts() + + const [feedMode, setFeedMode] = useState<'activity' | 'explore'>('activity') + if (user === null) { Router.replace('/') return <> @@ -71,22 +83,52 @@ const Home = (props: { /> )} + + - - {activeContracts && recentBets ? ( - - ) : ( - - )} + {feedMode === 'activity' && + (recentBets ? ( + + ) : ( + + ))} + + {feedMode === 'explore' && + (exploreContracts ? ( + + ) : ( + + ))}