Merge branch 'main' into user-profile

This commit is contained in:
mantikoros 2022-01-31 14:25:31 -06:00
commit e0cfb71a8c
40 changed files with 333 additions and 230 deletions

View File

@ -174,7 +174,10 @@ export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) {
} }
function calculateMktPayout(contract: Contract, bet: Bet) { function calculateMktPayout(contract: Contract, bet: Bet) {
const p = getProbability(contract.totalShares) const p =
contract.resolutionProbability !== undefined
? contract.resolutionProbability
: getProbability(contract.totalShares)
const weightedTotal = const weightedTotal =
p * contract.totalBets.YES + (1 - p) * contract.totalBets.NO p * contract.totalBets.YES + (1 - p) * contract.totalBets.NO

View File

@ -27,6 +27,7 @@ export type Contract = {
isResolved: boolean isResolved: boolean
resolutionTime?: number // When the contract creator resolved the market resolutionTime?: number // When the contract creator resolved the market
resolution?: outcome // Chosen by creator; must be one of outcomes resolution?: outcome // Chosen by creator; must be one of outcomes
resolutionProbability?: number
volume24Hours: number volume24Hours: number
volume7Days: number volume7Days: number

View File

@ -19,7 +19,7 @@ export function getNewContract(
calcStartPool(initialProb, ante) calcStartPool(initialProb, ante)
const tags = parseTags( const tags = parseTags(
`${extraTags.map((tag) => `#${tag}`).join(' ')} ${question} ${description}` `${question} ${description} ${extraTags.map((tag) => `#${tag}`).join(' ')}`
) )
const lowercaseTags = tags.map((tag) => tag.toLowerCase()) const lowercaseTags = tags.map((tag) => tag.toLowerCase())

View File

@ -59,8 +59,16 @@ export const getStandardPayouts = (
]) // add creator fee ]) // add creator fee
} }
export const getMktPayouts = (contract: Contract, bets: Bet[]) => { export const getMktPayouts = (
const p = getProbability(contract.totalShares) contract: Contract,
bets: Bet[],
resolutionProbability?: number
) => {
const p =
resolutionProbability === undefined
? getProbability(contract.totalShares)
: resolutionProbability
const poolTotal = contract.pool.YES + contract.pool.NO const poolTotal = contract.pool.YES + contract.pool.NO
console.log('Resolved MKT at p=', p, 'pool: $M', poolTotal) console.log('Resolved MKT at p=', p, 'pool: $M', poolTotal)
@ -116,14 +124,15 @@ export const getMktPayouts = (contract: Contract, bets: Bet[]) => {
export const getPayouts = ( export const getPayouts = (
outcome: outcome, outcome: outcome,
contract: Contract, contract: Contract,
bets: Bet[] bets: Bet[],
resolutionProbability?: number
) => { ) => {
switch (outcome) { switch (outcome) {
case 'YES': case 'YES':
case 'NO': case 'NO':
return getStandardPayouts(outcome, contract, bets) return getStandardPayouts(outcome, contract, bets)
case 'MKT': case 'MKT':
return getMktPayouts(contract, bets) return getMktPayouts(contract, bets, resolutionProbability)
case 'CANCEL': case 'CANCEL':
return getCancelPayouts(contract, bets) return getCancelPayouts(contract, bets)
} }

View File

@ -3,19 +3,25 @@ export function parseTags(text: string) {
const matches = (text.match(regex) || []).map((match) => const matches = (text.match(regex) || []).map((match) =>
match.trim().substring(1) match.trim().substring(1)
) )
const tagSet = new Set(matches) const tagSet = new Set()
const uniqueTags: string[] = [] const uniqueTags: string[] = []
tagSet.forEach((tag) => uniqueTags.push(tag)) // Keep casing of last tag.
matches.reverse()
for (const tag of matches) {
const lowercase = tag.toLowerCase()
if (!tagSet.has(lowercase)) {
tagSet.add(lowercase)
uniqueTags.push(tag)
}
}
uniqueTags.reverse()
return uniqueTags return uniqueTags
} }
export function parseWordsAsTags(text: string) { export function parseWordsAsTags(text: string) {
const regex = /(?:^|\s)(?:#?[a-z0-9_]+)/gi const taggedText = text
const matches = (text.match(regex) || []) .split(/\s+/)
.map((match) => match.replace('#', '').trim()) .map((tag) => `#${tag}`)
.filter((tag) => tag) .join(' ')
const tagSet = new Set(matches) return parseTags(taggedText)
const uniqueTags: string[] = []
tagSet.forEach((tag) => uniqueTags.push(tag))
return uniqueTags
} }

View File

@ -1,5 +1,7 @@
import { getProbability } from '../../common/calculate'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'
import { User } from '../../common/user' import { User } from '../../common/user'
import { formatPercent } from '../../common/util/format'
import { sendTemplateEmail } from './send-email' import { sendTemplateEmail } from './send-email'
import { getPrivateUser, getUser } from './utils' import { getPrivateUser, getUser } from './utils'
@ -18,7 +20,8 @@ export const sendMarketResolutionEmail = async (
payout: number, payout: number,
creator: User, creator: User,
contract: Contract, contract: Contract,
resolution: 'YES' | 'NO' | 'CANCEL' | 'MKT' resolution: 'YES' | 'NO' | 'CANCEL' | 'MKT',
resolutionProbability?: number
) => { ) => {
const privateUser = await getPrivateUser(userId) const privateUser = await getPrivateUser(userId)
if ( if (
@ -31,6 +34,14 @@ export const sendMarketResolutionEmail = async (
const user = await getUser(userId) const user = await getUser(userId)
if (!user) return if (!user) return
const prob = resolutionProbability ?? getProbability(contract.totalShares)
const toDisplayResolution = {
YES: 'YES',
NO: 'NO',
CANCEL: 'N/A',
MKT: formatPercent(prob),
}
const outcome = toDisplayResolution[resolution] const outcome = toDisplayResolution[resolution]
const subject = `Resolved ${outcome}: ${contract.question}` const subject = `Resolved ${outcome}: ${contract.question}`
@ -56,5 +67,3 @@ export const sendMarketResolutionEmail = async (
templateData templateData
) )
} }
const toDisplayResolution = { YES: 'YES', NO: 'NO', CANCEL: 'N/A', MKT: 'MKT' }

View File

@ -16,17 +16,24 @@ export const resolveMarket = functions
data: { data: {
outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT' outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT'
contractId: string contractId: string
probabilityInt?: number
}, },
context context
) => { ) => {
const userId = context?.auth?.uid const userId = context?.auth?.uid
if (!userId) return { status: 'error', message: 'Not authorized' } if (!userId) return { status: 'error', message: 'Not authorized' }
const { outcome, contractId } = data const { outcome, contractId, probabilityInt } = data
if (!['YES', 'NO', 'MKT', 'CANCEL'].includes(outcome)) if (!['YES', 'NO', 'MKT', 'CANCEL'].includes(outcome))
return { status: 'error', message: 'Invalid outcome' } return { status: 'error', message: 'Invalid outcome' }
if (
probabilityInt !== undefined &&
(probabilityInt < 1 || probabilityInt > 99 || !isFinite(probabilityInt))
)
return { status: 'error', message: 'Invalid probability' }
const contractDoc = firestore.doc(`contracts/${contractId}`) const contractDoc = firestore.doc(`contracts/${contractId}`)
const contractSnap = await contractDoc.get() const contractSnap = await contractDoc.get()
if (!contractSnap.exists) if (!contractSnap.exists)
@ -42,10 +49,16 @@ export const resolveMarket = functions
const creator = await getUser(contract.creatorId) const creator = await getUser(contract.creatorId)
if (!creator) return { status: 'error', message: 'Creator not found' } if (!creator) return { status: 'error', message: 'Creator not found' }
const resolutionProbability =
probabilityInt !== undefined ? probabilityInt / 100 : undefined
await contractDoc.update({ await contractDoc.update({
isResolved: true, isResolved: true,
resolution: outcome, resolution: outcome,
resolutionTime: Date.now(), resolutionTime: Date.now(),
...(resolutionProbability === undefined
? {}
: { resolutionProbability }),
}) })
console.log('contract ', contractId, 'resolved to:', outcome) console.log('contract ', contractId, 'resolved to:', outcome)
@ -57,7 +70,12 @@ export const resolveMarket = functions
const bets = betsSnap.docs.map((doc) => doc.data() as Bet) const bets = betsSnap.docs.map((doc) => doc.data() as Bet)
const openBets = bets.filter((b) => !b.isSold && !b.sale) const openBets = bets.filter((b) => !b.isSold && !b.sale)
const payouts = getPayouts(outcome, contract, openBets) const payouts = getPayouts(
outcome,
contract,
openBets,
resolutionProbability
)
console.log('payouts:', payouts) console.log('payouts:', payouts)
@ -79,7 +97,8 @@ export const resolveMarket = functions
userPayouts, userPayouts,
creator, creator,
contract, contract,
outcome outcome,
resolutionProbability
) )
return result return result
@ -91,7 +110,8 @@ const sendResolutionEmails = async (
userPayouts: { [userId: string]: number }, userPayouts: { [userId: string]: number },
creator: User, creator: User,
contract: Contract, contract: Contract,
outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT' outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT',
resolutionProbability?: number
) => { ) => {
const nonWinners = _.difference( const nonWinners = _.difference(
_.uniq(openBets.map(({ userId }) => userId)), _.uniq(openBets.map(({ userId }) => userId)),
@ -103,7 +123,14 @@ const sendResolutionEmails = async (
] ]
await Promise.all( await Promise.all(
emailPayouts.map(([userId, payout]) => emailPayouts.map(([userId, payout]) =>
sendMarketResolutionEmail(userId, payout, creator, contract, outcome) sendMarketResolutionEmail(
userId,
payout,
creator,
contract,
outcome,
resolutionProbability
)
) )
) )
} }

View File

@ -1,20 +1,14 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('stephen')
import { Bet } from '../../../common/bet' import { Bet } from '../../../common/bet'
import { getProbability } from '../../../common/calculate' import { getProbability } from '../../../common/calculate'
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
type DocRef = admin.firestore.DocumentReference type DocRef = admin.firestore.DocumentReference
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
// const serviceAccount = require('../../../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
const serviceAccount = require('../../../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-351a65eca3.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function migrateContract(contractRef: DocRef, contract: Contract) { async function migrateContract(contractRef: DocRef, contract: Contract) {

View File

@ -1,17 +1,11 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('stephen')
import { PrivateUser, STARTING_BALANCE, User } from '../../../common/user' import { PrivateUser, STARTING_BALANCE, User } from '../../../common/user'
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
const serviceAccount = require('../../../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function main() { async function main() {

View File

@ -1,16 +1,11 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('james')
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
// const serviceAccount = require('../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
const serviceAccount = require('../../../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-820891bb87.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function makeContractsPublic() { async function makeContractsPublic() {

View File

@ -1,18 +1,14 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('james')
import { Bet } from '../../../common/bet' import { Bet } from '../../../common/bet'
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
type DocRef = admin.firestore.DocumentReference type DocRef = admin.firestore.DocumentReference
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
const serviceAccount = require('../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-820891bb87.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function migrateBet(contractRef: DocRef, bet: Bet) { async function migrateBet(contractRef: DocRef, bet: Bet) {

View File

@ -1,6 +1,9 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('stephenDev')
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
import { Bet } from '../../../common/bet' import { Bet } from '../../../common/bet'
import { calculateShares, getProbability } from '../../../common/calculate' import { calculateShares, getProbability } from '../../../common/calculate'
@ -9,15 +12,6 @@ import { User } from '../../../common/user'
type DocRef = admin.firestore.DocumentReference type DocRef = admin.firestore.DocumentReference
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
// const serviceAccount = require('../../../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
const serviceAccount = require('../../../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-351a65eca3.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function recalculateContract( async function recalculateContract(

View File

@ -1,19 +1,14 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('james')
import { Bet } from '../../../common/bet' import { Bet } from '../../../common/bet'
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
type DocRef = admin.firestore.DocumentReference type DocRef = admin.firestore.DocumentReference
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
// const serviceAccount = require('../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
const serviceAccount = require('../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-820891bb87.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function recalculateContract(contractRef: DocRef, contract: Contract) { async function recalculateContract(contractRef: DocRef, contract: Contract) {

View File

@ -1,19 +1,12 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin('stephenDev')
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
import { getValues } from '../utils' import { getValues } from '../utils'
// Generate your own private key, and set the path below:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
// James:
const serviceAccount = require('../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-820891bb87.json')
// Stephen:
// const serviceAccount = require('../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore() const firestore = admin.firestore()
async function renameUserContracts( async function renameUserContracts(

View File

@ -0,0 +1,32 @@
import * as admin from 'firebase-admin'
// Generate your own private key, and set the path below:
// Prod:
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk
// Dev:
// https://console.firebase.google.com/u/0/project/dev-mantic-markets/settings/serviceaccounts/adminsdk
const pathsToPrivateKey = {
james:
'/Users/jahooma/mantic-markets-firebase-adminsdk-1ep46-820891bb87.json',
jamesDev:
'/Users/jahooma/dev-mantic-markets-firebase-adminsdk-sir5m-f38cdbee37.json',
stephen:
'../../../../../../Downloads/mantic-markets-firebase-adminsdk-1ep46-351a65eca3.json',
stephenDev:
'../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json',
}
export const initAdmin = (who: keyof typeof pathsToPrivateKey) => {
const serviceAccount = require(pathsToPrivateKey[who])
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
}
// Then:
// yarn watch (or yarn build)
// firebase use dev (or firebase use prod)
// Run script:
// node lib/functions/src/scripts/update-contract-tags.js

View File

@ -1,22 +1,15 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import * as _ from 'lodash' import * as _ from 'lodash'
// Generate your own private key, and set the path below: import { initAdmin } from './script-init'
// https://console.firebase.google.com/u/0/project/mantic-markets/settings/serviceaccounts/adminsdk initAdmin('jamesDev')
// James:
const serviceAccount = require('/Users/jahooma/mantic-markets-firebase-adminsdk-1ep46-820891bb87.json')
// Stephen:
// const serviceAccount = require('../../../../Downloads/dev-mantic-markets-firebase-adminsdk-sir5m-b2d27f8970.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
const firestore = admin.firestore()
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
import { parseTags } from '../../../common/util/parse' import { parseTags } from '../../../common/util/parse'
import { getValues } from '../utils' import { getValues } from '../utils'
async function updateContractTags() { async function updateContractTags() {
const firestore = admin.firestore()
console.log('Updating contracts tags') console.log('Updating contracts tags')
const contracts = await getValues<Contract>(firestore.collection('contracts')) const contracts = await getValues<Contract>(firestore.collection('contracts'))
@ -48,4 +41,6 @@ async function updateContractTags() {
} }
} }
if (require.main === module) updateContractTags().then(() => process.exit()) if (require.main === module) {
updateContractTags().then(() => process.exit())
}

View File

@ -1,6 +1,6 @@
import clsx from 'clsx' import clsx from 'clsx'
import { useUser } from '../hooks/use-user' import { useUser } from '../hooks/use-user'
import { formatMoney } from '../lib/util/format' import { formatMoney } from '../../common/util/format'
import { AddFundsButton } from './add-funds-button' import { AddFundsButton } from './add-funds-button'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Row } from './layout/row' import { Row } from './layout/row'

View File

@ -11,7 +11,7 @@ import {
formatMoney, formatMoney,
formatPercent, formatPercent,
formatWithCommas, formatWithCommas,
} from '../lib/util/format' } from '../../common/util/format'
import { Title } from './title' import { Title } from './title'
import { import {
getProbability, getProbability,

View File

@ -11,7 +11,7 @@ import {
formatMoney, formatMoney,
formatPercent, formatPercent,
formatWithCommas, formatWithCommas,
} from '../lib/util/format' } from '../../common/util/format'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Spacer } from './layout/spacer' import { Spacer } from './layout/spacer'
import { import {

View File

@ -1,7 +1,7 @@
import clsx from 'clsx' import clsx from 'clsx'
import Link from 'next/link' import Link from 'next/link'
import { Row } from '../components/layout/row' import { Row } from '../components/layout/row'
import { formatMoney } from '../lib/util/format' import { formatMoney } from '../../common/util/format'
import { UserLink } from './user-page' import { UserLink } from './user-page'
import { import {
Contract, Contract,
@ -74,7 +74,7 @@ export function ResolutionOrChance(props: {
const resolutionText = { const resolutionText = {
YES: 'YES', YES: 'YES',
NO: 'NO', NO: 'NO',
MKT: 'MKT', MKT: probPercent,
CANCEL: 'N/A', CANCEL: 'N/A',
'': '', '': '',
}[resolution || ''] }[resolution || '']
@ -142,12 +142,9 @@ export function AbbrContractDetails(props: {
export function ContractDetails(props: { contract: Contract }) { export function ContractDetails(props: { contract: Contract }) {
const { contract } = props const { contract } = props
const { question, description, closeTime, creatorName, creatorUsername } = const { closeTime, creatorName, creatorUsername } = contract
contract
const { truePool, createdDate, resolvedDate } = contractMetrics(contract) const { truePool, createdDate, resolvedDate } = contractMetrics(contract)
const tags = parseTags(`${question} ${description}`).map((tag) => `#${tag}`)
return ( return (
<Col className="text-sm text-gray-500 gap-2 sm:flex-row sm:flex-wrap"> <Col className="text-sm text-gray-500 gap-2 sm:flex-row sm:flex-wrap">
<Row className="gap-2 flex-wrap"> <Row className="gap-2 flex-wrap">
@ -199,10 +196,10 @@ export function ContractDetails(props: { contract: Contract }) {
// String version of the above, to send to the OpenGraph image generator // String version of the above, to send to the OpenGraph image generator
export function contractTextDetails(contract: Contract) { export function contractTextDetails(contract: Contract) {
const { question, description, closeTime } = contract const { closeTime, tags } = contract
const { truePool, createdDate, resolvedDate } = contractMetrics(contract) const { truePool, createdDate, resolvedDate } = contractMetrics(contract)
const tags = parseTags(`${question} ${description}`).map((tag) => `#${tag}`) const hashtags = tags.map((tag) => `#${tag}`)
return ( return (
`${resolvedDate ? `${createdDate} - ${resolvedDate}` : createdDate}` + `${resolvedDate ? `${createdDate} - ${resolvedDate}` : createdDate}` +
@ -212,6 +209,6 @@ export function contractTextDetails(contract: Contract) {
).format('MMM D, h:mma')}` ).format('MMM D, h:mma')}`
: '') + : '') +
`${formatMoney(truePool)} pool` + `${formatMoney(truePool)} pool` +
(tags.length > 0 ? `${tags.join(' ')}` : '') (hashtags.length > 0 ? `${hashtags.join(' ')}` : '')
) )
} }

View File

@ -27,7 +27,7 @@ import { Linkify } from './linkify'
import { Row } from './layout/row' import { Row } from './layout/row'
import { createComment } from '../lib/firebase/comments' import { createComment } from '../lib/firebase/comments'
import { useComments } from '../hooks/use-comments' import { useComments } from '../hooks/use-comments'
import { formatMoney } from '../lib/util/format' import { formatMoney } from '../../common/util/format'
import { ResolutionOrChance } from './contract-card' import { ResolutionOrChance } from './contract-card'
import { SiteLink } from './site-link' import { SiteLink } from './site-link'
import { Col } from './layout/col' import { Col } from './layout/col'
@ -160,7 +160,9 @@ export function ContractDescription(props: {
setEditing(false) setEditing(false)
const newDescription = `${contract.description}\n\n${description}`.trim() const newDescription = `${contract.description}\n\n${description}`.trim()
const tags = parseTags(`${contract.tags.join(' ')} ${newDescription}`) const tags = parseTags(
`${newDescription} ${contract.tags.map((tag) => `#${tag}`).join(' ')}`
)
const lowercaseTags = tags.map((tag) => tag.toLowerCase()) const lowercaseTags = tags.map((tag) => tag.toLowerCase())
await updateContract(contract.id, { await updateContract(contract.id, {
description: newDescription, description: newDescription,
@ -685,10 +687,7 @@ export function ContractFeed(props: {
))} ))}
</ul> </ul>
{tradingAllowed(contract) && ( {tradingAllowed(contract) && (
<BetRow <BetRow contract={contract} className={clsx('mb-2', betRowClassName)} />
contract={contract}
className={clsx('-mt-4', betRowClassName)}
/>
)} )}
</div> </div>
) )

View File

@ -3,6 +3,7 @@ import {
Contract, Contract,
deleteContract, deleteContract,
contractPath, contractPath,
tradingAllowed,
} from '../lib/firebase/contracts' } from '../lib/firebase/contracts'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Spacer } from './layout/spacer' import { Spacer } from './layout/spacer'
@ -58,11 +59,13 @@ export const ContractOverview = (props: {
large large
/> />
<BetRow {tradingAllowed(contract) && (
contract={contract} <BetRow
className="md:hidden" contract={contract}
labelClassName="hidden" className="md:hidden"
/> labelClassName="hidden"
/>
)}
</Row> </Row>
<ContractDetails contract={contract} /> <ContractDetails contract={contract} />

View File

@ -11,7 +11,6 @@ import {
import { User } from '../lib/firebase/users' import { User } from '../lib/firebase/users'
import { Col } from './layout/col' import { Col } from './layout/col'
import { SiteLink } from './site-link' import { SiteLink } from './site-link'
import { parseTags } from '../../common/util/parse'
import { ContractCard } from './contract-card' import { ContractCard } from './contract-card'
import { Sort, useQueryAndSortParams } from '../hooks/use-sort-and-query-params' import { Sort, useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
@ -122,12 +121,13 @@ function CreatorContractsGrid(props: { contracts: Contract[] }) {
function TagContractsGrid(props: { contracts: Contract[] }) { function TagContractsGrid(props: { contracts: Contract[] }) {
const { contracts } = props const { contracts } = props
const contractTags = _.flatMap(contracts, (contract) => const contractTags = _.flatMap(contracts, (contract) => {
parseTags(contract.question + ' ' + contract.description).map((tag) => ({ const { tags } = contract
return tags.map((tag) => ({
tag, tag,
contract, contract,
})) }))
) })
const groupedByTag = _.groupBy(contractTags, ({ tag }) => tag) const groupedByTag = _.groupBy(contractTags, ({ tag }) => tag)
const byTag = _.mapValues(groupedByTag, (contractTags) => const byTag = _.mapValues(groupedByTag, (contractTags) =>
contractTags.map(({ contract }) => contract) contractTags.map(({ contract }) => contract)
@ -210,7 +210,8 @@ export function SearchableGrid(props: {
check(c.question) || check(c.question) ||
check(c.description) || check(c.description) ||
check(c.creatorName) || check(c.creatorName) ||
check(c.creatorUsername) check(c.creatorUsername) ||
check(c.lowercaseTags.map((tag) => `#${tag}`).join(' '))
) )
if (sort === 'newest' || sort === 'all') { if (sort === 'newest' || sort === 'all') {

View File

@ -4,7 +4,7 @@ import { useState } from 'react'
import { parseWordsAsTags } from '../../common/util/parse' import { parseWordsAsTags } from '../../common/util/parse'
import { createFold } from '../lib/firebase/api-call' import { createFold } from '../lib/firebase/api-call'
import { foldPath } from '../lib/firebase/folds' import { foldPath } from '../lib/firebase/folds'
import { toCamelCase } from '../lib/util/format' import { toCamelCase } from '../../common/util/format'
import { ConfirmationButton } from './confirmation-button' import { ConfirmationButton } from './confirmation-button'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Spacer } from './layout/spacer' import { Spacer } from './layout/spacer'

View File

@ -6,7 +6,7 @@ import { PencilIcon } from '@heroicons/react/outline'
import { Fold } from '../../common/fold' import { Fold } from '../../common/fold'
import { parseWordsAsTags } from '../../common/util/parse' import { parseWordsAsTags } from '../../common/util/parse'
import { deleteFold, updateFold } from '../lib/firebase/folds' import { deleteFold, updateFold } from '../lib/firebase/folds'
import { toCamelCase } from '../lib/util/format' import { toCamelCase } from '../../common/util/format'
import { Spacer } from './layout/spacer' import { Spacer } from './layout/spacer'
import { TagsList } from './tags-list' import { TagsList } from './tags-list'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'

View File

@ -5,7 +5,7 @@ export function OutcomeLabel(props: {
if (outcome === 'YES') return <YesLabel /> if (outcome === 'YES') return <YesLabel />
if (outcome === 'NO') return <NoLabel /> if (outcome === 'NO') return <NoLabel />
if (outcome === 'MKT') return <MarketLabel /> if (outcome === 'MKT') return <ProbLabel />
return <CancelLabel /> return <CancelLabel />
} }
@ -24,3 +24,7 @@ export function CancelLabel() {
export function MarketLabel() { export function MarketLabel() {
return <span className="text-blue-400">MKT</span> return <span className="text-blue-400">MKT</span>
} }
export function ProbLabel() {
return <span className="text-blue-400">PROB</span>
}

View File

@ -0,0 +1,36 @@
import { Row } from './layout/row'
export function ProbabilitySelector(props: {
probabilityInt: number
setProbabilityInt: (p: number) => void
isSubmitting?: boolean
}) {
const { probabilityInt, setProbabilityInt, isSubmitting } = props
return (
<Row className="items-center gap-2">
<label className="input-group input-group-lg w-fit text-lg">
<input
type="number"
value={probabilityInt}
className="input input-bordered input-md text-lg"
disabled={isSubmitting}
min={1}
max={99}
onChange={(e) =>
setProbabilityInt(parseInt(e.target.value.substring(0, 2)))
}
/>
<span>%</span>
</label>
<input
type="range"
className="range range-primary"
min={1}
max={99}
value={probabilityInt}
onChange={(e) => setProbabilityInt(parseInt(e.target.value))}
/>
</Row>
)
}

View File

@ -1,5 +1,5 @@
import { firebaseLogout, User } from '../lib/firebase/users' import { firebaseLogout, User } from '../lib/firebase/users'
import { formatMoney } from '../lib/util/format' import { formatMoney } from '../../common/util/format'
import { Avatar } from './avatar' import { Avatar } from './avatar'
import { Col } from './layout/col' import { Col } from './layout/col'
import { MenuButton } from './menu' import { MenuButton } from './menu'

View File

@ -9,6 +9,8 @@ import { YesNoCancelSelector } from './yes-no-selector'
import { Spacer } from './layout/spacer' import { Spacer } from './layout/spacer'
import { ConfirmationButton as ConfirmationButton } from './confirmation-button' import { ConfirmationButton as ConfirmationButton } from './confirmation-button'
import { resolveMarket } from '../lib/firebase/api-call' import { resolveMarket } from '../lib/firebase/api-call'
import { ProbabilitySelector } from './probability-selector'
import { getProbability } from '../../common/calculate'
export function ResolutionPanel(props: { export function ResolutionPanel(props: {
creator: User creator: User
@ -26,6 +28,8 @@ export function ResolutionPanel(props: {
'YES' | 'NO' | 'MKT' | 'CANCEL' | undefined 'YES' | 'NO' | 'MKT' | 'CANCEL' | undefined
>() >()
const [prob, setProb] = useState(getProbability(contract.totalShares) * 100)
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
const [error, setError] = useState<string | undefined>(undefined) const [error, setError] = useState<string | undefined>(undefined)
@ -35,6 +39,7 @@ export function ResolutionPanel(props: {
const result = await resolveMarket({ const result = await resolveMarket({
outcome, outcome,
contractId: contract.id, contractId: contract.id,
probabilityInt: prob,
}).then((r) => r.data as any) }).then((r) => r.data as any)
console.log('resolved', outcome, 'result:', result) console.log('resolved', outcome, 'result:', result)
@ -82,8 +87,8 @@ export function ResolutionPanel(props: {
<>The pool will be returned to traders with no fees.</> <>The pool will be returned to traders with no fees.</>
) : outcome === 'MKT' ? ( ) : outcome === 'MKT' ? (
<> <>
Traders will be paid out at the current implied probability. You Traders will be paid out at the probability you specify. You earn 1%
earn 1% of the pool. of the pool.
</> </>
) : ( ) : (
<>Resolving this market will immediately pay out traders.</> <>Resolving this market will immediately pay out traders.</>
@ -113,7 +118,20 @@ export function ResolutionPanel(props: {
}} }}
onSubmit={resolve} onSubmit={resolve}
> >
<p>Are you sure you want to resolve this market?</p> {outcome === 'MKT' ? (
<>
<p className="mb-4">
What probability would you like to resolve the market to?
</p>
<ProbabilitySelector
probabilityInt={Math.round(prob)}
setProbabilityInt={setProb}
/>
</>
) : (
<p>Are you sure you want to resolve this market?</p>
)}
</ConfirmationButton> </ConfirmationButton>
</Col> </Col>
) )

View File

@ -1,6 +1,6 @@
import clsx from 'clsx' import clsx from 'clsx'
import React from 'react' import React from 'react'
import { formatMoney } from '../lib/util/format' import { formatMoney } from '../../common/util/format'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Row } from './layout/row' import { Row } from './layout/row'
@ -78,7 +78,7 @@ export function YesNoCancelSelector(props: {
onClick={() => onSelect('MKT')} onClick={() => onSelect('MKT')}
className={clsx(btnClassName, 'btn-sm')} className={clsx(btnClassName, 'btn-sm')}
> >
MKT PROB
</Button> </Button>
<Button <Button

View File

@ -5,6 +5,7 @@ import {
listenForContracts, listenForContracts,
listenForHotContracts, listenForHotContracts,
} from '../lib/firebase/contracts' } from '../lib/firebase/contracts'
import { listenForTaggedContracts } from '../lib/firebase/folds'
export const useContracts = () => { export const useContracts = () => {
const [contracts, setContracts] = useState<Contract[] | undefined>() const [contracts, setContracts] = useState<Contract[] | undefined>()
@ -16,6 +17,21 @@ export const useContracts = () => {
return contracts return contracts
} }
export const useTaggedContracts = (tags: string[] | undefined) => {
const [contracts, setContracts] = useState<Contract[] | undefined>(
tags && tags.length === 0 ? [] : undefined
)
const tagsKey = tags?.map((tag) => tag.toLowerCase()).join(',') ?? ''
useEffect(() => {
if (!tags || tags.length === 0) return
return listenForTaggedContracts(tags, setContracts)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tagsKey])
return contracts
}
export const useHotContracts = () => { export const useHotContracts = () => {
const [hotContracts, setHotContracts] = useState<Contract[] | undefined>() const [hotContracts, setHotContracts] = useState<Contract[] | undefined>()

View File

@ -17,7 +17,7 @@ import {
import _ from 'lodash' import _ from 'lodash'
import { app } from './init' import { app } from './init'
import { getValues, listenForValues } from './utils' import { getValues, listenForValue, listenForValues } from './utils'
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
import { getProbability } from '../../../common/calculate' import { getProbability } from '../../../common/calculate'
import { createRNG, shuffle } from '../../../common/util/random' import { createRNG, shuffle } from '../../../common/util/random'
@ -35,10 +35,11 @@ export function contractMetrics(contract: Contract) {
createdTime, createdTime,
resolutionTime, resolutionTime,
isResolved, isResolved,
resolutionProbability,
} = contract } = contract
const truePool = pool.YES + pool.NO const truePool = pool.YES + pool.NO
const prob = getProbability(totalShares) const prob = resolutionProbability ?? getProbability(totalShares)
const probPercent = Math.round(prob * 100) + '%' const probPercent = Math.round(prob * 100) + '%'
const startProb = getProbability(phantomShares) const startProb = getProbability(phantomShares)
@ -125,9 +126,7 @@ export function listenForContract(
setContract: (contract: Contract | null) => void setContract: (contract: Contract | null) => void
) { ) {
const contractRef = doc(contractCollection, contractId) const contractRef = doc(contractCollection, contractId)
return onSnapshot(contractRef, (contractSnap) => { return listenForValue<Contract>(contractRef, setContract)
setContract((contractSnap.data() ?? null) as Contract | null)
})
} }
function chooseRandomSubset(contracts: Contract[], count: number) { function chooseRandomSubset(contracts: Contract[], count: number) {

View File

@ -45,6 +45,17 @@ export async function getFoldBySlug(slug: string) {
return folds.length === 0 ? null : folds[0] return folds.length === 0 ? null : folds[0]
} }
function contractsByTagsQuery(tags: string[]) {
return query(
contractCollection,
where(
'lowercaseTags',
'array-contains-any',
tags.map((tag) => tag.toLowerCase())
)
)
}
export async function getFoldContracts(fold: Fold) { export async function getFoldContracts(fold: Fold) {
const { const {
tags, tags,
@ -56,18 +67,7 @@ export async function getFoldContracts(fold: Fold) {
const [tagsContracts, includedContracts] = await Promise.all([ const [tagsContracts, includedContracts] = await Promise.all([
// TODO: if tags.length > 10, execute multiple parallel queries // TODO: if tags.length > 10, execute multiple parallel queries
tags.length > 0 tags.length > 0 ? getValues<Contract>(contractsByTagsQuery(tags)) : [],
? getValues<Contract>(
query(
contractCollection,
where(
'lowercaseTags',
'array-contains-any',
tags.map((tag) => tag.toLowerCase())
)
)
)
: [],
// TODO: if contractIds.length > 10, execute multiple parallel queries // TODO: if contractIds.length > 10, execute multiple parallel queries
contractIds.length > 0 contractIds.length > 0
@ -97,6 +97,13 @@ export async function getFoldContracts(fold: Fold) {
return [...approvedContracts, ...includedContracts] return [...approvedContracts, ...includedContracts]
} }
export function listenForTaggedContracts(
tags: string[],
setContracts: (contracts: Contract[]) => void
) {
return listenForValues<Contract>(contractsByTagsQuery(tags), setContracts)
}
export function listenForFold( export function listenForFold(
foldId: string, foldId: string,
setFold: (fold: Fold | null) => void setFold: (fold: Fold | null) => void

View File

@ -27,13 +27,18 @@ export function scoreTraders(contracts: Contract[], bets: Bet[][]) {
} }
function scoreUsersByContract(contract: Contract, bets: Bet[]) { function scoreUsersByContract(contract: Contract, bets: Bet[]) {
const { resolution } = contract const { resolution, resolutionProbability } = contract
const [closedBets, openBets] = _.partition( const [closedBets, openBets] = _.partition(
bets, bets,
(bet) => bet.isSold || bet.sale (bet) => bet.isSold || bet.sale
) )
const resolvePayouts = getPayouts(resolution ?? 'MKT', contract, openBets) const resolvePayouts = getPayouts(
resolution ?? 'MKT',
contract,
openBets,
resolutionProbability
)
const salePayouts = closedBets.map((bet) => { const salePayouts = closedBets.map((bet) => {
const { userId, sale } = bet const { userId, sale } = bet

View File

@ -23,6 +23,8 @@ export function listenForValue<T>(
setValue: (value: T | null) => void setValue: (value: T | null) => void
) { ) {
return onSnapshot(docRef, (snapshot) => { return onSnapshot(docRef, (snapshot) => {
if (snapshot.metadata.fromCache) return
const value = snapshot.exists() ? (snapshot.data() as T) : null const value = snapshot.exists() ? (snapshot.data() as T) : null
setValue(value) setValue(value)
}) })

View File

@ -8,13 +8,13 @@ import { Spacer } from '../components/layout/spacer'
import { useUser } from '../hooks/use-user' import { useUser } from '../hooks/use-user'
import { Contract, contractPath } from '../lib/firebase/contracts' import { Contract, contractPath } from '../lib/firebase/contracts'
import { createContract } from '../lib/firebase/api-call' import { createContract } from '../lib/firebase/api-call'
import { Row } from '../components/layout/row'
import { AmountInput } from '../components/amount-input' import { AmountInput } from '../components/amount-input'
import { MINIMUM_ANTE } from '../../common/antes' import { MINIMUM_ANTE } from '../../common/antes'
import { InfoTooltip } from '../components/info-tooltip' import { InfoTooltip } from '../components/info-tooltip'
import { CREATOR_FEE } from '../../common/fees' import { CREATOR_FEE } from '../../common/fees'
import { Page } from '../components/page' import { Page } from '../components/page'
import { Title } from '../components/title' import { Title } from '../components/title'
import { ProbabilitySelector } from '../components/probability-selector'
export default function Create() { export default function Create() {
const [question, setQuestion] = useState('') const [question, setQuestion] = useState('')
@ -72,8 +72,8 @@ export function NewContract(props: { question: string; tag?: string }) {
const [anteError, setAnteError] = useState<string | undefined>() const [anteError, setAnteError] = useState<string | undefined>()
// By default, close the market a week from today // By default, close the market a week from today
// const weekFromToday = dayjs().add(7, 'day').format('YYYY-MM-DD') const weekFromToday = dayjs().add(7, 'day').format('YYYY-MM-DDT23:59')
const [closeDate, setCloseDate] = useState<undefined | string>(undefined) const [closeDate, setCloseDate] = useState<undefined | string>(weekFromToday)
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
@ -127,30 +127,11 @@ export function NewContract(props: { question: string; tag?: string }) {
<label className="label"> <label className="label">
<span className="mb-1">Initial probability</span> <span className="mb-1">Initial probability</span>
</label> </label>
<Row className="items-center gap-2">
<label className="input-group input-group-lg w-fit text-lg"> <ProbabilitySelector
<input probabilityInt={initialProb}
type="number" setProbabilityInt={setInitialProb}
value={initialProb} />
className="input input-bordered input-md text-lg"
disabled={isSubmitting}
min={1}
max={99}
onChange={(e) =>
setInitialProb(parseInt(e.target.value.substring(0, 2)))
}
/>
<span>%</span>
</label>
<input
type="range"
className="range range-primary"
min={1}
max={99}
value={initialProb}
onChange={(e) => setInitialProb(parseInt(e.target.value))}
/>
</Row>
</div> </div>
<Spacer h={4} /> <Spacer h={4} />
@ -175,15 +156,15 @@ export function NewContract(props: { question: string; tag?: string }) {
<div className="form-control items-start mb-1"> <div className="form-control items-start mb-1">
<label className="label gap-2 mb-1"> <label className="label gap-2 mb-1">
<span>Last trading day</span> <span>Market close</span>
<InfoTooltip text="Trading allowed through 11:59 pm local time on this date." /> <InfoTooltip text="Trading will be halted after this time (local timezone)." />
</label> </label>
<input <input
type="date" type="datetime-local"
className="input input-bordered" className="input input-bordered"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
onChange={(e) => setCloseDate(e.target.value || '')} onChange={(e) => setCloseDate(e.target.value || '')}
min={new Date().toISOString().split('T')[0]} min={Date.now()}
disabled={isSubmitting} disabled={isSubmitting}
value={closeDate} value={closeDate}
/> />

View File

@ -28,12 +28,13 @@ import { useRouter } from 'next/router'
import clsx from 'clsx' import clsx from 'clsx'
import { scoreCreators, scoreTraders } from '../../../lib/firebase/scoring' import { scoreCreators, scoreTraders } from '../../../lib/firebase/scoring'
import { Leaderboard } from '../../../components/leaderboard' import { Leaderboard } from '../../../components/leaderboard'
import { formatMoney, toCamelCase } from '../../../lib/util/format' import { formatMoney, toCamelCase } from '../../../../common/util/format'
import { EditFoldButton } from '../../../components/edit-fold-button' import { EditFoldButton } from '../../../components/edit-fold-button'
import Custom404 from '../../404' import Custom404 from '../../404'
import { FollowFoldButton } from '../../../components/follow-fold-button' import { FollowFoldButton } from '../../../components/follow-fold-button'
import FeedCreate from '../../../components/feed-create' import FeedCreate from '../../../components/feed-create'
import { SEO } from '../../../components/SEO' import { SEO } from '../../../components/SEO'
import { useTaggedContracts } from '../../../hooks/use-contracts'
export async function getStaticProps(props: { params: { slugs: string[] } }) { export async function getStaticProps(props: { params: { slugs: string[] } }) {
const { slugs } = props.params const { slugs } = props.params
@ -104,8 +105,14 @@ async function toUserScores(userScores: { [userId: string]: number }) {
const topUsers = await Promise.all( const topUsers = await Promise.all(
topUserPairs.map(([userId]) => getUser(userId)) topUserPairs.map(([userId]) => getUser(userId))
) )
const topUserScores = topUserPairs.map(([_, score]) => score) const existingPairs = topUserPairs.filter(([id, _]) =>
return [topUsers, topUserScores] as const topUsers.find((user) => user?.id === id)
)
const topExistingUsers = existingPairs.map(
([id]) => topUsers.find((user) => user?.id === id) as User
)
const topUserScores = existingPairs.map(([_, score]) => score)
return [topExistingUsers, topUserScores] as const
} }
export async function getStaticPaths() { export async function getStaticPaths() {
@ -127,8 +134,6 @@ export default function FoldPage(props: {
}) { }) {
const { const {
curator, curator,
contracts,
activeContracts,
activeContractBets, activeContractBets,
activeContractComments, activeContractComments,
topTraders, topTraders,
@ -151,6 +156,16 @@ export default function FoldPage(props: {
const user = useUser() const user = useUser()
const isCurator = user && fold && user.id === fold.curatorId const isCurator = user && fold && user.id === fold.curatorId
const taggedContracts = useTaggedContracts(fold?.tags) ?? props.contracts
const contractsMap = _.fromPairs(
taggedContracts.map((contract) => [contract.id, contract])
)
const contracts = props.contracts.map((contract) => contractsMap[contract.id])
const activeContracts = props.activeContracts.map(
(contract) => contractsMap[contract.id]
)
if (fold === null || !foldSubpages.includes(page) || slugs[2]) { if (fold === null || !foldSubpages.includes(page) || slugs[2]) {
return <Custom404 /> return <Custom404 />
} }

View File

@ -1,12 +1,8 @@
import React from 'react' import React from 'react'
import Router from 'next/router' import Router from 'next/router'
import _ from 'lodash'
import { import { Contract, listAllContracts } from '../lib/firebase/contracts'
Contract,
getClosingSoonContracts,
getHotContracts,
listAllContracts,
} from '../lib/firebase/contracts'
import { Page } from '../components/page' import { Page } from '../components/page'
import { ActivityFeed, findActiveContracts } from './activity' import { ActivityFeed, findActiveContracts } from './activity'
import { import {
@ -19,15 +15,13 @@ import FeedCreate from '../components/feed-create'
import { Spacer } from '../components/layout/spacer' import { Spacer } from '../components/layout/spacer'
import { Col } from '../components/layout/col' import { Col } from '../components/layout/col'
import { useUser } from '../hooks/use-user' import { useUser } from '../hooks/use-user'
import { useContracts } from '../hooks/use-contracts'
export async function getStaticProps() { export async function getStaticProps() {
const [contracts, recentComments, hotContracts, closingSoonContracts] = const [contracts, recentComments] = await Promise.all([
await Promise.all([ listAllContracts().catch((_) => []),
listAllContracts().catch((_) => []), getRecentComments().catch(() => []),
getRecentComments().catch(() => []), ])
getHotContracts().catch(() => []),
getClosingSoonContracts().catch(() => []),
])
const activeContracts = findActiveContracts(contracts, recentComments) const activeContracts = findActiveContracts(contracts, recentComments)
const activeContractBets = await Promise.all( const activeContractBets = await Promise.all(
@ -44,8 +38,6 @@ export async function getStaticProps() {
activeContracts, activeContracts,
activeContractBets, activeContractBets,
activeContractComments, activeContractComments,
hotContracts,
closingSoonContracts,
}, },
revalidate: 60, // regenerate after a minute revalidate: 60, // regenerate after a minute
@ -56,27 +48,18 @@ const Home = (props: {
activeContracts: Contract[] activeContracts: Contract[]
activeContractBets: Bet[][] activeContractBets: Bet[][]
activeContractComments: Comment[][] activeContractComments: Comment[][]
hotContracts: Contract[]
closingSoonContracts: Contract[]
}) => { }) => {
const { const { activeContracts, activeContractBets, activeContractComments } = props
activeContracts,
activeContractBets,
activeContractComments,
// hotContracts,
// closingSoonContracts,
} = props
const user = useUser() const user = useUser()
// const initialActiveContracts = props.activeContracts ?? [] const contracts = useContracts() ?? activeContracts
// const contracts = useContracts() const contractsMap = _.fromPairs(
// const recentComments = useRecentComments() contracts.map((contract) => [contract.id, contract])
// const activeContracts = )
// recentComments && contracts const updatedContracts = activeContracts.map(
// ? findActiveContracts(contracts, recentComments) (contract) => contractsMap[contract.id]
// : initialActiveContracts )
// TODO: get activeContractBets, activeContractComments associated with activeContracts
if (user === null) { if (user === null) {
Router.replace('/') Router.replace('/')
@ -89,17 +72,10 @@ const Home = (props: {
<Col className="max-w-3xl"> <Col className="max-w-3xl">
<FeedCreate user={user ?? undefined} /> <FeedCreate user={user ?? undefined} />
<Spacer h={4} /> <Spacer h={4} />
{/* <HotMarkets contracts={hotContracts?.slice(0, 4) ?? []} />
<Spacer h={4} />
<ClosingSoonMarkets contracts={closingSoonContracts ?? []} />
<Spacer h={10} /> */}
<ActivityFeed <ActivityFeed
contracts={activeContracts ?? []} contracts={updatedContracts}
contractBets={activeContractBets ?? []} contractBets={activeContractBets}
contractComments={activeContractComments ?? []} contractComments={activeContractComments}
/> />
</Col> </Col>
</Col> </Col>

View File

@ -1,9 +1,10 @@
import _ from 'lodash' import _ from 'lodash'
import { Col } from '../components/layout/col' import { Col } from '../components/layout/col'
import { Leaderboard } from '../components/leaderboard' import { Leaderboard } from '../components/leaderboard'
import { Page } from '../components/page' import { Page } from '../components/page'
import { getTopCreators, getTopTraders, User } from '../lib/firebase/users' import { getTopCreators, getTopTraders, User } from '../lib/firebase/users'
import { formatMoney } from '../lib/util/format' import { formatMoney } from '../../common/util/format'
export async function getStaticProps() { export async function getStaticProps() {
const [topTraders, topCreators] = await Promise.all([ const [topTraders, topCreators] = await Promise.all([