Change Tipper
interface, memoize FeedComment
(#1000)
* Change `Tipper` interface, memoize `FeedComment` * Fixup per James feedback * More fixup per James feedback
This commit is contained in:
parent
d2273087cf
commit
ddb186dd98
|
@ -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<ReplyTo>()
|
||||
const user = useUser()
|
||||
const router = useRouter()
|
||||
const answerElementId = `answer-${answer.id}`
|
||||
const highlighted = router.asPath.endsWith(`#${answerElementId}`)
|
||||
const answerRef = useRef<HTMLDivElement>(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}
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
|
@ -112,7 +121,7 @@ export function FeedAnswerCommentGroup(props: {
|
|||
contract={contract}
|
||||
parentAnswerOutcome={answer.number.toString()}
|
||||
replyTo={replyTo}
|
||||
onSubmitComment={() => setReplyTo(undefined)}
|
||||
onSubmitComment={onSubmitComment}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -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<ReplyTo>()
|
||||
|
||||
const user = useUser()
|
||||
const onSubmitComment = useEvent(() => setReplyTo(undefined))
|
||||
const onReplyClick = useEvent((comment: ContractComment) => {
|
||||
setReplyTo({ id: comment.id, username: comment.userUsername })
|
||||
})
|
||||
|
||||
return (
|
||||
<Col className="relative w-full items-stretch gap-3 pb-4">
|
||||
<span
|
||||
|
@ -44,10 +53,10 @@ export function FeedCommentThread(props: {
|
|||
indent={commentIdx != 0}
|
||||
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}
|
||||
/>
|
||||
))}
|
||||
{replyTo && (
|
||||
|
@ -60,7 +69,7 @@ export function FeedCommentThread(props: {
|
|||
contract={contract}
|
||||
parentCommentId={parentComment.id}
|
||||
replyTo={replyTo}
|
||||
onSubmitComment={() => setReplyTo(undefined)}
|
||||
onSubmitComment={onSubmitComment}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
|
@ -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 && (
|
||||
<button
|
||||
className="font-bold hover:underline"
|
||||
onClick={onReplyClick}
|
||||
onClick={() => onReplyClick(comment)}
|
||||
>
|
||||
Reply
|
||||
</button>
|
||||
)}
|
||||
{tips && <Tipper comment={comment} tips={tips} />}
|
||||
{showTip && (
|
||||
<Tipper
|
||||
comment={comment}
|
||||
myTip={myTip ?? 0}
|
||||
totalTip={totalTip ?? 0}
|
||||
/>
|
||||
)}
|
||||
{(contract.openCommentBounties ?? 0) > 0 && (
|
||||
<AwardBountyButton comment={comment} contract={contract} />
|
||||
)}
|
||||
|
@ -193,7 +211,7 @@ export function FeedComment(props: {
|
|||
</div>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function CommentStatus(props: {
|
||||
contract: Contract
|
||||
|
|
|
@ -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)}!`)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
</button>
|
||||
)}
|
||||
<Tipper comment={comment} tips={tips ?? {}} />
|
||||
<Tipper
|
||||
comment={comment}
|
||||
myTip={me ? tips[me.id] ?? 0 : 0}
|
||||
totalTip={sum(Object.values(tips))}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
</Row>
|
||||
|
|
Loading…
Reference in New Issue
Block a user