adding replies toggle, filtering out responses that don't have replies

This commit is contained in:
ingawei 2022-10-04 20:33:26 -07:00
parent c115b5cca7
commit 8dd5b477d9
3 changed files with 99 additions and 65 deletions

View File

@ -0,0 +1,23 @@
import clsx from 'clsx'
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
import { Row } from '../layout/row'
export function ReplyToggle(props: {
seeReplies: boolean
numComments: number
onClick: () => void
}) {
const { seeReplies, numComments, onClick } = props
return (
<button className="text-left text-sm text-indigo-600" onClick={onClick}>
<Row className="items-center gap-1">
<div>
{numComments} {numComments === 1 ? 'Reply' : 'Replies'}
</div>
<TriangleDownFillIcon
className={clsx('h-2 w-2', seeReplies ? 'rotate-180' : '')}
/>
</Row>
</button>
)
}

View File

@ -36,6 +36,7 @@ import {
usePersistentState,
} from 'web/hooks/use-persistent-state'
import { safeLocalStorage } from 'web/lib/util/local'
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
export function ContractTabs(props: {
contract: Contract
@ -123,24 +124,28 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
const topLevelComments = commentsByParent['_'] ?? []
const sortRow = comments.length > 0 && (
<Row className="mb-4 items-center">
<Button
size={'xs'}
color={'gray-white'}
onClick={() => setSort(sort === 'Newest' ? 'Best' : 'Newest')}
>
<Tooltip
text={
sort === 'Best'
? 'Highest tips + bounties first. Your new comments briefly appear to you first.'
: ''
}
>
Sort by: {sort}
</Tooltip>
</Button>
<Row className="mb-4 items-center justify-end gap-4">
<BountiedContractSmallBadge contract={contract} showAmount />
<Row className="items-center gap-1">
<div className="text-greyscale-4 text-sm">Sort by:</div>
<button
className="text-greyscale-6 w-20 text-sm"
onClick={() => setSort(sort === 'Newest' ? 'Best' : 'Newest')}
>
<Tooltip
text={
sort === 'Best'
? 'Highest tips + bounties first. Your new comments briefly appear to you first.'
: ''
}
>
<Row className="items-center gap-1">
{sort}
<TriangleDownFillIcon className=" h-2 w-2" />
</Row>
</Tooltip>
</button>
</Row>
</Row>
)
@ -159,24 +164,32 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
return (
<>
{sortRow}
{sortedAnswers.map((answer) => (
<div key={answer.id} className="relative pb-4">
<span
className="absolute top-5 left-5 -ml-px h-[calc(100%-2rem)] w-0.5 bg-gray-200"
aria-hidden="true"
/>
<FeedAnswerCommentGroup
contract={contract}
answer={answer}
answerComments={commentsByOutcome[answer.number.toString()] ?? []}
tips={tips}
/>
</div>
))}
<Col className="mt-8 flex w-full">
<div className="text-md mt-8 mb-2 text-left">General Comments</div>
<div className="mb-4 w-full border-b border-gray-200" />
<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">
<span
className="absolute top-5 left-5 -ml-px h-[calc(100%-2rem)] w-0.5 bg-gray-200"
aria-hidden="true"
/>
<FeedAnswerCommentGroup
contract={contract}
answer={answer}
answerComments={
commentsByOutcome[answer.number.toString()] ?? []
}
tips={tips}
/>
</div>
)
} else {
return <></>
}
})}
{sortRow}
<ContractCommentInput className="mb-5" contract={contract} />
{generalTopLevelComments.map((comment) => (

View File

@ -16,6 +16,8 @@ import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-ti
import { useRouter } from 'next/router'
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'
export function FeedAnswerCommentGroup(props: {
contract: FreeResponseContract
@ -26,6 +28,8 @@ export function FeedAnswerCommentGroup(props: {
const { answer, contract, answerComments, tips } = props
const { username, avatarUrl, name, text } = answer
const [seeReplies, setSeeReplies] = useState(false)
const [replyTo, setReplyTo] = useState<ReplyTo>()
const router = useRouter()
const answerElementId = `answer-${answer.id}`
@ -37,7 +41,6 @@ export function FeedAnswerCommentGroup(props: {
answerRef.current.scrollIntoView(true)
}
}, [highlighted])
return (
<Col className="relative flex-1 items-stretch gap-3">
<Row
@ -49,7 +52,6 @@ export function FeedAnswerCommentGroup(props: {
id={answerElementId}
>
<Avatar username={username} avatarUrl={avatarUrl} />
<Col className="min-w-0 flex-1 lg:gap-1">
<div className="text-sm text-gray-500">
<UserLink username={username} name={name} /> answered
@ -60,12 +62,11 @@ export function FeedAnswerCommentGroup(props: {
elementId={answerElementId}
/>
</div>
<Col className="align-items justify-between gap-2 sm:flex-row">
<Row className="align-items justify-between gap-2 sm:flex-row">
<span className="whitespace-pre-line text-lg">
<Linkify text={text} />
</span>
<div className="sm:hidden">
<div>
<button
className="text-xs font-bold text-gray-500 hover:underline"
onClick={() =>
@ -75,33 +76,30 @@ export function FeedAnswerCommentGroup(props: {
Reply
</button>
</div>
</Col>
<div className="justify-initial hidden sm:block">
<button
className="text-xs font-bold text-gray-500 hover:underline"
onClick={() =>
setReplyTo({ id: answer.id, username: answer.username })
}
>
Reply
</button>
</div>
</Row>
<ReplyToggle
seeReplies={seeReplies}
numComments={answerComments.length}
onClick={() => setSeeReplies(!seeReplies)}
/>
</Col>
</Row>
<Col className="gap-3 pl-1">
{answerComments.map((comment) => (
<FeedComment
key={comment.id}
indent={true}
contract={contract}
comment={comment}
tips={tips[comment.id] ?? {}}
onReplyClick={() =>
setReplyTo({ id: comment.id, username: comment.userUsername })
}
/>
))}
</Col>
{seeReplies && (
<Col className="gap-3 pl-1">
{answerComments.map((comment) => (
<FeedComment
key={comment.id}
indent={true}
contract={contract}
comment={comment}
tips={tips[comment.id] ?? {}}
onReplyClick={() =>
setReplyTo({ id: comment.id, username: comment.userUsername })
}
/>
))}
</Col>
)}
{replyTo && (
<div className="relative ml-7">
<span