making answer reply button work
This commit is contained in:
parent
e5af7bf76f
commit
0e842ab391
|
@ -1,7 +1,11 @@
|
||||||
import { sortBy, partition, sum } from 'lodash'
|
import { sortBy, partition, sum } from 'lodash'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
|
import {
|
||||||
|
Contract,
|
||||||
|
FreeResponseContract,
|
||||||
|
MultipleChoiceContract,
|
||||||
|
} from 'common/contract'
|
||||||
import { Col } from '../layout/col'
|
import { Col } from '../layout/col'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { getDpmOutcomeProbability } from 'common/calculate-dpm'
|
import { getDpmOutcomeProbability } from 'common/calculate-dpm'
|
||||||
|
@ -29,11 +33,22 @@ import { ChatAlt2Icon, ChatAltIcon } from '@heroicons/react/solid'
|
||||||
import { ChatIcon } from '@heroicons/react/outline'
|
import { ChatIcon } from '@heroicons/react/outline'
|
||||||
import { ReplyTo } from '../feed/feed-comments'
|
import { ReplyTo } from '../feed/feed-comments'
|
||||||
|
|
||||||
|
// TODO: get a color
|
||||||
|
// export function useAnswerColor(answer: Answer, contract:Contract) {
|
||||||
|
// const colorSortedAnswer = useChartAnswers(contract).map(
|
||||||
|
// (value, _index) => value.text
|
||||||
|
// )
|
||||||
|
// colorIndex={colorSortedAnswer.indexOf(answer.text)}
|
||||||
|
// const color =
|
||||||
|
// colorIndex != undefined ? CATEGORY_COLORS[colorIndex] : '#B1B1C7'
|
||||||
|
// }
|
||||||
|
|
||||||
export function AnswersPanel(props: {
|
export function AnswersPanel(props: {
|
||||||
contract: FreeResponseContract | MultipleChoiceContract
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
|
onAnswerCommentClick: (answer: Answer) => void
|
||||||
}) {
|
}) {
|
||||||
const isAdmin = useAdmin()
|
const isAdmin = useAdmin()
|
||||||
const { contract } = props
|
const { contract, onAnswerCommentClick } = props
|
||||||
const { creatorId, resolution, resolutions, totalBets, outcomeType } =
|
const { creatorId, resolution, resolutions, totalBets, outcomeType } =
|
||||||
contract
|
contract
|
||||||
const [showAllAnswers, setShowAllAnswers] = useState(false)
|
const [showAllAnswers, setShowAllAnswers] = useState(false)
|
||||||
|
@ -141,6 +156,7 @@ export function AnswersPanel(props: {
|
||||||
answer={item}
|
answer={item}
|
||||||
contract={contract}
|
contract={contract}
|
||||||
colorIndex={colorSortedAnswer.indexOf(item.text)}
|
colorIndex={colorSortedAnswer.indexOf(item.text)}
|
||||||
|
onAnswerCommentClick={onAnswerCommentClick}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{hasZeroBetAnswers && !showAllAnswers && (
|
{hasZeroBetAnswers && !showAllAnswers && (
|
||||||
|
@ -188,8 +204,9 @@ function OpenAnswer(props: {
|
||||||
contract: FreeResponseContract | MultipleChoiceContract
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
answer: Answer
|
answer: Answer
|
||||||
colorIndex: number | undefined
|
colorIndex: number | undefined
|
||||||
|
onAnswerCommentClick: (answer: Answer) => void
|
||||||
}) {
|
}) {
|
||||||
const { answer, contract, colorIndex } = props
|
const { answer, contract, colorIndex, onAnswerCommentClick } = props
|
||||||
const { username, avatarUrl, text } = answer
|
const { username, avatarUrl, text } = answer
|
||||||
const prob = getDpmOutcomeProbability(contract.totalShares, answer.id)
|
const prob = getDpmOutcomeProbability(contract.totalShares, answer.id)
|
||||||
const probPercent = formatPercent(prob)
|
const probPercent = formatPercent(prob)
|
||||||
|
@ -242,6 +259,7 @@ function OpenAnswer(props: {
|
||||||
{
|
{
|
||||||
<button
|
<button
|
||||||
className="p-1"
|
className="p-1"
|
||||||
|
onClick={() => onAnswerCommentClick(answer)}
|
||||||
// onClick={() =>
|
// onClick={() =>
|
||||||
// //TODO: make replies
|
// //TODO: make replies
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
FeedCommentThread,
|
FeedCommentThread,
|
||||||
ContractCommentInput,
|
ContractCommentInput,
|
||||||
CommentActions,
|
CommentActions,
|
||||||
|
AnswerCommentInput,
|
||||||
} from '../feed/feed-comments'
|
} from '../feed/feed-comments'
|
||||||
import { groupBy, sortBy, sum } from 'lodash'
|
import { groupBy, sortBy, sum } from 'lodash'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
|
@ -46,16 +47,27 @@ import { Avatar } from '../avatar'
|
||||||
import { UserLink } from '../user-link'
|
import { UserLink } from '../user-link'
|
||||||
import { CopyLinkDateTimeComponent } from '../feed/copy-link-date-time'
|
import { CopyLinkDateTimeComponent } from '../feed/copy-link-date-time'
|
||||||
import { Linkify } from '../linkify'
|
import { Linkify } from '../linkify'
|
||||||
import { ArrowRightIcon, ReplyIcon } from '@heroicons/react/solid'
|
import { ArrowRightIcon, ReplyIcon, XIcon } from '@heroicons/react/solid'
|
||||||
import Curve from 'web/public/custom-components/curve'
|
import Curve from 'web/public/custom-components/curve'
|
||||||
|
import { Answer } from 'common/answer'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
export function ContractTabs(props: {
|
export function ContractTabs(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
userBets: Bet[]
|
userBets: Bet[]
|
||||||
comments: ContractComment[]
|
comments: ContractComment[]
|
||||||
|
answerResponse?: Answer | undefined
|
||||||
|
onCancelAnswerResponse?: () => void
|
||||||
}) {
|
}) {
|
||||||
const { contract, bets, userBets, comments } = props
|
const {
|
||||||
|
contract,
|
||||||
|
bets,
|
||||||
|
userBets,
|
||||||
|
comments,
|
||||||
|
answerResponse,
|
||||||
|
onCancelAnswerResponse,
|
||||||
|
} = props
|
||||||
|
|
||||||
const yourTrades = (
|
const yourTrades = (
|
||||||
<div>
|
<div>
|
||||||
|
@ -68,7 +80,14 @@ export function ContractTabs(props: {
|
||||||
const tabs = buildArray(
|
const tabs = buildArray(
|
||||||
{
|
{
|
||||||
title: 'Comments',
|
title: 'Comments',
|
||||||
content: <CommentsTabContent contract={contract} comments={comments} />,
|
content: (
|
||||||
|
<CommentsTabContent
|
||||||
|
contract={contract}
|
||||||
|
comments={comments}
|
||||||
|
answerResponse={answerResponse}
|
||||||
|
onCancelAnswerResponse={onCancelAnswerResponse}
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
bets.length > 0 && {
|
bets.length > 0 && {
|
||||||
title: capitalize(PAST_BETS),
|
title: capitalize(PAST_BETS),
|
||||||
|
@ -88,8 +107,10 @@ export function ContractTabs(props: {
|
||||||
const CommentsTabContent = memo(function CommentsTabContent(props: {
|
const CommentsTabContent = memo(function CommentsTabContent(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
comments: ContractComment[]
|
comments: ContractComment[]
|
||||||
|
answerResponse?: Answer
|
||||||
|
onCancelAnswerResponse?: () => void
|
||||||
}) {
|
}) {
|
||||||
const { contract } = props
|
const { contract, answerResponse, onCancelAnswerResponse } = props
|
||||||
const tips = useTipTxns({ contractId: contract.id })
|
const tips = useTipTxns({ contractId: contract.id })
|
||||||
const comments = useComments(contract.id) ?? props.comments
|
const comments = useComments(contract.id) ?? props.comments
|
||||||
const [sort, setSort] = usePersistentState<'Newest' | 'Best'>('Newest', {
|
const [sort, setSort] = usePersistentState<'Newest' | 'Best'>('Newest', {
|
||||||
|
@ -107,10 +128,7 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
|
||||||
|
|
||||||
// replied to answers/comments are NOT newest, otherwise newest first
|
// replied to answers/comments are NOT newest, otherwise newest first
|
||||||
const shouldBeNewestFirst = (c: ContractComment) =>
|
const shouldBeNewestFirst = (c: ContractComment) =>
|
||||||
c.replyToCommentId == undefined &&
|
c.replyToCommentId == undefined
|
||||||
(contract.outcomeType === 'FREE_RESPONSE'
|
|
||||||
? c.betId === undefined && c.answerOutcome == undefined
|
|
||||||
: true)
|
|
||||||
|
|
||||||
// TODO: links to comments are broken because tips load after render and
|
// TODO: links to comments are broken because tips load after render and
|
||||||
// comments will reorganize themselves if there are tips/bounties awarded
|
// comments will reorganize themselves if there are tips/bounties awarded
|
||||||
|
@ -160,11 +178,28 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// console.log('answer response:: ', answerResponse)
|
||||||
|
// sortedComments.map((comment, index) =>
|
||||||
|
// console.log(
|
||||||
|
// index,
|
||||||
|
// ',',
|
||||||
|
// comment.content.content[0].content[0].text,
|
||||||
|
// ':',
|
||||||
|
// dayjs(comment.createdTime).format('MM/DD/YY H:mm')
|
||||||
|
// )
|
||||||
|
// )
|
||||||
if (contract.outcomeType === 'FREE_RESPONSE') {
|
if (contract.outcomeType === 'FREE_RESPONSE') {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{sortRow}
|
|
||||||
<ContractCommentInput className="mb-5" contract={contract} />
|
<ContractCommentInput className="mb-5" contract={contract} />
|
||||||
|
{sortRow}
|
||||||
|
{answerResponse && (
|
||||||
|
<AnswerCommentInput
|
||||||
|
contract={contract}
|
||||||
|
answerResponse={answerResponse}
|
||||||
|
onCancelAnswerResponse={onCancelAnswerResponse}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{topLevelComments.map((parent) => {
|
{topLevelComments.map((parent) => {
|
||||||
if (parent.answerOutcome != undefined) {
|
if (parent.answerOutcome != undefined) {
|
||||||
const answer = contract.answers.find(
|
const answer = contract.answers.find(
|
||||||
|
|
|
@ -20,9 +20,13 @@ import { Editor } from '@tiptap/react'
|
||||||
import { UserLink } from 'web/components/user-link'
|
import { UserLink } from 'web/components/user-link'
|
||||||
import { CommentInput } from '../comment-input'
|
import { CommentInput } from '../comment-input'
|
||||||
import { AwardBountyButton } from 'web/components/award-bounty-button'
|
import { AwardBountyButton } from 'web/components/award-bounty-button'
|
||||||
import { ReplyIcon } from '@heroicons/react/solid'
|
import { ReplyIcon, XIcon } from '@heroicons/react/solid'
|
||||||
import { Button } from '../button'
|
import { Button } from '../button'
|
||||||
import { ReplyToggle } from '../comments/comments'
|
import { ReplyToggle } from '../comments/comments'
|
||||||
|
import { CommentsAnswer } from './feed-answer-comment-group'
|
||||||
|
import Curve from 'web/public/custom-components/curve'
|
||||||
|
import { Answer } from 'common/answer'
|
||||||
|
import { useEvent } from 'web/hooks/use-event'
|
||||||
|
|
||||||
export type ReplyTo = { id: string; username: string }
|
export type ReplyTo = { id: string; username: string }
|
||||||
|
|
||||||
|
@ -124,8 +128,6 @@ export function ParentFeedComment(props: {
|
||||||
'hover:bg-greyscale-1 ml-3 gap-2 transition-colors',
|
'hover:bg-greyscale-1 ml-3 gap-2 transition-colors',
|
||||||
highlighted ? `-m-1.5 rounded bg-indigo-500/[0.2] p-1.5` : ''
|
highlighted ? `-m-1.5 rounded bg-indigo-500/[0.2] p-1.5` : ''
|
||||||
)}
|
)}
|
||||||
onMouseOver={() => setShowActions(true)}
|
|
||||||
onMouseLeave={() => setShowActions(false)}
|
|
||||||
>
|
>
|
||||||
<Col className="-ml-3.5">
|
<Col className="-ml-3.5">
|
||||||
<Avatar size="sm" username={userUsername} avatarUrl={userAvatarUrl} />
|
<Avatar size="sm" username={userUsername} avatarUrl={userAvatarUrl} />
|
||||||
|
@ -146,7 +148,6 @@ export function ParentFeedComment(props: {
|
||||||
/>
|
/>
|
||||||
<Row className="grow justify-end">
|
<Row className="grow justify-end">
|
||||||
<CommentActions
|
<CommentActions
|
||||||
showActions={showActions}
|
|
||||||
onReplyClick={onReplyClick}
|
onReplyClick={onReplyClick}
|
||||||
tips={tips}
|
tips={tips}
|
||||||
comment={comment}
|
comment={comment}
|
||||||
|
@ -178,8 +179,6 @@ export function FeedComment(props: {
|
||||||
}
|
}
|
||||||
}, [highlighted])
|
}, [highlighted])
|
||||||
|
|
||||||
const [showActions, setShowActions] = useState(false)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
ref={commentRef}
|
ref={commentRef}
|
||||||
|
@ -188,8 +187,6 @@ export function FeedComment(props: {
|
||||||
'hover:bg-greyscale-1 ml-10 gap-2 transition-colors',
|
'hover:bg-greyscale-1 ml-10 gap-2 transition-colors',
|
||||||
highlighted ? `-m-1.5 rounded bg-indigo-500/[0.2] p-1.5` : ''
|
highlighted ? `-m-1.5 rounded bg-indigo-500/[0.2] p-1.5` : ''
|
||||||
)}
|
)}
|
||||||
onMouseOver={() => setShowActions(true)}
|
|
||||||
onMouseLeave={() => setShowActions(false)}
|
|
||||||
>
|
>
|
||||||
<Col className="-ml-3">
|
<Col className="-ml-3">
|
||||||
<Avatar size="xs" username={userUsername} avatarUrl={userAvatarUrl} />
|
<Avatar size="xs" username={userUsername} avatarUrl={userAvatarUrl} />
|
||||||
|
@ -208,7 +205,6 @@ export function FeedComment(props: {
|
||||||
/>
|
/>
|
||||||
<Row className="justify-end">
|
<Row className="justify-end">
|
||||||
<CommentActions
|
<CommentActions
|
||||||
showActions={showActions}
|
|
||||||
onReplyClick={onReplyClick}
|
onReplyClick={onReplyClick}
|
||||||
tips={tips}
|
tips={tips}
|
||||||
comment={comment}
|
comment={comment}
|
||||||
|
@ -221,20 +217,14 @@ export function FeedComment(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CommentActions(props: {
|
export function CommentActions(props: {
|
||||||
showActions: boolean
|
|
||||||
onReplyClick?: () => void
|
onReplyClick?: () => void
|
||||||
tips?: CommentTips | undefined
|
tips?: CommentTips | undefined
|
||||||
comment: ContractComment
|
comment: ContractComment
|
||||||
contract: Contract<AnyContractType>
|
contract: Contract<AnyContractType>
|
||||||
}) {
|
}) {
|
||||||
const { showActions, onReplyClick, tips, comment, contract } = props
|
const { onReplyClick, tips, comment, contract } = props
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row className={clsx('ml-2 items-center gap-2 text-xs text-gray-500')}>
|
||||||
className={clsx(
|
|
||||||
'ml-2 items-center gap-2 text-xs text-gray-500 transition-opacity',
|
|
||||||
showActions ? '' : 'md:opacity-0'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{onReplyClick && (
|
{onReplyClick && (
|
||||||
<Button
|
<Button
|
||||||
className="font-bold hover:underline"
|
className="font-bold hover:underline"
|
||||||
|
@ -377,3 +367,42 @@ export function FeedCommentHeader(props: {
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AnswerCommentInput(props: {
|
||||||
|
contract: Contract<AnyContractType>
|
||||||
|
answerResponse: Answer
|
||||||
|
onCancelAnswerResponse?: () => void
|
||||||
|
}) {
|
||||||
|
const { contract, answerResponse, onCancelAnswerResponse } = props
|
||||||
|
const [replyTo, setReplyTo] = useState<ReplyTo | undefined>({
|
||||||
|
id: answerResponse.id,
|
||||||
|
username: answerResponse.username,
|
||||||
|
})
|
||||||
|
const onSubmitComment = useEvent(() => {
|
||||||
|
setReplyTo(undefined)
|
||||||
|
onCancelAnswerResponse
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row className="gap-2">
|
||||||
|
<CommentsAnswer answer={answerResponse} contract={contract} />
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<div className="ml-1">
|
||||||
|
<Curve size={28} strokeWidth={1} color="#D8D8EB" />
|
||||||
|
</div>
|
||||||
|
<div className="w-full pt-1">
|
||||||
|
<ContractCommentInput
|
||||||
|
contract={contract}
|
||||||
|
parentAnswerOutcome={answerResponse.number.toString()}
|
||||||
|
replyTo={replyTo}
|
||||||
|
onSubmitComment={onSubmitComment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button onClick={onCancelAnswerResponse}>
|
||||||
|
<XIcon className="h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ import { BetsSummary } from 'web/components/bet-summary'
|
||||||
import { listAllComments } from 'web/lib/firebase/comments'
|
import { listAllComments } from 'web/lib/firebase/comments'
|
||||||
import { ContractComment } from 'common/comment'
|
import { ContractComment } from 'common/comment'
|
||||||
import { ScrollToTopButton } from 'web/components/scroll-to-top-button'
|
import { ScrollToTopButton } from 'web/components/scroll-to-top-button'
|
||||||
|
import { Answer } from 'common/answer'
|
||||||
|
import { useEvent } from 'web/hooks/use-event'
|
||||||
|
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz(props: {
|
export async function getStaticPropz(props: {
|
||||||
|
@ -204,6 +206,16 @@ export function ContractPageContent(
|
||||||
contractId: contract.id,
|
contractId: contract.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [answerResponse, setAnswerResponse] = useState<Answer | undefined>(
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
const onAnswerCommentClick = useEvent((answer: Answer) => {
|
||||||
|
setAnswerResponse(answer)
|
||||||
|
})
|
||||||
|
|
||||||
|
const onCancelAnswerResponse = useEvent(() => setAnswerResponse(undefined))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
rightSidebar={
|
rightSidebar={
|
||||||
|
@ -253,7 +265,10 @@ export function ContractPageContent(
|
||||||
outcomeType === 'MULTIPLE_CHOICE') && (
|
outcomeType === 'MULTIPLE_CHOICE') && (
|
||||||
<>
|
<>
|
||||||
<Spacer h={4} />
|
<Spacer h={4} />
|
||||||
<AnswersPanel contract={contract} />
|
<AnswersPanel
|
||||||
|
contract={contract}
|
||||||
|
onAnswerCommentClick={onAnswerCommentClick}
|
||||||
|
/>
|
||||||
<Spacer h={4} />
|
<Spacer h={4} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -283,6 +298,8 @@ export function ContractPageContent(
|
||||||
bets={bets}
|
bets={bets}
|
||||||
userBets={userBets}
|
userBets={userBets}
|
||||||
comments={comments}
|
comments={comments}
|
||||||
|
answerResponse={answerResponse}
|
||||||
|
onCancelAnswerResponse={onCancelAnswerResponse}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
{!isCreator && <RecommendedContractsWidget contract={contract} />}
|
{!isCreator && <RecommendedContractsWidget contract={contract} />}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user