From ddb186dd980d1ec35c662fccdd3ec4c0b9c012df Mon Sep 17 00:00:00 2001 From: Marshall Polaris Date: Tue, 4 Oct 2022 23:16:56 -0700 Subject: [PATCH] Change `Tipper` interface, memoize `FeedComment` (#1000) * Change `Tipper` interface, memoize `FeedComment` * Fixup per James feedback * More fixup per James feedback --- .../feed/feed-answer-comment-group.tsx | 19 +++++-- web/components/feed/feed-comments.tsx | 52 +++++++++++++------ web/components/tipper.tsx | 25 ++++----- web/posts/post-comments.tsx | 8 ++- 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/web/components/feed/feed-answer-comment-group.tsx b/web/components/feed/feed-answer-comment-group.tsx index e17ea578..11bc6139 100644 --- a/web/components/feed/feed-answer-comment-group.tsx +++ b/web/components/feed/feed-answer-comment-group.tsx @@ -2,6 +2,7 @@ import { Answer } from 'common/answer' import { FreeResponseContract } from 'common/contract' import { ContractComment } from 'common/comment' import React, { useEffect, useRef, useState } from 'react' +import { sum } from 'lodash' import { Col } from 'web/components/layout/col' import { Row } from 'web/components/layout/row' import { Avatar } from 'web/components/avatar' @@ -14,6 +15,8 @@ import { } from 'web/components/feed/feed-comments' import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time' import { useRouter } from 'next/router' +import { useUser } from 'web/hooks/use-user' +import { useEvent } from 'web/hooks/use-event' import { CommentTipMap } from 'web/hooks/use-tip-txns' import { UserLink } from 'web/components/user-link' @@ -27,11 +30,17 @@ export function FeedAnswerCommentGroup(props: { const { username, avatarUrl, name, text } = answer const [replyTo, setReplyTo] = useState() + const user = useUser() const router = useRouter() const answerElementId = `answer-${answer.id}` const highlighted = router.asPath.endsWith(`#${answerElementId}`) const answerRef = useRef(null) + const onSubmitComment = useEvent(() => setReplyTo(undefined)) + const onReplyClick = useEvent((comment: ContractComment) => { + setReplyTo({ id: comment.id, username: comment.userUsername }) + }) + useEffect(() => { if (highlighted && answerRef.current != null) { answerRef.current.scrollIntoView(true) @@ -95,10 +104,10 @@ export function FeedAnswerCommentGroup(props: { indent={true} contract={contract} comment={comment} - tips={tips[comment.id] ?? {}} - onReplyClick={() => - setReplyTo({ id: comment.id, username: comment.userUsername }) - } + myTip={user ? tips[comment.id]?.[user.id] : undefined} + totalTip={sum(Object.values(tips[comment.id] ?? {}))} + showTip={true} + onReplyClick={onReplyClick} /> ))} @@ -112,7 +121,7 @@ export function FeedAnswerCommentGroup(props: { contract={contract} parentAnswerOutcome={answer.number.toString()} replyTo={replyTo} - onSubmitComment={() => setReplyTo(undefined)} + onSubmitComment={onSubmitComment} /> )} diff --git a/web/components/feed/feed-comments.tsx b/web/components/feed/feed-comments.tsx index b9387a03..7111e88f 100644 --- a/web/components/feed/feed-comments.tsx +++ b/web/components/feed/feed-comments.tsx @@ -1,11 +1,14 @@ +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 React, { useEffect, useRef, useState } from 'react' import { useUser } from 'web/hooks/use-user' import { formatMoney } from 'common/util/format' -import { useRouter } from 'next/router' import { Row } from 'web/components/layout/row' -import clsx from 'clsx' import { Avatar } from 'web/components/avatar' import { OutcomeLabel } from 'web/components/outcome-label' import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time' @@ -14,9 +17,9 @@ 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, CommentTips } from 'web/hooks/use-tip-txns' +import { CommentTipMap } from 'web/hooks/use-tip-txns' +import { useEvent } from 'web/hooks/use-event' import { Content } from '../editor' -import { Editor } from '@tiptap/react' import { UserLink } from 'web/components/user-link' import { CommentInput } from '../comment-input' import { AwardBountyButton } from 'web/components/award-bounty-button' @@ -32,6 +35,12 @@ export function FeedCommentThread(props: { const { contract, threadComments, tips, parentComment } = props const [replyTo, setReplyTo] = useState() + const user = useUser() + const onSubmitComment = useEvent(() => setReplyTo(undefined)) + const onReplyClick = useEvent((comment: ContractComment) => { + setReplyTo({ id: comment.id, username: comment.userUsername }) + }) + return ( - setReplyTo({ id: comment.id, username: comment.userUsername }) - } + myTip={user ? tips[comment.id]?.[user.id] : undefined} + totalTip={sum(Object.values(tips[comment.id] ?? {}))} + showTip={true} + onReplyClick={onReplyClick} /> ))} {replyTo && ( @@ -60,7 +69,7 @@ export function FeedCommentThread(props: { contract={contract} parentCommentId={parentComment.id} replyTo={replyTo} - onSubmitComment={() => setReplyTo(undefined)} + onSubmitComment={onSubmitComment} /> )} @@ -68,14 +77,17 @@ export function FeedCommentThread(props: { ) } -export function FeedComment(props: { +export const FeedComment = memo(function FeedComment(props: { contract: Contract comment: ContractComment - tips?: CommentTips + showTip?: boolean + myTip?: number + totalTip?: number indent?: boolean - onReplyClick?: () => void + onReplyClick?: (comment: ContractComment) => void }) { - const { contract, comment, tips, indent, onReplyClick } = props + const { contract, comment, myTip, totalTip, showTip, indent, onReplyClick } = + props const { text, content, @@ -180,12 +192,18 @@ export function FeedComment(props: { {onReplyClick && ( )} - {tips && } + {showTip && ( + + )} {(contract.openCommentBounties ?? 0) > 0 && ( )} @@ -193,7 +211,7 @@ export function FeedComment(props: { ) -} +}) function CommentStatus(props: { contract: Contract diff --git a/web/components/tipper.tsx b/web/components/tipper.tsx index b201f946..ac978f81 100644 --- a/web/components/tipper.tsx +++ b/web/components/tipper.tsx @@ -1,10 +1,9 @@ import { useEffect, useRef, useState } from 'react' import toast from 'react-hot-toast' -import { debounce, sum } from 'lodash' +import { debounce } from 'lodash' import { Comment } from 'common/comment' import { User } from 'common/user' -import { CommentTips } from 'web/hooks/use-tip-txns' import { useUser } from 'web/hooks/use-user' import { transact } from 'web/lib/firebase/api' import { track } from 'web/lib/service/analytics' @@ -13,25 +12,27 @@ import { Row } from './layout/row' import { LIKE_TIP_AMOUNT } from 'common/like' import { formatMoney } from 'common/util/format' -export function Tipper(prop: { comment: Comment; tips: CommentTips }) { - const { comment, tips } = prop +export function Tipper(prop: { + comment: Comment + myTip: number + totalTip: number +}) { + const { comment, myTip, totalTip } = prop const me = useUser() - const myId = me?.id ?? '' - const savedTip = tips[myId] ?? 0 - const [localTip, setLocalTip] = useState(savedTip) + const [localTip, setLocalTip] = useState(myTip) // listen for user being set const initialized = useRef(false) useEffect(() => { - if (tips[myId] && !initialized.current) { - setLocalTip(tips[myId]) + if (myTip && !initialized.current) { + setLocalTip(myTip) initialized.current = true } - }, [tips, myId]) + }, [myTip]) - const total = sum(Object.values(tips)) - savedTip + localTip + const total = totalTip - myTip + localTip // declare debounced function only on first render const [saveTip] = useState(() => @@ -73,7 +74,7 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) { const addTip = (delta: number) => { setLocalTip(localTip + delta) - me && saveTip(me, comment, localTip - savedTip + delta) + me && saveTip(me, comment, localTip - myTip + delta) toast(`You tipped ${comment.userName} ${formatMoney(LIKE_TIP_AMOUNT)}!`) } diff --git a/web/posts/post-comments.tsx b/web/posts/post-comments.tsx index 74fbb300..a9a8532e 100644 --- a/web/posts/post-comments.tsx +++ b/web/posts/post-comments.tsx @@ -1,5 +1,6 @@ import { track } from '@amplitude/analytics-browser' import { Editor } from '@tiptap/core' +import { sum } from 'lodash' import clsx from 'clsx' import { PostComment } from 'common/comment' import { Post } from 'common/post' @@ -109,6 +110,7 @@ export function PostComment(props: { const { text, content, userUsername, userName, userAvatarUrl, createdTime } = comment + const me = useUser() const [highlighted, setHighlighted] = useState(false) const router = useRouter() useEffect(() => { @@ -162,7 +164,11 @@ export function PostComment(props: { Reply )} - +