Revert "Turn on no unused variables linting, kill dead code (#484)"
This reverts commit 515928a69a
.
This commit is contained in:
parent
4ad04869a1
commit
5beda1ded7
|
@ -8,15 +8,8 @@ module.exports = {
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
varsIgnorePattern: '^_',
|
|
||||||
caughtErrorsIgnorePattern: '^_',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@next/next/no-img-element': 'off',
|
'@next/next/no-img-element': 'off',
|
||||||
'@next/next/no-typos': 'off',
|
'@next/next/no-typos': 'off',
|
||||||
'lodash/import-scope': [2, 'member'],
|
'lodash/import-scope': [2, 'member'],
|
||||||
|
|
|
@ -116,7 +116,7 @@ export function AnswersPanel(props: { contract: FreeResponseContract }) {
|
||||||
{!resolveOption && (
|
{!resolveOption && (
|
||||||
<div className={clsx('flow-root pr-2 md:pr-0')}>
|
<div className={clsx('flow-root pr-2 md:pr-0')}>
|
||||||
<div className={clsx(tradingAllowed(contract) ? '' : '-mb-6')}>
|
<div className={clsx(tradingAllowed(contract) ? '' : '-mb-6')}>
|
||||||
{answerItems.map((item) => (
|
{answerItems.map((item, activityItemIdx) => (
|
||||||
<div key={item.id} className={'relative pb-2'}>
|
<div key={item.id} className={'relative pb-2'}>
|
||||||
<div className="relative flex items-start space-x-3">
|
<div className="relative flex items-start space-x-3">
|
||||||
<OpenAnswer {...item} />
|
<OpenAnswer {...item} />
|
||||||
|
|
|
@ -138,8 +138,9 @@ export function BetsList(props: { user: User; hideBetsBefore?: number }) {
|
||||||
return !hasSoldAll
|
return !hasSoldAll
|
||||||
})
|
})
|
||||||
|
|
||||||
const unsettled = contracts.filter(
|
const [settled, unsettled] = partition(
|
||||||
(c) => !c.isResolved && contractsMetrics[c.id].invested !== 0
|
contracts,
|
||||||
|
(c) => c.isResolved || contractsMetrics[c.id].invested === 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentInvested = sumBy(
|
const currentInvested = sumBy(
|
||||||
|
@ -260,7 +261,7 @@ function ContractBets(props: {
|
||||||
|
|
||||||
const isBinary = outcomeType === 'BINARY'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
|
|
||||||
const { payout, profit, profitPercent } = getContractBetMetrics(
|
const { payout, profit, profitPercent, invested } = getContractBetMetrics(
|
||||||
contract,
|
contract,
|
||||||
bets
|
bets
|
||||||
)
|
)
|
||||||
|
@ -656,6 +657,7 @@ function SellButton(props: { contract: Contract; bet: Bet }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfirmationButton
|
<ConfirmationButton
|
||||||
|
id={`sell-${bet.id}`}
|
||||||
openModalBtn={{
|
openModalBtn={{
|
||||||
className: clsx('btn-sm', isSubmitting && 'btn-disabled loading'),
|
className: clsx('btn-sm', isSubmitting && 'btn-disabled loading'),
|
||||||
label: 'Sell',
|
label: 'Sell',
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { manaToUSD } from '../../../common/util/format'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
|
|
||||||
export function CharityCard(props: { charity: Charity }) {
|
export function CharityCard(props: { charity: Charity }) {
|
||||||
const { slug, photo, preview, id, tags } = props.charity
|
const { name, slug, photo, preview, id, tags } = props.charity
|
||||||
|
|
||||||
const txns = useCharityTxns(id)
|
const txns = useCharityTxns(id)
|
||||||
const raised = sumBy(txns, (txn) => txn.amount)
|
const raised = sumBy(txns, (txn) => txn.amount)
|
||||||
|
|
|
@ -24,12 +24,13 @@ export function ChoicesToggleGroup(props: {
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
className={clsx(className, 'flex flex-row flex-wrap items-center gap-3')}
|
className={clsx(className, 'flex flex-row flex-wrap items-center gap-3')}
|
||||||
value={currentChoice.toString()}
|
value={currentChoice.toString()}
|
||||||
onChange={setChoice}
|
onChange={(str) => null}
|
||||||
>
|
>
|
||||||
{Object.keys(choicesMap).map((choiceKey) => (
|
{Object.keys(choicesMap).map((choiceKey) => (
|
||||||
<RadioGroup.Option
|
<RadioGroup.Option
|
||||||
key={choiceKey}
|
key={choiceKey}
|
||||||
value={choicesMap[choiceKey]}
|
value={choicesMap[choiceKey]}
|
||||||
|
onClick={() => setChoice(choicesMap[choiceKey])}
|
||||||
className={({ active }) =>
|
className={({ active }) =>
|
||||||
clsx(
|
clsx(
|
||||||
active ? 'ring-2 ring-indigo-500 ring-offset-2' : '',
|
active ? 'ring-2 ring-indigo-500 ring-offset-2' : '',
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Modal } from './layout/modal'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
|
|
||||||
export function ConfirmationButton(props: {
|
export function ConfirmationButton(props: {
|
||||||
|
id: string
|
||||||
openModalBtn: {
|
openModalBtn: {
|
||||||
label: string
|
label: string
|
||||||
icon?: JSX.Element
|
icon?: JSX.Element
|
||||||
|
@ -21,7 +22,7 @@ export function ConfirmationButton(props: {
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
const { openModalBtn, cancelBtn, submitBtn, onSubmit, children } = props
|
const { id, openModalBtn, cancelBtn, submitBtn, onSubmit, children } = props
|
||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ export function ResolveConfirmationButton(props: {
|
||||||
props
|
props
|
||||||
return (
|
return (
|
||||||
<ConfirmationButton
|
<ConfirmationButton
|
||||||
|
id="resolution-modal"
|
||||||
openModalBtn={{
|
openModalBtn={{
|
||||||
className: clsx(
|
className: clsx(
|
||||||
'border-none self-start',
|
'border-none self-start',
|
||||||
|
|
|
@ -140,7 +140,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const textColor = `text-${getColor(contract)}`
|
const textColor = `text-${getColor(contract, previewProb)}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col
|
<Col
|
||||||
|
@ -223,7 +223,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
|
|
||||||
export function ProbBar(props: { contract: Contract; previewProb?: number }) {
|
export function ProbBar(props: { contract: Contract; previewProb?: number }) {
|
||||||
const { contract, previewProb } = props
|
const { contract, previewProb } = props
|
||||||
const color = getColor(contract)
|
const color = getColor(contract, previewProb)
|
||||||
const prob = previewProb ?? getProb(contract)
|
const prob = previewProb ?? getProb(contract)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -257,7 +257,7 @@ function QuickOutcomeView(props: {
|
||||||
// If there's a preview prob, display that instead of the current prob
|
// If there's a preview prob, display that instead of the current prob
|
||||||
const override =
|
const override =
|
||||||
previewProb === undefined ? undefined : formatPercent(previewProb)
|
previewProb === undefined ? undefined : formatPercent(previewProb)
|
||||||
const textColor = `text-${getColor(contract)}`
|
const textColor = `text-${getColor(contract, previewProb)}`
|
||||||
|
|
||||||
let display: string | undefined
|
let display: string | undefined
|
||||||
switch (outcomeType) {
|
switch (outcomeType) {
|
||||||
|
@ -306,7 +306,7 @@ function getNumericScale(contract: NumericContract) {
|
||||||
return (ev - min) / (max - min)
|
return (ev - min) / (max - min)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getColor(contract: Contract) {
|
export function getColor(contract: Contract, previewProb?: number) {
|
||||||
// TODO: Try injecting a gradient here
|
// TODO: Try injecting a gradient here
|
||||||
// return 'primary'
|
// return 'primary'
|
||||||
const { resolution } = contract
|
const { resolution } = contract
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import { SparklesIcon } from '@heroicons/react/solid'
|
import { sample } from 'lodash'
|
||||||
|
import { SparklesIcon, XIcon } from '@heroicons/react/solid'
|
||||||
|
import { Avatar } from './avatar'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { Spacer } from './layout/spacer'
|
import { Spacer } from './layout/spacer'
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
import { NewContract } from '../pages/create'
|
||||||
|
import { firebaseLogin, User } from 'web/lib/firebase/users'
|
||||||
import { ContractsGrid } from './contract/contracts-list'
|
import { ContractsGrid } from './contract/contracts-list'
|
||||||
import { Contract } from 'common/contract'
|
import { Contract, MAX_QUESTION_LENGTH } from 'common/contract'
|
||||||
import { Col } from './layout/col'
|
import { Col } from './layout/col'
|
||||||
|
import clsx from 'clsx'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
|
import { ENV_CONFIG } from '../../common/envs/constants'
|
||||||
import { SiteLink } from './site-link'
|
import { SiteLink } from './site-link'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
|
|
||||||
|
@ -61,3 +67,90 @@ export function FeedPromo(props: { hotContracts: Contract[] }) {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function FeedCreate(props: {
|
||||||
|
user?: User
|
||||||
|
tag?: string
|
||||||
|
placeholder?: string
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { user, tag, className } = props
|
||||||
|
const [question, setQuestion] = useState('')
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false)
|
||||||
|
const inputRef = useRef<HTMLTextAreaElement | null>()
|
||||||
|
|
||||||
|
// Rotate through a new placeholder each day
|
||||||
|
// Easter egg idea: click your own name to shuffle the placeholder
|
||||||
|
// const daysSinceEpoch = Math.floor(Date.now() / 1000 / 60 / 60 / 24)
|
||||||
|
|
||||||
|
// Take care not to produce a different placeholder on the server and client
|
||||||
|
const [defaultPlaceholder, setDefaultPlaceholder] = useState('')
|
||||||
|
useEffect(() => {
|
||||||
|
setDefaultPlaceholder(`e.g. ${sample(ENV_CONFIG.newQuestionPlaceholders)}`)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const placeholder = props.placeholder ?? defaultPlaceholder
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'w-full cursor-text rounded bg-white p-4 shadow-md',
|
||||||
|
isExpanded ? 'ring-2 ring-indigo-300' : '',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
!isExpanded && inputRef.current?.focus()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="relative flex items-start space-x-3">
|
||||||
|
<Avatar username={user?.username} avatarUrl={user?.avatarUrl} noLink />
|
||||||
|
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<Row className="justify-between">
|
||||||
|
<p className="my-0.5 text-sm">Ask a question... </p>
|
||||||
|
{isExpanded && (
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-circle btn-ghost rounded"
|
||||||
|
onClick={() => setIsExpanded(false)}
|
||||||
|
>
|
||||||
|
<XIcon
|
||||||
|
className="mx-auto h-6 w-6 text-gray-500"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
<textarea
|
||||||
|
ref={inputRef as any}
|
||||||
|
className={clsx(
|
||||||
|
'w-full resize-none appearance-none border-transparent bg-transparent p-0 text-indigo-700 placeholder:text-gray-400 focus:border-transparent focus:ring-transparent',
|
||||||
|
question && 'text-lg sm:text-xl',
|
||||||
|
!question && 'text-base sm:text-lg'
|
||||||
|
)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={question}
|
||||||
|
rows={question.length > 68 ? 4 : 2}
|
||||||
|
maxLength={MAX_QUESTION_LENGTH}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
onChange={(e) => setQuestion(e.target.value.replace('\n', ''))}
|
||||||
|
onFocus={() => setIsExpanded(true)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hide component instead of deleting, so edits to NewContract don't get lost */}
|
||||||
|
<div className={isExpanded ? '' : 'hidden'}>
|
||||||
|
<NewContract question={question} tag={tag} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Show a fake "Create Market" button, which gets replaced with the NewContract one*/}
|
||||||
|
{!isExpanded && (
|
||||||
|
<div className="flex justify-end sm:-mt-4">
|
||||||
|
<button className="btn btn-sm capitalize" disabled>
|
||||||
|
Create Market
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Answer } from 'common/answer'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { Comment } from 'common/comment'
|
import { Comment } from 'common/comment'
|
||||||
import { formatPercent } from 'common/util/format'
|
import { formatPercent } from 'common/util/format'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import { Modal } from 'web/components/layout/modal'
|
import { Modal } from 'web/components/layout/modal'
|
||||||
import { AnswerBetPanel } from 'web/components/answers/answer-bet-panel'
|
import { AnswerBetPanel } from 'web/components/answers/answer-bet-panel'
|
||||||
|
|
|
@ -3,6 +3,7 @@ import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
BanIcon,
|
BanIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
|
DotsVerticalIcon,
|
||||||
LockClosedIcon,
|
LockClosedIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
} from '@heroicons/react/solid'
|
} from '@heroicons/react/solid'
|
||||||
|
@ -273,3 +274,30 @@ function FeedClose(props: { contract: Contract }) {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Should highlight the entire Feed segment
|
||||||
|
function FeedExpand(props: { setExpanded: (expanded: boolean) => void }) {
|
||||||
|
const { setExpanded } = props
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={() => setExpanded(true)}>
|
||||||
|
<div className="relative px-1">
|
||||||
|
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300">
|
||||||
|
<DotsVerticalIcon
|
||||||
|
className="h-5 w-5 text-gray-500"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button onClick={() => setExpanded(true)}>
|
||||||
|
<div className="min-w-0 flex-1 py-1.5">
|
||||||
|
<div className="text-sm text-gray-500 hover:text-gray-700">
|
||||||
|
<span>Show all activity</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ export function CreateFoldButton() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfirmationButton
|
<ConfirmationButton
|
||||||
|
id="create-fold"
|
||||||
openModalBtn={{
|
openModalBtn={{
|
||||||
label: 'New',
|
label: 'New',
|
||||||
icon: <PlusCircleIcon className="mr-2 h-5 w-5" />,
|
icon: <PlusCircleIcon className="mr-2 h-5 w-5" />,
|
||||||
|
|
|
@ -98,7 +98,7 @@ function AddLiquidityPanel(props: { contract: CPMMContract }) {
|
||||||
setError('Server error')
|
setError('Server error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((_) => setError('Server error'))
|
.catch((e) => setError('Server error'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -162,7 +162,7 @@ function WithdrawLiquidityPanel(props: {
|
||||||
const { contract, lpShares } = props
|
const { contract, lpShares } = props
|
||||||
const { YES: yesShares, NO: noShares } = lpShares
|
const { YES: yesShares, NO: noShares } = lpShares
|
||||||
|
|
||||||
const [_error, setError] = useState<string | undefined>(undefined)
|
const [error, setError] = useState<string | undefined>(undefined)
|
||||||
const [isSuccess, setIsSuccess] = useState(false)
|
const [isSuccess, setIsSuccess] = useState(false)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
|
@ -171,12 +171,12 @@ function WithdrawLiquidityPanel(props: {
|
||||||
setIsSuccess(false)
|
setIsSuccess(false)
|
||||||
|
|
||||||
withdrawLiquidity({ contractId: contract.id })
|
withdrawLiquidity({ contractId: contract.id })
|
||||||
.then((_) => {
|
.then((r) => {
|
||||||
setIsSuccess(true)
|
setIsSuccess(true)
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
.catch((_) => setError('Server error'))
|
.catch((e) => setError('Server error'))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSuccess)
|
if (isSuccess)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { User } from 'web/lib/firebase/users'
|
import { firebaseLogout, User } from 'web/lib/firebase/users'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { Avatar } from '../avatar'
|
import { Avatar } from '../avatar'
|
||||||
|
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
|
||||||
|
|
||||||
export function ProfileSummary(props: { user: User }) {
|
export function ProfileSummary(props: { user: User }) {
|
||||||
const { user } = props
|
const { user } = props
|
||||||
|
|
|
@ -7,12 +7,15 @@ import {
|
||||||
CashIcon,
|
CashIcon,
|
||||||
HeartIcon,
|
HeartIcon,
|
||||||
PresentationChartLineIcon,
|
PresentationChartLineIcon,
|
||||||
|
ChatAltIcon,
|
||||||
SparklesIcon,
|
SparklesIcon,
|
||||||
NewspaperIcon,
|
NewspaperIcon,
|
||||||
} from '@heroicons/react/outline'
|
} from '@heroicons/react/outline'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import { sortBy } from 'lodash'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import { useFollowedFolds } from 'web/hooks/use-fold'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { firebaseLogin, firebaseLogout, User } from 'web/lib/firebase/users'
|
import { firebaseLogin, firebaseLogout, User } from 'web/lib/firebase/users'
|
||||||
import { ManifoldLogo } from './manifold-logo'
|
import { ManifoldLogo } from './manifold-logo'
|
||||||
|
@ -176,6 +179,8 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
let folds = useFollowedFolds(user) || []
|
||||||
|
folds = sortBy(folds, 'followCount').reverse()
|
||||||
const mustWaitForFreeMarketStatus = useHasCreatedContractToday(user)
|
const mustWaitForFreeMarketStatus = useHasCreatedContractToday(user)
|
||||||
const navigationOptions =
|
const navigationOptions =
|
||||||
user === null
|
user === null
|
||||||
|
|
|
@ -53,6 +53,7 @@ function NumericBuyPanel(props: {
|
||||||
|
|
||||||
const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
|
const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
|
||||||
|
|
||||||
|
const [valueError, setValueError] = useState<string | undefined>()
|
||||||
const [error, setError] = useState<string | undefined>()
|
const [error, setError] = useState<string | undefined>()
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [wasSubmitted, setWasSubmitted] = useState(false)
|
const [wasSubmitted, setWasSubmitted] = useState(false)
|
||||||
|
|
|
@ -6,11 +6,12 @@ import { Toaster } from 'react-hot-toast'
|
||||||
|
|
||||||
export function Page(props: {
|
export function Page(props: {
|
||||||
margin?: boolean
|
margin?: boolean
|
||||||
|
assertUser?: 'signed-in' | 'signed-out'
|
||||||
rightSidebar?: ReactNode
|
rightSidebar?: ReactNode
|
||||||
suspend?: boolean
|
suspend?: boolean
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
}) {
|
}) {
|
||||||
const { margin, children, rightSidebar, suspend } = props
|
const { margin, assertUser, children, rightSidebar, suspend } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { isEqual, sortBy } from 'lodash'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Fold } from 'common/fold'
|
import { Fold } from 'common/fold'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import {
|
import {
|
||||||
|
listAllFolds,
|
||||||
listenForFold,
|
listenForFold,
|
||||||
listenForFolds,
|
listenForFolds,
|
||||||
listenForFoldsWithTags,
|
listenForFoldsWithTags,
|
||||||
|
@ -78,3 +80,38 @@ export const useFollowedFoldIds = (user: User | null | undefined) => {
|
||||||
|
|
||||||
return followedFoldIds
|
return followedFoldIds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We also cache followedFolds directly in JSON.
|
||||||
|
// TODO: Extract out localStorage caches to a utility
|
||||||
|
export const useFollowedFolds = (user: User | null | undefined) => {
|
||||||
|
const [followedFolds, setFollowedFolds] = useState<Fold[] | undefined>()
|
||||||
|
const ids = useFollowedFoldIds(user)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user && ids) {
|
||||||
|
const key = `followed-full-folds-${user.id}`
|
||||||
|
const followedFoldJson = localStorage.getItem(key)
|
||||||
|
if (followedFoldJson) {
|
||||||
|
setFollowedFolds(JSON.parse(followedFoldJson))
|
||||||
|
// Exit early if ids and followedFoldIds have all the same elements.
|
||||||
|
if (
|
||||||
|
isEqual(
|
||||||
|
sortBy(ids),
|
||||||
|
sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, fetch the full contents of all folds
|
||||||
|
listAllFolds().then((folds) => {
|
||||||
|
const followedFolds = folds.filter((fold) => ids.includes(fold.id))
|
||||||
|
setFollowedFolds(followedFolds)
|
||||||
|
localStorage.setItem(key, JSON.stringify(followedFolds))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [user, ids])
|
||||||
|
|
||||||
|
return followedFolds
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { collection, query, where } from 'firebase/firestore'
|
import { collection, query, where } from 'firebase/firestore'
|
||||||
import { Notification } from 'common/notification'
|
import { Notification } from 'common/notification'
|
||||||
import { db } from 'web/lib/firebase/init'
|
import { db } from 'web/lib/firebase/init'
|
||||||
import { listenForValues } from 'web/lib/firebase/utils'
|
import { getValues, listenForValues } from 'web/lib/firebase/utils'
|
||||||
|
|
||||||
function getNotificationsQuery(userId: string, unseenOnly?: boolean) {
|
function getNotificationsQuery(userId: string, unseenOnly?: boolean) {
|
||||||
const notifsCollection = collection(db, `/users/${userId}/notifications`)
|
const notifsCollection = collection(db, `/users/${userId}/notifications`)
|
||||||
|
|
|
@ -37,7 +37,7 @@ export default function Activity() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Page suspend={!!contract}>
|
<Page assertUser="signed-in" suspend={!!contract}>
|
||||||
<Col className="mx-auto w-full max-w-[700px]">
|
<Col className="mx-auto w-full max-w-[700px]">
|
||||||
<CategorySelector category={category} setCategory={setCategory} />
|
<CategorySelector category={category} setCategory={setCategory} />
|
||||||
<Spacer h={1} />
|
<Spacer h={1} />
|
||||||
|
|
|
@ -19,7 +19,6 @@ export default async function handler(
|
||||||
listAllComments(contractId),
|
listAllComments(contractId),
|
||||||
])
|
])
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const bets = allBets.map(({ userId, ...bet }) => bet) as Exclude<
|
const bets = allBets.map(({ userId, ...bet }) => bet) as Exclude<
|
||||||
Bet,
|
Bet,
|
||||||
'userId'
|
'userId'
|
||||||
|
|
|
@ -24,7 +24,6 @@ export default async function handler(
|
||||||
listAllComments(contract.id),
|
listAllComments(contract.id),
|
||||||
])
|
])
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const bets = allBets.map(({ userId, ...bet }) => bet) as Exclude<
|
const bets = allBets.map(({ userId, ...bet }) => bet) as Exclude<
|
||||||
Bet,
|
Bet,
|
||||||
'userId'
|
'userId'
|
||||||
|
|
|
@ -56,8 +56,8 @@ export default function Create() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow user to create a new contract
|
// Allow user to create a new contract
|
||||||
export function NewContract(props: { question: string }) {
|
export function NewContract(props: { question: string; tag?: string }) {
|
||||||
const { question } = props
|
const { question, tag } = props
|
||||||
const creator = useUser()
|
const creator = useUser()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -74,7 +74,7 @@ export function NewContract(props: { question: string }) {
|
||||||
// const [tagText, setTagText] = useState<string>(tag ?? '')
|
// const [tagText, setTagText] = useState<string>(tag ?? '')
|
||||||
// const tags = parseWordsAsTags(tagText)
|
// const tags = parseWordsAsTags(tagText)
|
||||||
|
|
||||||
const [ante, _setAnte] = useState(FIXED_ANTE)
|
const [ante, setAnte] = useState(FIXED_ANTE)
|
||||||
|
|
||||||
const mustWaitForDailyFreeMarketStatus = useHasCreatedContractToday(creator)
|
const mustWaitForDailyFreeMarketStatus = useHasCreatedContractToday(creator)
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,10 @@ import { EditFoldButton } from 'web/components/folds/edit-fold-button'
|
||||||
import Custom404 from '../../404'
|
import Custom404 from '../../404'
|
||||||
import { FollowFoldButton } from 'web/components/folds/follow-fold-button'
|
import { FollowFoldButton } from 'web/components/folds/follow-fold-button'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
|
import { useTaggedContracts } from 'web/hooks/use-contracts'
|
||||||
import { Linkify } from 'web/components/linkify'
|
import { Linkify } from 'web/components/linkify'
|
||||||
import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
||||||
|
import { filterDefined } from 'common/util/array'
|
||||||
import { findActiveContracts } from 'web/components/feed/find-active-contracts'
|
import { findActiveContracts } from 'web/components/feed/find-active-contracts'
|
||||||
import { Tabs } from 'web/components/layout/tabs'
|
import { Tabs } from 'web/components/layout/tabs'
|
||||||
|
|
||||||
|
@ -131,6 +133,15 @@ 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 = Object.fromEntries(
|
||||||
|
taggedContracts.map((contract) => [contract.id, contract])
|
||||||
|
)
|
||||||
|
|
||||||
|
const contracts = filterDefined(
|
||||||
|
props.contracts.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 />
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ const Home = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Page suspend={!!contract}>
|
<Page assertUser="signed-in" suspend={!!contract}>
|
||||||
<Col className="mx-auto w-full p-2">
|
<Col className="mx-auto w-full p-2">
|
||||||
<ContractSearch
|
<ContractSearch
|
||||||
querySortOptions={{
|
querySortOptions={{
|
||||||
|
|
|
@ -40,7 +40,7 @@ const Home = (props: { hotContracts: Contract[] }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page assertUser="signed-out">
|
||||||
<div className="px-4 pt-2 md:mt-0 lg:hidden">
|
<div className="px-4 pt-2 md:mt-0 lg:hidden">
|
||||||
<ManifoldLogo />
|
<ManifoldLogo />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,14 +8,19 @@ import {
|
||||||
} from '@heroicons/react/outline'
|
} from '@heroicons/react/outline'
|
||||||
|
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
import { firebaseLogin } from 'web/lib/firebase/users'
|
||||||
|
import { ContractsGrid } from 'web/components/contract/contracts-list'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { Contract } from 'web/lib/firebase/contracts'
|
||||||
|
|
||||||
|
export default function LandingPage(props: { hotContracts: Contract[] }) {
|
||||||
|
const { hotContracts } = props
|
||||||
|
|
||||||
export default function LandingPage() {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Hero />
|
<Hero />
|
||||||
<FeaturesSection />
|
<FeaturesSection />
|
||||||
|
{/* <ExploreMarketsSection hotContracts={hotContracts} /> */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -144,3 +149,20 @@ function FeaturesSection() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ExploreMarketsSection(props: { hotContracts: Contract[] }) {
|
||||||
|
const { hotContracts } = props
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-4xl px-4 py-8">
|
||||||
|
<p className="my-12 text-3xl font-extrabold leading-8 tracking-tight text-indigo-700 sm:text-4xl">
|
||||||
|
Today's top markets
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ContractsGrid
|
||||||
|
contracts={hotContracts}
|
||||||
|
loadMore={() => {}}
|
||||||
|
hasMore={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz() {
|
export async function getStaticPropz() {
|
||||||
const [topTraders, topCreators] = await Promise.all([
|
const [topTraders, topCreators] = await Promise.all([
|
||||||
getTopTraders().catch(() => {}),
|
getTopTraders().catch((_) => {}),
|
||||||
getTopCreators().catch(() => {}),
|
getTopCreators().catch((_) => {}),
|
||||||
])
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -290,3 +290,13 @@ ${TEST_VALUE}
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given a date string like '2022-04-02',
|
||||||
|
// return the time just before midnight on that date (in the user's local time), as millis since epoch
|
||||||
|
function dateToMillis(date: string) {
|
||||||
|
return dayjs(date)
|
||||||
|
.set('hour', 23)
|
||||||
|
.set('minute', 59)
|
||||||
|
.set('second', 59)
|
||||||
|
.valueOf()
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@ import { Linkify } from 'web/components/linkify'
|
||||||
import {
|
import {
|
||||||
BinaryOutcomeLabel,
|
BinaryOutcomeLabel,
|
||||||
CancelLabel,
|
CancelLabel,
|
||||||
|
FreeResponseOutcomeLabel,
|
||||||
MultiLabel,
|
MultiLabel,
|
||||||
|
OutcomeLabel,
|
||||||
ProbPercentLabel,
|
ProbPercentLabel,
|
||||||
} from 'web/components/outcome-label'
|
} from 'web/components/outcome-label'
|
||||||
import {
|
import {
|
||||||
|
@ -42,7 +44,7 @@ import {
|
||||||
import { getContractFromId } from 'web/lib/firebase/contracts'
|
import { getContractFromId } from 'web/lib/firebase/contracts'
|
||||||
import { CheckIcon, XIcon } from '@heroicons/react/outline'
|
import { CheckIcon, XIcon } from '@heroicons/react/outline'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney, formatPercent } from 'common/util/format'
|
||||||
|
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
@ -182,7 +184,7 @@ function NotificationGroupItem(props: {
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { notificationGroup, className } = props
|
const { notificationGroup, className } = props
|
||||||
const { sourceContractId, notifications } = notificationGroup
|
const { sourceContractId, notifications, timePeriod } = notificationGroup
|
||||||
const {
|
const {
|
||||||
sourceContractTitle,
|
sourceContractTitle,
|
||||||
sourceContractSlug,
|
sourceContractSlug,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { zip, uniq, sumBy, concat, countBy, sortBy, sum } from 'lodash'
|
import { zip, uniq, sumBy, concat, countBy, sortBy, sum } from 'lodash'
|
||||||
|
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
|
||||||
import {
|
import {
|
||||||
DailyCountChart,
|
DailyCountChart,
|
||||||
DailyPercentChart,
|
DailyPercentChart,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user