Revert "Turn on no unused variables linting, kill dead code (#484)"

This reverts commit 515928a69a.
This commit is contained in:
Austin Chen 2022-06-12 20:55:48 -07:00
parent 4ad04869a1
commit 5beda1ded7
30 changed files with 252 additions and 43 deletions

View File

@ -8,15 +8,8 @@ module.exports = {
],
rules: {
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': '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-typos': 'off',
'lodash/import-scope': [2, 'member'],

View File

@ -116,7 +116,7 @@ export function AnswersPanel(props: { contract: FreeResponseContract }) {
{!resolveOption && (
<div className={clsx('flow-root pr-2 md:pr-0')}>
<div className={clsx(tradingAllowed(contract) ? '' : '-mb-6')}>
{answerItems.map((item) => (
{answerItems.map((item, activityItemIdx) => (
<div key={item.id} className={'relative pb-2'}>
<div className="relative flex items-start space-x-3">
<OpenAnswer {...item} />

View File

@ -138,8 +138,9 @@ export function BetsList(props: { user: User; hideBetsBefore?: number }) {
return !hasSoldAll
})
const unsettled = contracts.filter(
(c) => !c.isResolved && contractsMetrics[c.id].invested !== 0
const [settled, unsettled] = partition(
contracts,
(c) => c.isResolved || contractsMetrics[c.id].invested === 0
)
const currentInvested = sumBy(
@ -260,7 +261,7 @@ function ContractBets(props: {
const isBinary = outcomeType === 'BINARY'
const { payout, profit, profitPercent } = getContractBetMetrics(
const { payout, profit, profitPercent, invested } = getContractBetMetrics(
contract,
bets
)
@ -656,6 +657,7 @@ function SellButton(props: { contract: Contract; bet: Bet }) {
return (
<ConfirmationButton
id={`sell-${bet.id}`}
openModalBtn={{
className: clsx('btn-sm', isSubmitting && 'btn-disabled loading'),
label: 'Sell',

View File

@ -8,7 +8,7 @@ import { manaToUSD } from '../../../common/util/format'
import { Row } from '../layout/row'
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 raised = sumBy(txns, (txn) => txn.amount)

View File

@ -24,12 +24,13 @@ export function ChoicesToggleGroup(props: {
<RadioGroup
className={clsx(className, 'flex flex-row flex-wrap items-center gap-3')}
value={currentChoice.toString()}
onChange={setChoice}
onChange={(str) => null}
>
{Object.keys(choicesMap).map((choiceKey) => (
<RadioGroup.Option
key={choiceKey}
value={choicesMap[choiceKey]}
onClick={() => setChoice(choicesMap[choiceKey])}
className={({ active }) =>
clsx(
active ? 'ring-2 ring-indigo-500 ring-offset-2' : '',

View File

@ -5,6 +5,7 @@ import { Modal } from './layout/modal'
import { Row } from './layout/row'
export function ConfirmationButton(props: {
id: string
openModalBtn: {
label: string
icon?: JSX.Element
@ -21,7 +22,7 @@ export function ConfirmationButton(props: {
onSubmit: () => void
children: ReactNode
}) {
const { openModalBtn, cancelBtn, submitBtn, onSubmit, children } = props
const { id, openModalBtn, cancelBtn, submitBtn, onSubmit, children } = props
const [open, setOpen] = useState(false)
@ -66,6 +67,7 @@ export function ResolveConfirmationButton(props: {
props
return (
<ConfirmationButton
id="resolution-modal"
openModalBtn={{
className: clsx(
'border-none self-start',

View File

@ -140,7 +140,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
}
}
const textColor = `text-${getColor(contract)}`
const textColor = `text-${getColor(contract, previewProb)}`
return (
<Col
@ -223,7 +223,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
export function ProbBar(props: { contract: Contract; previewProb?: number }) {
const { contract, previewProb } = props
const color = getColor(contract)
const color = getColor(contract, previewProb)
const prob = previewProb ?? getProb(contract)
return (
<>
@ -257,7 +257,7 @@ function QuickOutcomeView(props: {
// If there's a preview prob, display that instead of the current prob
const override =
previewProb === undefined ? undefined : formatPercent(previewProb)
const textColor = `text-${getColor(contract)}`
const textColor = `text-${getColor(contract, previewProb)}`
let display: string | undefined
switch (outcomeType) {
@ -306,7 +306,7 @@ function getNumericScale(contract: NumericContract) {
return (ev - min) / (max - min)
}
export function getColor(contract: Contract) {
export function getColor(contract: Contract, previewProb?: number) {
// TODO: Try injecting a gradient here
// return 'primary'
const { resolution } = contract

View File

@ -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 { 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 { Contract } from 'common/contract'
import { Contract, MAX_QUESTION_LENGTH } from 'common/contract'
import { Col } from './layout/col'
import clsx from 'clsx'
import { Row } from './layout/row'
import { ENV_CONFIG } from '../../common/envs/constants'
import { SiteLink } from './site-link'
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>
)
}

View File

@ -2,7 +2,7 @@ import { Answer } from 'common/answer'
import { Bet } from 'common/bet'
import { Comment } from 'common/comment'
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 { Modal } from 'web/components/layout/modal'
import { AnswerBetPanel } from 'web/components/answers/answer-bet-panel'

View File

@ -3,6 +3,7 @@ import React, { useState } from 'react'
import {
BanIcon,
CheckIcon,
DotsVerticalIcon,
LockClosedIcon,
XIcon,
} 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>
</>
)
}

View File

@ -48,6 +48,7 @@ export function CreateFoldButton() {
return (
<ConfirmationButton
id="create-fold"
openModalBtn={{
label: 'New',
icon: <PlusCircleIcon className="mr-2 h-5 w-5" />,

View File

@ -98,7 +98,7 @@ function AddLiquidityPanel(props: { contract: CPMMContract }) {
setError('Server error')
}
})
.catch((_) => setError('Server error'))
.catch((e) => setError('Server error'))
}
return (
@ -162,7 +162,7 @@ function WithdrawLiquidityPanel(props: {
const { contract, lpShares } = props
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 [isLoading, setIsLoading] = useState(false)
@ -171,12 +171,12 @@ function WithdrawLiquidityPanel(props: {
setIsSuccess(false)
withdrawLiquidity({ contractId: contract.id })
.then((_) => {
.then((r) => {
setIsSuccess(true)
setError(undefined)
setIsLoading(false)
})
.catch((_) => setError('Server error'))
.catch((e) => setError('Server error'))
}
if (isSuccess)

View File

@ -1,7 +1,8 @@
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 { Avatar } from '../avatar'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
export function ProfileSummary(props: { user: User }) {
const { user } = props

View File

@ -7,12 +7,15 @@ import {
CashIcon,
HeartIcon,
PresentationChartLineIcon,
ChatAltIcon,
SparklesIcon,
NewspaperIcon,
} from '@heroicons/react/outline'
import clsx from 'clsx'
import { sortBy } from 'lodash'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useFollowedFolds } from 'web/hooks/use-fold'
import { useUser } from 'web/hooks/use-user'
import { firebaseLogin, firebaseLogout, User } from 'web/lib/firebase/users'
import { ManifoldLogo } from './manifold-logo'
@ -176,6 +179,8 @@ export default function Sidebar(props: { className?: string }) {
}, [])
const user = useUser()
let folds = useFollowedFolds(user) || []
folds = sortBy(folds, 'followCount').reverse()
const mustWaitForFreeMarketStatus = useHasCreatedContractToday(user)
const navigationOptions =
user === null

View File

@ -53,6 +53,7 @@ function NumericBuyPanel(props: {
const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
const [valueError, setValueError] = useState<string | undefined>()
const [error, setError] = useState<string | undefined>()
const [isSubmitting, setIsSubmitting] = useState(false)
const [wasSubmitted, setWasSubmitted] = useState(false)

View File

@ -6,11 +6,12 @@ import { Toaster } from 'react-hot-toast'
export function Page(props: {
margin?: boolean
assertUser?: 'signed-in' | 'signed-out'
rightSidebar?: ReactNode
suspend?: boolean
children?: ReactNode
}) {
const { margin, children, rightSidebar, suspend } = props
const { margin, assertUser, children, rightSidebar, suspend } = props
return (
<>

View File

@ -1,7 +1,9 @@
import { isEqual, sortBy } from 'lodash'
import { useEffect, useState } from 'react'
import { Fold } from 'common/fold'
import { User } from 'common/user'
import {
listAllFolds,
listenForFold,
listenForFolds,
listenForFoldsWithTags,
@ -78,3 +80,38 @@ export const useFollowedFoldIds = (user: User | null | undefined) => {
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
}

View File

@ -1,7 +1,7 @@
import { collection, query, where } from 'firebase/firestore'
import { Notification } from 'common/notification'
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) {
const notifsCollection = collection(db, `/users/${userId}/notifications`)

View File

@ -37,7 +37,7 @@ export default function Activity() {
return (
<>
<Page suspend={!!contract}>
<Page assertUser="signed-in" suspend={!!contract}>
<Col className="mx-auto w-full max-w-[700px]">
<CategorySelector category={category} setCategory={setCategory} />
<Spacer h={1} />

View File

@ -19,7 +19,6 @@ export default async function handler(
listAllComments(contractId),
])
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const bets = allBets.map(({ userId, ...bet }) => bet) as Exclude<
Bet,
'userId'

View File

@ -24,7 +24,6 @@ export default async function handler(
listAllComments(contract.id),
])
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const bets = allBets.map(({ userId, ...bet }) => bet) as Exclude<
Bet,
'userId'

View File

@ -56,8 +56,8 @@ export default function Create() {
}
// Allow user to create a new contract
export function NewContract(props: { question: string }) {
const { question } = props
export function NewContract(props: { question: string; tag?: string }) {
const { question, tag } = props
const creator = useUser()
useEffect(() => {
@ -74,7 +74,7 @@ export function NewContract(props: { question: string }) {
// const [tagText, setTagText] = useState<string>(tag ?? '')
// const tags = parseWordsAsTags(tagText)
const [ante, _setAnte] = useState(FIXED_ANTE)
const [ante, setAnte] = useState(FIXED_ANTE)
const mustWaitForDailyFreeMarketStatus = useHasCreatedContractToday(creator)

View File

@ -27,8 +27,10 @@ import { EditFoldButton } from 'web/components/folds/edit-fold-button'
import Custom404 from '../../404'
import { FollowFoldButton } from 'web/components/folds/follow-fold-button'
import { SEO } from 'web/components/SEO'
import { useTaggedContracts } from 'web/hooks/use-contracts'
import { Linkify } from 'web/components/linkify'
import { fromPropz, usePropz } from 'web/hooks/use-propz'
import { filterDefined } from 'common/util/array'
import { findActiveContracts } from 'web/components/feed/find-active-contracts'
import { Tabs } from 'web/components/layout/tabs'
@ -131,6 +133,15 @@ export default function FoldPage(props: {
const user = useUser()
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]) {
return <Custom404 />
}

View File

@ -23,7 +23,7 @@ const Home = () => {
return (
<>
<Page suspend={!!contract}>
<Page assertUser="signed-in" suspend={!!contract}>
<Col className="mx-auto w-full p-2">
<ContractSearch
querySortOptions={{

View File

@ -40,7 +40,7 @@ const Home = (props: { hotContracts: Contract[] }) => {
}
return (
<Page>
<Page assertUser="signed-out">
<div className="px-4 pt-2 md:mt-0 lg:hidden">
<ManifoldLogo />
</div>

View File

@ -8,14 +8,19 @@ import {
} from '@heroicons/react/outline'
import { firebaseLogin } from 'web/lib/firebase/users'
import { ContractsGrid } from 'web/components/contract/contracts-list'
import { Col } from 'web/components/layout/col'
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 (
<div>
<Hero />
<FeaturesSection />
{/* <ExploreMarketsSection hotContracts={hotContracts} /> */}
</div>
)
}
@ -144,3 +149,20 @@ function FeaturesSection() {
</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>
)
}

View File

@ -8,8 +8,8 @@ import { fromPropz, usePropz } from 'web/hooks/use-propz'
export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz() {
const [topTraders, topCreators] = await Promise.all([
getTopTraders().catch(() => {}),
getTopCreators().catch(() => {}),
getTopTraders().catch((_) => {}),
getTopCreators().catch((_) => {}),
])
return {

View File

@ -290,3 +290,13 @@ ${TEST_VALUE}
</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()
}

View File

@ -31,7 +31,9 @@ import { Linkify } from 'web/components/linkify'
import {
BinaryOutcomeLabel,
CancelLabel,
FreeResponseOutcomeLabel,
MultiLabel,
OutcomeLabel,
ProbPercentLabel,
} from 'web/components/outcome-label'
import {
@ -42,7 +44,7 @@ import {
import { getContractFromId } from 'web/lib/firebase/contracts'
import { CheckIcon, XIcon } from '@heroicons/react/outline'
import toast from 'react-hot-toast'
import { formatMoney } from 'common/util/format'
import { formatMoney, formatPercent } from 'common/util/format'
export default function Notifications() {
const user = useUser()
@ -182,7 +184,7 @@ function NotificationGroupItem(props: {
className?: string
}) {
const { notificationGroup, className } = props
const { sourceContractId, notifications } = notificationGroup
const { sourceContractId, notifications, timePeriod } = notificationGroup
const {
sourceContractTitle,
sourceContractSlug,

View File

@ -1,5 +1,6 @@
import dayjs from 'dayjs'
import { zip, uniq, sumBy, concat, countBy, sortBy, sum } from 'lodash'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
import {
DailyCountChart,
DailyPercentChart,