incorporating answer replies into general comments section
This commit is contained in:
parent
d685f0288b
commit
7e29733144
|
@ -9,7 +9,13 @@ export function ReplyToggle(props: {
|
|||
}) {
|
||||
const { seeReplies, numComments, onClick } = props
|
||||
return (
|
||||
<button className="text-left text-sm text-indigo-600" onClick={onClick}>
|
||||
<button
|
||||
className={clsx(
|
||||
'text-left text-sm text-indigo-600',
|
||||
numComments === 0 ? 'hidden' : ''
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Row className="items-center gap-1">
|
||||
<div>
|
||||
{numComments} {numComments === 1 ? 'Reply' : 'Replies'}
|
||||
|
|
|
@ -37,6 +37,13 @@ import {
|
|||
} from 'web/hooks/use-persistent-state'
|
||||
import { safeLocalStorage } from 'web/lib/util/local'
|
||||
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
||||
import { connectStorageEmulator } from 'firebase/storage'
|
||||
import { Avatar } from '../avatar'
|
||||
import { UserLink } from '../user-link'
|
||||
import { CopyLinkDateTimeComponent } from '../feed/copy-link-date-time'
|
||||
import { Linkify } from '../linkify'
|
||||
import { ArrowRightIcon } from '@heroicons/react/solid'
|
||||
import Curve from 'web/public/custom-components/curve'
|
||||
|
||||
export function ContractTabs(props: {
|
||||
contract: Contract
|
||||
|
@ -149,77 +156,117 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
|
|||
</Row>
|
||||
)
|
||||
|
||||
// if (contract.outcomeType === 'FREE_RESPONSE') {
|
||||
// const sortedAnswers = sortBy(
|
||||
// contract.answers,
|
||||
// (a) => -getOutcomeProbability(contract, a.id)
|
||||
// )
|
||||
// const commentsByOutcome = groupBy(
|
||||
// sortedComments,
|
||||
// (c) => c.answerOutcome ?? c.betOutcome ?? '_'
|
||||
// )
|
||||
// const generalTopLevelComments = topLevelComments.filter(
|
||||
// (c) => c.answerOutcome === undefined && c.betId === undefined
|
||||
// )
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <Col className="flex w-full">
|
||||
// <div className="mb-4 w-full border-gray-200" />
|
||||
// {sortedAnswers.map((answer) => {
|
||||
// const answerComments =
|
||||
// commentsByOutcome[answer.number.toString()] ?? []
|
||||
// if (answerComments.length > 0) {
|
||||
// return (
|
||||
// <div key={answer.id} className="relative pb-4">
|
||||
// <FeedAnswerCommentGroup
|
||||
// contract={contract}
|
||||
// answer={answer}
|
||||
// answerComments={
|
||||
// commentsByOutcome[answer.number.toString()] ?? []
|
||||
// }
|
||||
// tips={tips}
|
||||
// />
|
||||
// </div>
|
||||
// )
|
||||
// } else {
|
||||
// return <></>
|
||||
// }
|
||||
// })}
|
||||
// <ContractCommentInput className="mb-5" contract={contract} />
|
||||
// {sortRow}
|
||||
// {generalTopLevelComments.map((comment) => (
|
||||
// <FeedCommentThread
|
||||
// key={comment.id}
|
||||
// contract={contract}
|
||||
// parentComment={comment}
|
||||
// threadComments={commentsByParent[comment.id] ?? []}
|
||||
// tips={tips}
|
||||
// />
|
||||
// ))}
|
||||
// </Col>
|
||||
// </>
|
||||
// )
|
||||
// } else {
|
||||
return (
|
||||
<>
|
||||
{sortRow}
|
||||
<ContractCommentInput className="mb-5" contract={contract} />
|
||||
{topLevelComments.map((parent) => (
|
||||
<FeedCommentThread
|
||||
key={parent.id}
|
||||
contract={contract}
|
||||
parentComment={parent}
|
||||
threadComments={sortBy(
|
||||
commentsByParent[parent.id] ?? [],
|
||||
(c) => c.createdTime
|
||||
)}
|
||||
tips={tips}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
// }
|
||||
if (contract.outcomeType === 'FREE_RESPONSE') {
|
||||
const sortedAnswers = sortBy(
|
||||
contract.answers,
|
||||
(a) => -getOutcomeProbability(contract, a.id)
|
||||
)
|
||||
const commentsByOutcome = groupBy(
|
||||
sortedComments,
|
||||
(c) => c.answerOutcome ?? c.betOutcome ?? '_'
|
||||
)
|
||||
// const generalTopLevelComments = topLevelComments.filter(
|
||||
// (c) => c.answerOutcome === undefined && c.betId === undefined
|
||||
// )
|
||||
// console.log('answer: ', sortedAnswers)
|
||||
// console.log('comments by outcome:', commentsByOutcome)
|
||||
return (
|
||||
<>
|
||||
{sortRow}
|
||||
<ContractCommentInput className="mb-5" contract={contract} />
|
||||
{topLevelComments.map((parent) => {
|
||||
if (parent.answerOutcome != undefined) {
|
||||
const answer = sortedAnswers.find(
|
||||
(answer) => answer.id === parent.answerOutcome
|
||||
)
|
||||
if (answer === undefined) {
|
||||
console.error('Could not find answer that matches ID')
|
||||
return <></>
|
||||
} else {
|
||||
const { username, avatarUrl, name, text } = answer
|
||||
const answerElementId = `answer-${answer.id}`
|
||||
return (
|
||||
<>
|
||||
<Row className="bg-greyscale-2 w-fit gap-1 rounded-t-xl rounded-bl-xl px-2 py-2">
|
||||
<div className="ml-2">
|
||||
<Avatar
|
||||
username={username}
|
||||
avatarUrl={avatarUrl}
|
||||
size="xxs"
|
||||
/>
|
||||
</div>
|
||||
<Col>
|
||||
<Row className="gap-1">
|
||||
<div className="text-greyscale-6 text-xs">
|
||||
<UserLink username={username} name={name} /> answered
|
||||
<CopyLinkDateTimeComponent
|
||||
prefix={contract.creatorUsername}
|
||||
slug={contract.slug}
|
||||
createdTime={answer.createdTime}
|
||||
elementId={answerElementId}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
<div className="text-greyscale-7 text-sm">{text}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<div className="ml-2">
|
||||
<Curve size={28} strokeWidth={1} color="#B1B1C7" />
|
||||
</div>
|
||||
<div className="w-full pt-1">
|
||||
<FeedCommentThread
|
||||
key={parent.id}
|
||||
contract={contract}
|
||||
parentComment={parent}
|
||||
threadComments={sortBy(
|
||||
commentsByParent[parent.id] ?? [],
|
||||
(c) => c.createdTime
|
||||
)}
|
||||
tips={tips}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
</>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<FeedCommentThread
|
||||
key={parent.id}
|
||||
contract={contract}
|
||||
parentComment={parent}
|
||||
threadComments={sortBy(
|
||||
commentsByParent[parent.id] ?? [],
|
||||
(c) => c.createdTime
|
||||
)}
|
||||
tips={tips}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{sortRow}
|
||||
<ContractCommentInput className="mb-5" contract={contract} />
|
||||
{topLevelComments.map((parent) => (
|
||||
<FeedCommentThread
|
||||
key={parent.id}
|
||||
contract={contract}
|
||||
parentComment={parent}
|
||||
threadComments={sortBy(
|
||||
commentsByParent[parent.id] ?? [],
|
||||
(c) => c.createdTime
|
||||
)}
|
||||
tips={tips}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const BetsTabContent = memo(function BetsTabContent(props: {
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CommentTipMap } from 'web/hooks/use-tip-txns'
|
|||
import { UserLink } from 'web/components/user-link'
|
||||
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
||||
import { ReplyToggle } from '../comments/comments'
|
||||
import { ReplyIcon } from '@heroicons/react/solid'
|
||||
|
||||
export function FeedAnswerCommentGroup(props: {
|
||||
contract: FreeResponseContract
|
||||
|
@ -62,11 +63,11 @@ export function FeedAnswerCommentGroup(props: {
|
|||
elementId={answerElementId}
|
||||
/>
|
||||
</div>
|
||||
<Row className="align-items justify-between gap-2 sm:flex-row">
|
||||
<span className="text-md whitespace-pre-line">
|
||||
<Linkify text={text} />
|
||||
</span>
|
||||
<div>
|
||||
{/* <Row className="align-items justify-between gap-2 sm:flex-row"> */}
|
||||
<span className="text-md whitespace-pre-line">
|
||||
<Linkify text={text} />
|
||||
</span>
|
||||
{/* <div>
|
||||
<button
|
||||
className="text-xs font-bold text-gray-500 hover:underline"
|
||||
onClick={() =>
|
||||
|
@ -75,13 +76,25 @@ export function FeedAnswerCommentGroup(props: {
|
|||
>
|
||||
Reply
|
||||
</button>
|
||||
</div> */}
|
||||
{/* </Row> */}
|
||||
<Row className="w-full">
|
||||
<ReplyToggle
|
||||
seeReplies={seeReplies}
|
||||
numComments={answerComments.length}
|
||||
onClick={() => setSeeReplies(!seeReplies)}
|
||||
/>
|
||||
<div className="justify-self-end">
|
||||
<button
|
||||
className="text-greyscale-5"
|
||||
onClick={() =>
|
||||
setReplyTo({ id: answer.id, username: answer.username })
|
||||
}
|
||||
>
|
||||
<ReplyIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</Row>
|
||||
<ReplyToggle
|
||||
seeReplies={seeReplies}
|
||||
numComments={answerComments.length}
|
||||
onClick={() => setSeeReplies(!seeReplies)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
{seeReplies && (
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ContractComment } from 'common/comment'
|
||||
import { Contract } from 'common/contract'
|
||||
import { AnyContractType, Contract } from 'common/contract'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
|
@ -115,39 +115,57 @@ export function FeedComment(props: {
|
|||
</Col>
|
||||
<Col className="w-full">
|
||||
<FeedCommentHeader comment={comment} contract={contract} />
|
||||
<Row>
|
||||
<Content
|
||||
className="text-greyscale-7 mt-2 grow text-[14px]"
|
||||
content={content || text}
|
||||
smallImage
|
||||
/>
|
||||
<Row
|
||||
className={clsx(
|
||||
'ml-2 items-center gap-2 text-xs text-gray-500 transition-opacity',
|
||||
showActions ? '' : 'opacity-0'
|
||||
)}
|
||||
>
|
||||
{onReplyClick && (
|
||||
<Button
|
||||
className="font-bold hover:underline"
|
||||
onClick={onReplyClick}
|
||||
size="2xs"
|
||||
color="gray-white"
|
||||
>
|
||||
<ReplyIcon className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
{tips && <Tipper comment={comment} tips={tips} />}
|
||||
{(contract.openCommentBounties ?? 0) > 0 && (
|
||||
<AwardBountyButton comment={comment} contract={contract} />
|
||||
)}
|
||||
</Row>
|
||||
</Row>
|
||||
{/* TODO: bug where if this is iFrame, it does not scroll */}
|
||||
<Content
|
||||
className="text-greyscale-7 mt-2 grow text-[14px]"
|
||||
content={content || text}
|
||||
smallImage
|
||||
/>
|
||||
<CommentActions
|
||||
showActions={showActions}
|
||||
onReplyClick={onReplyClick}
|
||||
tips={tips}
|
||||
comment={comment}
|
||||
contract={contract}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export function CommentActions(props: {
|
||||
showActions: boolean
|
||||
onReplyClick?: () => void
|
||||
tips?: CommentTips | undefined
|
||||
comment: ContractComment
|
||||
contract: Contract<AnyContractType>
|
||||
}) {
|
||||
const { showActions, onReplyClick, tips, comment, contract } = props
|
||||
return (
|
||||
<Row
|
||||
className={clsx(
|
||||
'ml-2 items-center justify-end gap-2 text-xs text-gray-500 transition-opacity',
|
||||
showActions ? '' : 'md:opacity-0'
|
||||
)}
|
||||
>
|
||||
{onReplyClick && (
|
||||
<Button
|
||||
className="font-bold hover:underline"
|
||||
onClick={onReplyClick}
|
||||
size="2xs"
|
||||
color="gray-white"
|
||||
>
|
||||
<ReplyIcon className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
{tips && <Tipper comment={comment} tips={tips} />}
|
||||
{(contract.openCommentBounties ?? 0) > 0 && (
|
||||
<AwardBountyButton comment={comment} contract={contract} />
|
||||
)}
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
function CommentStatus(props: {
|
||||
contract: Contract
|
||||
outcome: string
|
||||
|
|
Loading…
Reference in New Issue
Block a user