Quick back navigation into feed
This commit is contained in:
parent
9f2ac17ffb
commit
e53cde5b34
|
@ -1,7 +1,6 @@
|
|||
import _ from 'lodash'
|
||||
import { useLayoutEffect, useState } from 'react'
|
||||
|
||||
import { Answer } from '../../../common/answer'
|
||||
import { DPM, FreeResponse, FullContract } from '../../../common/contract'
|
||||
import { Col } from '../layout/col'
|
||||
import { formatPercent } from '../../../common/util/format'
|
||||
|
@ -15,12 +14,11 @@ import { AnswerResolvePanel } from './answer-resolve-panel'
|
|||
|
||||
export function AnswersPanel(props: {
|
||||
contract: FullContract<DPM, FreeResponse>
|
||||
answers: Answer[]
|
||||
}) {
|
||||
const { contract } = props
|
||||
const { creatorId, resolution, resolutions, totalBets } = contract
|
||||
|
||||
const answers = useAnswers(contract.id) ?? props.answers
|
||||
const answers = useAnswers(contract.id) ?? contract.answers
|
||||
const [winningAnswers, otherAnswers] = _.partition(
|
||||
answers.filter(
|
||||
(answer) => answer.id !== '0' && totalBets[answer.id] > 0.000000001
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import _ from 'lodash'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Contract, tradingAllowed } from '../../lib/firebase/contracts'
|
||||
import { Contract } from '../../lib/firebase/contracts'
|
||||
import { Comment } from '../../lib/firebase/comments'
|
||||
import { Col } from '../layout/col'
|
||||
import { Bet } from '../../../common/bet'
|
||||
import { useUser } from '../../hooks/use-user'
|
||||
import BetRow from '../bet-row'
|
||||
import { FeedQuestion } from './feed-items'
|
||||
import { ContractActivity } from './contract-activity'
|
||||
|
||||
export function ActivityFeed(props: {
|
||||
|
@ -15,8 +12,9 @@ export function ActivityFeed(props: {
|
|||
recentBets: Bet[]
|
||||
recentComments: Comment[]
|
||||
mode: 'only-recent' | 'abbreviated' | 'all'
|
||||
getContractPath?: (contract: Contract) => string
|
||||
}) {
|
||||
const { contracts, recentBets, recentComments, mode } = props
|
||||
const { contracts, recentBets, recentComments, mode, getContractPath } = props
|
||||
|
||||
const user = useUser()
|
||||
|
||||
|
@ -36,23 +34,13 @@ export function ActivityFeed(props: {
|
|||
bets={groupedBets[contract.id] ?? []}
|
||||
comments={groupedComments[contract.id] ?? []}
|
||||
mode={mode}
|
||||
contractPath={getContractPath ? getContractPath(contract) : undefined}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function SummaryActivityFeed(props: { contracts: Contract[] }) {
|
||||
const { contracts } = props
|
||||
|
||||
return (
|
||||
<FeedContainer
|
||||
contracts={contracts}
|
||||
renderContract={(contract) => <ContractSummary contract={contract} />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function FeedContainer(props: {
|
||||
contracts: Contract[]
|
||||
renderContract: (contract: Contract) => any
|
||||
|
@ -73,27 +61,3 @@ function FeedContainer(props: {
|
|||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function ContractSummary(props: {
|
||||
contract: Contract
|
||||
betRowClassName?: string
|
||||
}) {
|
||||
const { contract, betRowClassName } = props
|
||||
const { outcomeType } = contract
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
|
||||
return (
|
||||
<div className="flow-root pr-2 md:pr-0">
|
||||
<div className={clsx(tradingAllowed(contract) ? '' : '-mb-8')}>
|
||||
<div className="relative pb-8">
|
||||
<div className="relative flex items-start space-x-3">
|
||||
<FeedQuestion contract={contract} showDescription />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isBinary && tradingAllowed(contract) && (
|
||||
<BetRow contract={contract} className={clsx('mb-2', betRowClassName)} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ export type DescriptionItem = BaseActivityItem & {
|
|||
export type QuestionItem = BaseActivityItem & {
|
||||
type: 'question'
|
||||
showDescription: boolean
|
||||
contractPath?: string
|
||||
}
|
||||
|
||||
export type BetItem = BaseActivityItem & {
|
||||
|
@ -280,7 +281,14 @@ export function getAllContractActivityItems(
|
|||
filterToOutcome && answer
|
||||
? [{ type: 'createanswer', id: answer.id, contract, answer }]
|
||||
: abbreviated
|
||||
? [{ type: 'question', id: '0', contract, showDescription: false }]
|
||||
? [
|
||||
{
|
||||
type: 'question',
|
||||
id: '0',
|
||||
contract,
|
||||
showDescription: false,
|
||||
},
|
||||
]
|
||||
: [{ type: 'description', id: '0', contract }]
|
||||
|
||||
items.push(
|
||||
|
@ -325,8 +333,12 @@ export function getRecentContractActivityItems(
|
|||
contract: Contract,
|
||||
bets: Bet[],
|
||||
comments: Comment[],
|
||||
user: User | null | undefined
|
||||
user: User | null | undefined,
|
||||
options: {
|
||||
contractPath?: string
|
||||
}
|
||||
) {
|
||||
const { contractPath } = options
|
||||
bets = bets
|
||||
.filter((bet) => !bet.isRedemption)
|
||||
.sort((b1, b2) => b1.createdTime - b2.createdTime)
|
||||
|
@ -337,6 +349,7 @@ export function getRecentContractActivityItems(
|
|||
id: '0',
|
||||
contract,
|
||||
showDescription: false,
|
||||
contractPath,
|
||||
}
|
||||
|
||||
const items =
|
||||
|
|
|
@ -19,11 +19,19 @@ export function ContractActivity(props: {
|
|||
user: User | null | undefined
|
||||
mode: 'only-recent' | 'abbreviated' | 'all'
|
||||
filterToOutcome?: string // Which multi-category outcome to filter
|
||||
contractPath?: string
|
||||
className?: string
|
||||
betRowClassName?: string
|
||||
}) {
|
||||
const { contract, user, filterToOutcome, mode, className, betRowClassName } =
|
||||
props
|
||||
const {
|
||||
contract,
|
||||
user,
|
||||
filterToOutcome,
|
||||
mode,
|
||||
contractPath,
|
||||
className,
|
||||
betRowClassName,
|
||||
} = props
|
||||
|
||||
const updatedComments =
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
|
@ -36,7 +44,9 @@ export function ContractActivity(props: {
|
|||
|
||||
const items =
|
||||
mode === 'only-recent'
|
||||
? getRecentContractActivityItems(contract, bets, comments, user)
|
||||
? getRecentContractActivityItems(contract, bets, comments, user, {
|
||||
contractPath,
|
||||
})
|
||||
: getAllContractActivityItems(
|
||||
contract,
|
||||
bets,
|
||||
|
|
|
@ -295,7 +295,8 @@ function TruncatedComment(props: {
|
|||
|
||||
export function FeedQuestion(props: {
|
||||
contract: Contract
|
||||
showDescription?: boolean
|
||||
showDescription: boolean
|
||||
contractPath?: string
|
||||
}) {
|
||||
const { contract, showDescription } = props
|
||||
const { creatorName, creatorUsername, question, resolution, outcomeType } =
|
||||
|
@ -335,7 +336,9 @@ export function FeedQuestion(props: {
|
|||
<Col className="items-start justify-between gap-2 sm:flex-row sm:gap-4">
|
||||
<Col>
|
||||
<SiteLink
|
||||
href={contractPath(contract)}
|
||||
href={
|
||||
props.contractPath ? props.contractPath : contractPath(contract)
|
||||
}
|
||||
className="text-lg text-indigo-700 sm:text-xl"
|
||||
>
|
||||
{question}
|
||||
|
|
|
@ -6,9 +6,10 @@ export function Page(props: {
|
|||
margin?: boolean
|
||||
assertUser?: 'signed-in' | 'signed-out'
|
||||
rightSidebar?: React.ReactNode
|
||||
suspend?: boolean
|
||||
children?: any
|
||||
}) {
|
||||
const { margin, assertUser, children, rightSidebar } = props
|
||||
const { margin, assertUser, children, rightSidebar, suspend } = props
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -17,6 +18,7 @@ export function Page(props: {
|
|||
'mx-auto w-full pb-14 lg:grid lg:grid-cols-12 lg:gap-8 lg:pt-6 xl:max-w-7xl',
|
||||
margin && 'px-4'
|
||||
)}
|
||||
style={suspend ? visuallyHiddenStyle : undefined}
|
||||
>
|
||||
<div className="hidden lg:col-span-2 lg:block">
|
||||
<Sidebar />
|
||||
|
@ -41,3 +43,15 @@ export function Page(props: {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const visuallyHiddenStyle = {
|
||||
clip: 'rect(0 0 0 0)',
|
||||
clipPath: 'inset(50%)',
|
||||
height: 1,
|
||||
margin: -1,
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
position: 'absolute',
|
||||
width: 1,
|
||||
whiteSpace: 'nowrap',
|
||||
} as const
|
||||
|
|
|
@ -28,6 +28,10 @@ export function contractPath(contract: Contract) {
|
|||
return `/${contract.creatorUsername}/${contract.slug}`
|
||||
}
|
||||
|
||||
export function homeContractPath(contract: Contract) {
|
||||
return `/home?c=${contract.slug}`
|
||||
}
|
||||
|
||||
export function contractMetrics(contract: Contract) {
|
||||
const { createdTime, resolutionTime, isResolved } = contract
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { ArrowLeftIcon } from '@heroicons/react/outline'
|
||||
|
||||
import { useContractWithPreload } from '../../hooks/use-contract'
|
||||
import { ContractOverview } from '../../components/contract/contract-overview'
|
||||
|
@ -21,10 +22,6 @@ import { contractTextDetails } from '../../components/contract/contract-card'
|
|||
import { Bet, listAllBets } from '../../lib/firebase/bets'
|
||||
import { Comment, listAllComments } from '../../lib/firebase/comments'
|
||||
import Custom404 from '../404'
|
||||
import { getFoldsByTags } from '../../lib/firebase/folds'
|
||||
import { Fold } from '../../../common/fold'
|
||||
import { listAllAnswers } from '../../lib/firebase/answers'
|
||||
import { Answer } from '../../../common/answer'
|
||||
import { AnswersPanel } from '../../components/answers/answers-panel'
|
||||
import { fromPropz, usePropz } from '../../hooks/use-propz'
|
||||
import { Leaderboard } from '../../components/leaderboard'
|
||||
|
@ -34,6 +31,8 @@ import { formatMoney } from '../../../common/util/format'
|
|||
import { FeedBet, FeedComment } from '../../components/feed/feed-items'
|
||||
import { useUserById } from '../../hooks/use-users'
|
||||
import { ContractTabs } from '../../components/contract/contract-tabs'
|
||||
import { FirstArgument } from '../../../common/util/types'
|
||||
import { DPM, FreeResponse, FullContract } from '../../../common/contract'
|
||||
|
||||
export const getStaticProps = fromPropz(getStaticPropz)
|
||||
export async function getStaticPropz(props: {
|
||||
|
@ -43,18 +42,11 @@ export async function getStaticPropz(props: {
|
|||
const contract = (await getContractFromSlug(contractSlug)) || null
|
||||
const contractId = contract?.id
|
||||
|
||||
const foldsPromise = getFoldsByTags(contract?.tags ?? [])
|
||||
|
||||
const [bets, comments, answers] = await Promise.all([
|
||||
const [bets, comments] = await Promise.all([
|
||||
contractId ? listAllBets(contractId) : [],
|
||||
contractId ? listAllComments(contractId) : [],
|
||||
contractId && contract.outcomeType === 'FREE_RESPONSE'
|
||||
? listAllAnswers(contractId)
|
||||
: [],
|
||||
])
|
||||
|
||||
const folds = await foldsPromise
|
||||
|
||||
return {
|
||||
props: {
|
||||
contract,
|
||||
|
@ -62,8 +54,6 @@ export async function getStaticPropz(props: {
|
|||
slug: contractSlug,
|
||||
bets,
|
||||
comments,
|
||||
answers,
|
||||
folds,
|
||||
},
|
||||
|
||||
revalidate: 60, // regenerate after a minute
|
||||
|
@ -79,19 +69,22 @@ export default function ContractPage(props: {
|
|||
username: string
|
||||
bets: Bet[]
|
||||
comments: Comment[]
|
||||
answers: Answer[]
|
||||
slug: string
|
||||
folds: Fold[]
|
||||
backToHome?: () => void
|
||||
}) {
|
||||
props = usePropz(props, getStaticPropz) ?? {
|
||||
contract: null,
|
||||
username: '',
|
||||
comments: [],
|
||||
answers: [],
|
||||
bets: [],
|
||||
slug: '',
|
||||
folds: [],
|
||||
}
|
||||
return <ContractPageContent {...props} />
|
||||
}
|
||||
|
||||
export function ContractPageContent(props: FirstArgument<typeof ContractPage>) {
|
||||
const { backToHome } = props
|
||||
|
||||
const user = useUser()
|
||||
|
||||
const contract = useContractWithPreload(props.contract)
|
||||
|
@ -136,6 +129,16 @@ export default function ContractPage(props: {
|
|||
)}
|
||||
|
||||
<Col className="w-full justify-between rounded border-0 border-gray-100 bg-white px-2 py-6 md:px-6 md:py-8">
|
||||
{backToHome && (
|
||||
<button
|
||||
className="btn btn-sm mb-4 items-center gap-2 self-start border-0 border-gray-700 bg-white normal-case text-gray-700 hover:bg-white hover:text-gray-700 lg:hidden"
|
||||
onClick={backToHome}
|
||||
>
|
||||
<ArrowLeftIcon className="h-5 w-5 text-gray-700" />
|
||||
Back
|
||||
</button>
|
||||
)}
|
||||
|
||||
<ContractOverview
|
||||
contract={contract}
|
||||
bets={bets ?? []}
|
||||
|
@ -145,8 +148,7 @@ export default function ContractPage(props: {
|
|||
<>
|
||||
<Spacer h={4} />
|
||||
<AnswersPanel
|
||||
contract={contract as any}
|
||||
answers={props.answers}
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
/>
|
||||
<Spacer h={4} />
|
||||
</>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import Router from 'next/router'
|
||||
import React, { useEffect } from 'react'
|
||||
import Router, { useRouter } from 'next/router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import { Page } from '../components/page'
|
||||
|
@ -13,6 +13,7 @@ import { useRecentBets } from '../hooks/use-bets'
|
|||
import { useActiveContracts } from '../hooks/use-contracts'
|
||||
import { useRecentComments } from '../hooks/use-comments'
|
||||
import { useAlgoFeed } from '../hooks/use-algo-feed'
|
||||
import { ContractPageContent } from './[username]/[contractSlug]'
|
||||
|
||||
const Home = () => {
|
||||
const user = useUser()
|
||||
|
@ -29,33 +30,55 @@ const Home = () => {
|
|||
(contract) => contractsDict[contract.id] ?? contract
|
||||
)
|
||||
|
||||
const router = useRouter()
|
||||
const slug = router.query.c as string | undefined
|
||||
const contract = feedContracts.find((c) => c.slug === slug)
|
||||
|
||||
useEffect(() => {
|
||||
if (slug && !contract) {
|
||||
delete router.query.c
|
||||
router.replace(router, undefined, { shallow: true })
|
||||
}
|
||||
})
|
||||
|
||||
if (user === null) {
|
||||
Router.replace('/')
|
||||
return <></>
|
||||
}
|
||||
|
||||
const activityContent =
|
||||
contracts && recentBets && recentComments ? (
|
||||
<ActivityFeed
|
||||
contracts={updatedContracts}
|
||||
recentBets={recentBets}
|
||||
recentComments={recentComments}
|
||||
mode="only-recent"
|
||||
/>
|
||||
) : (
|
||||
<LoadingIndicator className="mt-4" />
|
||||
)
|
||||
|
||||
return (
|
||||
<Page assertUser="signed-in">
|
||||
<Col className="items-center">
|
||||
<Col className="w-full max-w-[700px]">
|
||||
<FeedCreate user={user ?? undefined} />
|
||||
<Spacer h={10} />
|
||||
{activityContent}
|
||||
<>
|
||||
<Page assertUser="signed-in" suspend={!!contract}>
|
||||
<Col className="items-center">
|
||||
<Col className="w-full max-w-[700px]">
|
||||
<FeedCreate user={user ?? undefined} />
|
||||
<Spacer h={10} />
|
||||
{contracts && recentBets && recentComments ? (
|
||||
<ActivityFeed
|
||||
contracts={updatedContracts}
|
||||
recentBets={recentBets}
|
||||
recentComments={recentComments}
|
||||
mode="only-recent"
|
||||
getContractPath={(c) => `home?c=${c.slug}`}
|
||||
/>
|
||||
) : (
|
||||
<LoadingIndicator className="mt-4" />
|
||||
)}
|
||||
</Col>
|
||||
</Col>
|
||||
</Col>
|
||||
</Page>
|
||||
</Page>
|
||||
|
||||
{contract && (
|
||||
<ContractPageContent
|
||||
contract={contract}
|
||||
username={contract.creatorUsername}
|
||||
slug={contract.slug}
|
||||
bets={[]}
|
||||
comments={[]}
|
||||
backToHome={router.back}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user