Fold type, fold page, query for fold contracts

This commit is contained in:
jahooma 2022-01-19 16:16:08 -06:00
parent 5ce23c0bdb
commit 7a2eac604d
10 changed files with 155 additions and 12 deletions

17
common/fold.ts Normal file
View File

@ -0,0 +1,17 @@
export type Fold = {
id: string
slug: string
name: string
curatorId: string // User id
createdTime: number
tags: string
contractIds: string[]
excludedContractIds: string[]
// Invariant: exactly one of the following is defined.
// Default: creatorIds: undefined, excludedCreatorIds: []
creatorIds?: string[]
excludedCreatorIds?: string[]
}

View File

@ -35,5 +35,8 @@ service cloud.firestore {
allow read;
}
match /folds/{foldId} {
allow read;
}
}
}

View File

@ -75,9 +75,7 @@ const recentCommentsQuery = query(
)
export async function getRecentComments() {
const snapshot = await getDocs(recentCommentsQuery)
const comments = snapshot.docs.map((doc) => doc.data() as Comment)
return comments
return getValues<Comment>(recentCommentsQuery)
}
export function listenForRecentComments(

View File

@ -54,7 +54,7 @@ export function contractMetrics(contract: Contract) {
}
const db = getFirestore(app)
const contractCollection = collection(db, 'contracts')
export const contractCollection = collection(db, 'contracts')
// Push contract to Firestore
export async function setContract(contract: Contract) {

57
web/lib/firebase/folds.ts Normal file
View File

@ -0,0 +1,57 @@
import { collection, query, where } from 'firebase/firestore'
import { Fold } from '../../../common/fold'
import { Contract, contractCollection } from './contracts'
import { db } from './init'
import { getValues } from './utils'
const foldCollection = collection(db, 'folds')
export async function getFoldBySlug(slug: string) {
const q = query(foldCollection, where('slug', '==', slug))
const folds = await getValues<Fold>(q)
return folds.length === 0 ? null : folds[0]
}
export async function getFoldContracts(fold: Fold) {
const {
tags,
contractIds,
excludedContractIds,
creatorIds,
excludedCreatorIds,
} = fold
const [tagsContracts, includedContracts] = await Promise.all([
// TODO: if tags.length > 10, execute multiple parallel queries
getValues<Contract>(
query(contractCollection, where('tags', 'array-contains-any', tags))
),
// TODO: if contractIds.length > 10, execute multiple parallel queries
contractIds.length > 0
? getValues<Contract>(
query(contractCollection, where('id', 'in', contractIds))
)
: [],
])
const excludedContractsSet = new Set(excludedContractIds)
const creatorSet = creatorIds ? new Set(creatorIds) : undefined
const excludedCreatorSet = excludedCreatorIds
? new Set(excludedCreatorIds)
: undefined
const approvedContracts = tagsContracts.filter((contract) => {
const { id, creatorId } = contract
if (excludedContractsSet.has(id)) return false
if (creatorSet && !creatorSet.has(creatorId)) return false
if (excludedCreatorSet && excludedCreatorSet.has(creatorId)) return false
return true
})
return [...approvedContracts, ...includedContracts]
}

View File

@ -8,8 +8,8 @@ import {
DocumentReference,
} from 'firebase/firestore'
export const getValue = async <T>(collectionName: string, docName: string) => {
const snap = await getDoc(doc(db, collectionName, docName))
export const getValue = async <T>(doc: DocumentReference) => {
const snap = await getDoc(doc)
return snap.exists() ? (snap.data() as T) : null
}

View File

@ -23,7 +23,9 @@ import { Bet, listAllBets } from '../../lib/firebase/bets'
import { Comment, listAllComments } from '../../lib/firebase/comments'
import Custom404 from '../404'
export async function getStaticProps(props: { params: any }) {
export async function getStaticProps(props: {
params: { username: string; contractSlug: string }
}) {
const { username, contractSlug } = props.params
const contract = (await getContractFromSlug(contractSlug)) || null
const contractId = contract?.id

View File

@ -68,15 +68,17 @@ export function ActivityFeed(props: {
contracts: Contract[]
contractBets: Bet[][]
contractComments: Comment[][]
listenForChanges?: boolean
}) {
const { contractBets, contractComments } = props
const { contractBets, contractComments, listenForChanges } = props
const contracts = useContracts() ?? props.contracts
const recentComments = useRecentComments()
const activeContracts = recentComments
? findActiveContracts(contracts, recentComments)
: props.contracts
const activeContracts =
listenForChanges && recentComments
? findActiveContracts(contracts, recentComments)
: props.contracts
return contracts.length > 0 ? (
return activeContracts.length > 0 ? (
<Col className="items-center">
<Col className="w-full max-w-3xl">
<Title text="Recent Activity" />

View File

@ -0,0 +1,63 @@
import { Fold } from '../../../common/fold'
import { Comment } from '../../../common/comment'
import { Page } from '../../components/page'
import { Title } from '../../components/title'
import { Bet, listAllBets } from '../../lib/firebase/bets'
import { getRecentComments, listAllComments } from '../../lib/firebase/comments'
import { Contract } from '../../lib/firebase/contracts'
import { getFoldBySlug, getFoldContracts } from '../../lib/firebase/folds'
import { ActivityFeed, findActiveContracts } from '../activity'
export async function getStaticProps(props: { params: { foldSlug: string } }) {
const { foldSlug } = props.params
const recentCommentsPromise = getRecentComments().catch(() => [])
const fold = await getFoldBySlug(foldSlug)
const contracts = fold ? await getFoldContracts(fold) : []
const recentComments = await recentCommentsPromise
const activeContracts = findActiveContracts(contracts, recentComments)
const activeContractBets = await Promise.all(
activeContracts.map((contract) => listAllBets(contract.id))
)
const activeContractComments = await Promise.all(
activeContracts.map((contract) => listAllComments(contract.id))
)
return {
props: {
fold,
activeContracts,
activeContractBets,
activeContractComments,
},
revalidate: 60, // regenerate after a minute
}
}
export async function getStaticPaths() {
return { paths: [], fallback: 'blocking' }
}
export default function FoldPage(props: {
fold: Fold
activeContracts: Contract[]
activeContractBets: Bet[][]
activeContractComments: Comment[][]
}) {
const { fold, activeContracts, activeContractBets, activeContractComments } =
props
return (
<Page>
<Title text={fold.name} />
<ActivityFeed
contracts={activeContracts}
contractBets={activeContractBets}
contractComments={activeContractComments}
/>
</Page>
)
}

View File

@ -73,6 +73,7 @@ const Home = (props: {
contracts={activeContracts}
contractBets={activeContractBets}
contractComments={activeContractComments}
listenForChanges
/>
</Page>
)