incorporating answer replies into general comments section

This commit is contained in:
ingawei 2022-10-06 17:37:41 -07:00
parent d685f0288b
commit 7e29733144
4 changed files with 195 additions and 111 deletions

View File

@ -9,7 +9,13 @@ export function ReplyToggle(props: {
}) { }) {
const { seeReplies, numComments, onClick } = props const { seeReplies, numComments, onClick } = props
return ( 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"> <Row className="items-center gap-1">
<div> <div>
{numComments} {numComments === 1 ? 'Reply' : 'Replies'} {numComments} {numComments === 1 ? 'Reply' : 'Replies'}

View File

@ -37,6 +37,13 @@ import {
} from 'web/hooks/use-persistent-state' } from 'web/hooks/use-persistent-state'
import { safeLocalStorage } from 'web/lib/util/local' import { safeLocalStorage } from 'web/lib/util/local'
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon' 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: { export function ContractTabs(props: {
contract: Contract contract: Contract
@ -149,58 +156,98 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
</Row> </Row>
) )
// if (contract.outcomeType === 'FREE_RESPONSE') { if (contract.outcomeType === 'FREE_RESPONSE') {
// const sortedAnswers = sortBy( const sortedAnswers = sortBy(
// contract.answers, contract.answers,
// (a) => -getOutcomeProbability(contract, a.id) (a) => -getOutcomeProbability(contract, a.id)
// ) )
// const commentsByOutcome = groupBy( const commentsByOutcome = groupBy(
// sortedComments, sortedComments,
// (c) => c.answerOutcome ?? c.betOutcome ?? '_' (c) => c.answerOutcome ?? c.betOutcome ?? '_'
// ) )
// const generalTopLevelComments = topLevelComments.filter( // const generalTopLevelComments = topLevelComments.filter(
// (c) => c.answerOutcome === undefined && c.betId === undefined // (c) => c.answerOutcome === undefined && c.betId === undefined
// ) // )
// console.log('answer: ', sortedAnswers)
// return ( // console.log('comments by outcome:', commentsByOutcome)
// <> return (
// <Col className="flex w-full"> <>
// <div className="mb-4 w-full border-gray-200" /> {sortRow}
// {sortedAnswers.map((answer) => { <ContractCommentInput className="mb-5" contract={contract} />
// const answerComments = {topLevelComments.map((parent) => {
// commentsByOutcome[answer.number.toString()] ?? [] if (parent.answerOutcome != undefined) {
// if (answerComments.length > 0) { const answer = sortedAnswers.find(
// return ( (answer) => answer.id === parent.answerOutcome
// <div key={answer.id} className="relative pb-4"> )
// <FeedAnswerCommentGroup if (answer === undefined) {
// contract={contract} console.error('Could not find answer that matches ID')
// answer={answer} return <></>
// answerComments={ } else {
// commentsByOutcome[answer.number.toString()] ?? [] const { username, avatarUrl, name, text } = answer
// } const answerElementId = `answer-${answer.id}`
// tips={tips} return (
// /> <>
// </div> <Row className="bg-greyscale-2 w-fit gap-1 rounded-t-xl rounded-bl-xl px-2 py-2">
// ) <div className="ml-2">
// } else { <Avatar
// return <></> username={username}
// } avatarUrl={avatarUrl}
// })} size="xxs"
// <ContractCommentInput className="mb-5" contract={contract} /> />
// {sortRow} </div>
// {generalTopLevelComments.map((comment) => ( <Col>
// <FeedCommentThread <Row className="gap-1">
// key={comment.id} <div className="text-greyscale-6 text-xs">
// contract={contract} <UserLink username={username} name={name} /> answered
// parentComment={comment} <CopyLinkDateTimeComponent
// threadComments={commentsByParent[comment.id] ?? []} prefix={contract.creatorUsername}
// tips={tips} slug={contract.slug}
// /> createdTime={answer.createdTime}
// ))} elementId={answerElementId}
// </Col> />
// </> </div>
// ) </Row>
// } else { <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 ( return (
<> <>
{sortRow} {sortRow}
@ -219,7 +266,7 @@ const CommentsTabContent = memo(function CommentsTabContent(props: {
))} ))}
</> </>
) )
// } }
}) })
const BetsTabContent = memo(function BetsTabContent(props: { const BetsTabContent = memo(function BetsTabContent(props: {

View File

@ -18,6 +18,7 @@ import { CommentTipMap } from 'web/hooks/use-tip-txns'
import { UserLink } from 'web/components/user-link' import { UserLink } from 'web/components/user-link'
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon' import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
import { ReplyToggle } from '../comments/comments' import { ReplyToggle } from '../comments/comments'
import { ReplyIcon } from '@heroicons/react/solid'
export function FeedAnswerCommentGroup(props: { export function FeedAnswerCommentGroup(props: {
contract: FreeResponseContract contract: FreeResponseContract
@ -62,11 +63,11 @@ export function FeedAnswerCommentGroup(props: {
elementId={answerElementId} elementId={answerElementId}
/> />
</div> </div>
<Row className="align-items justify-between gap-2 sm:flex-row"> {/* <Row className="align-items justify-between gap-2 sm:flex-row"> */}
<span className="text-md whitespace-pre-line"> <span className="text-md whitespace-pre-line">
<Linkify text={text} /> <Linkify text={text} />
</span> </span>
<div> {/* <div>
<button <button
className="text-xs font-bold text-gray-500 hover:underline" className="text-xs font-bold text-gray-500 hover:underline"
onClick={() => onClick={() =>
@ -75,13 +76,25 @@ export function FeedAnswerCommentGroup(props: {
> >
Reply Reply
</button> </button>
</div> </div> */}
</Row> {/* </Row> */}
<Row className="w-full">
<ReplyToggle <ReplyToggle
seeReplies={seeReplies} seeReplies={seeReplies}
numComments={answerComments.length} numComments={answerComments.length}
onClick={() => setSeeReplies(!seeReplies)} 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>
</Col> </Col>
</Row> </Row>
{seeReplies && ( {seeReplies && (

View File

@ -1,5 +1,5 @@
import { ContractComment } from 'common/comment' import { ContractComment } from 'common/comment'
import { Contract } from 'common/contract' import { AnyContractType, Contract } from 'common/contract'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { useUser } from 'web/hooks/use-user' import { useUser } from 'web/hooks/use-user'
import { formatMoney } from 'common/util/format' import { formatMoney } from 'common/util/format'
@ -115,16 +115,37 @@ export function FeedComment(props: {
</Col> </Col>
<Col className="w-full"> <Col className="w-full">
<FeedCommentHeader comment={comment} contract={contract} /> <FeedCommentHeader comment={comment} contract={contract} />
<Row> {/* TODO: bug where if this is iFrame, it does not scroll */}
<Content <Content
className="text-greyscale-7 mt-2 grow text-[14px]" className="text-greyscale-7 mt-2 grow text-[14px]"
content={content || text} content={content || text}
smallImage 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 <Row
className={clsx( className={clsx(
'ml-2 items-center gap-2 text-xs text-gray-500 transition-opacity', 'ml-2 items-center justify-end gap-2 text-xs text-gray-500 transition-opacity',
showActions ? '' : 'opacity-0' showActions ? '' : 'md:opacity-0'
)} )}
> >
{onReplyClick && ( {onReplyClick && (
@ -142,9 +163,6 @@ export function FeedComment(props: {
<AwardBountyButton comment={comment} contract={contract} /> <AwardBountyButton comment={comment} contract={contract} />
)} )}
</Row> </Row>
</Row>
</Col>
</Row>
) )
} }