import React, { memo, useEffect, useRef, useState } from 'react' import { Editor } from '@tiptap/react' import { useRouter } from 'next/router' import { sum } from 'lodash' import clsx from 'clsx' import { ContractComment } from 'common/comment' import { Contract } from 'common/contract' import { useUser } from 'web/hooks/use-user' import { formatMoney } from 'common/util/format' import { Row } from 'web/components/layout/row' import { Avatar } from 'web/components/avatar' import { OutcomeLabel } from 'web/components/outcome-label' import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time' import { firebaseLogin } from 'web/lib/firebase/users' import { createCommentOnContract } from 'web/lib/firebase/comments' import { Col } from 'web/components/layout/col' import { track } from 'web/lib/service/analytics' import { Tipper } from '../tipper' import { CommentTipMap } from 'web/hooks/use-tip-txns' import { useEvent } from 'web/hooks/use-event' import { Content } from '../editor' import { UserLink } from 'web/components/user-link' import { CommentInput } from '../comment-input' import { AwardBountyButton } from 'web/components/award-bounty-button' import { ReplyIcon } from '@heroicons/react/solid' import { Button } from '../button' import { ReplyToggle } from '../comments/reply-toggle' export type ReplyTo = { id: string; username: string } export function FeedCommentThread(props: { contract: Contract threadComments: ContractComment[] tips: CommentTipMap parentComment: ContractComment }) { const { contract, threadComments, tips, parentComment } = props const [replyTo, setReplyTo] = useState() const [seeReplies, setSeeReplies] = useState(false) const user = useUser() const onSubmitComment = useEvent(() => setReplyTo(undefined)) const onReplyClick = useEvent((comment: ContractComment) => { setReplyTo({ id: comment.id, username: comment.userUsername }) }) return ( setSeeReplies(!seeReplies)} onReplyClick={() => setReplyTo({ id: parentComment.id, username: parentComment.userUsername, }) } /> {seeReplies && threadComments.map((comment, _commentIdx) => ( ))} {replyTo && ( )} ) } export function ParentFeedComment(props: { contract: Contract comment: ContractComment showTip?: boolean myTip?: number totalTip?: number seeReplies: boolean numComments: number onReplyClick?: (comment: ContractComment) => void onSeeReplyClick: () => void }) { const { contract, comment, myTip, totalTip, showTip, onReplyClick, onSeeReplyClick, seeReplies, numComments, } = props const { text, content, userUsername, userAvatarUrl } = comment const { isReady, asPath } = useRouter() const [highlighted, setHighlighted] = useState(false) const commentRef = useRef(null) useEffect(() => { if (isReady && asPath.endsWith(`#${comment.id}`)) { setHighlighted(true) } }, [isReady, asPath, comment.id]) useEffect(() => { if (highlighted && commentRef.current) { commentRef.current.scrollIntoView(true) } }, [highlighted]) return ( {onReplyClick && ( )} {showTip && ( )} {(contract.openCommentBounties ?? 0) > 0 && ( )} ) } export const FeedComment = memo(function FeedComment(props: { contract: Contract comment: ContractComment showTip?: boolean myTip?: number totalTip?: number onReplyClick?: (comment: ContractComment) => void }) { const { contract, comment, myTip, totalTip, showTip, onReplyClick } = props const { text, content, userUsername, userAvatarUrl } = comment const { isReady, asPath } = useRouter() const [highlighted, setHighlighted] = useState(false) const commentRef = useRef(null) useEffect(() => { if (isReady && asPath.endsWith(`#${comment.id}`)) { setHighlighted(true) } }, [isReady, asPath, comment.id]) useEffect(() => { if (highlighted && commentRef.current) { commentRef.current.scrollIntoView(true) } }, [highlighted]) return ( ) }) function CommentStatus(props: { contract: Contract outcome: string prob?: number }) { const { contract, outcome, prob } = props return ( <> {` predicting `} {prob && ' at ' + Math.round(prob * 100) + '%'} ) } export function ContractCommentInput(props: { contract: Contract className?: string parentAnswerOutcome?: string | undefined replyTo?: ReplyTo parentCommentId?: string onSubmitComment?: () => void }) { const user = useUser() const { contract, parentAnswerOutcome, parentCommentId, replyTo, className } = props const { openCommentBounties } = contract async function onSubmitComment(editor: Editor) { if (!user) { track('sign in to comment') return await firebaseLogin() } await createCommentOnContract( contract.id, editor.getJSON(), user, !!openCommentBounties, parentAnswerOutcome, parentCommentId ) props.onSubmitComment?.() } return ( ) } export function FeedCommentHeader(props: { comment: ContractComment contract: Contract }) { const { comment, contract } = props const { userUsername, userName, commenterPositionProb, commenterPositionShares, commenterPositionOutcome, createdTime, bountiesAwarded, } = comment const betOutcome = comment.betOutcome let bought: string | undefined let money: string | undefined if (comment.betAmount != null) { bought = comment.betAmount >= 0 ? 'bought' : 'sold' money = formatMoney(Math.abs(comment.betAmount)) } const totalAwarded = bountiesAwarded ?? 0 return (
{' '} {comment.betId == null && commenterPositionProb != null && commenterPositionOutcome != null && commenterPositionShares != null && commenterPositionShares > 0 && contract.outcomeType !== 'NUMERIC' && ( <> {'is '} )} {bought} {money} {contract.outcomeType !== 'FREE_RESPONSE' && betOutcome && ( <> {' '} of{' '} )} {totalAwarded > 0 && ( +{formatMoney(totalAwarded)} )}
) }