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,77 +156,117 @@ 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>
return ( </Col>
<> </Row>
{sortRow} <Row>
<ContractCommentInput className="mb-5" contract={contract} /> <div className="ml-2">
{topLevelComments.map((parent) => ( <Curve size={28} strokeWidth={1} color="#B1B1C7" />
<FeedCommentThread </div>
key={parent.id} <div className="w-full pt-1">
contract={contract} <FeedCommentThread
parentComment={parent} key={parent.id}
threadComments={sortBy( contract={contract}
commentsByParent[parent.id] ?? [], parentComment={parent}
(c) => c.createdTime threadComments={sortBy(
)} commentsByParent[parent.id] ?? [],
tips={tips} (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: { 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> */}
{/* </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> </div>
</Row> </Row>
<ReplyToggle
seeReplies={seeReplies}
numComments={answerComments.length}
onClick={() => setSeeReplies(!seeReplies)}
/>
</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,39 +115,57 @@ 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
/> />
<Row <CommentActions
className={clsx( showActions={showActions}
'ml-2 items-center gap-2 text-xs text-gray-500 transition-opacity', onReplyClick={onReplyClick}
showActions ? '' : 'opacity-0' tips={tips}
)} comment={comment}
> contract={contract}
{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>
</Col> </Col>
</Row> </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: { function CommentStatus(props: {
contract: Contract contract: Contract
outcome: string outcome: string