import { ChevronDoubleRightIcon, ChevronLeftIcon, ChevronRightIcon, } from '@heroicons/react/solid' import clsx from 'clsx' import { Comment } from 'common/comment' import { User } from 'common/user' import { formatMoney } from 'common/util/format' import { debounce, sumBy } from 'lodash' import { useEffect, useMemo, useRef, useState } from 'react' import { CommentTips } from 'web/hooks/use-tip-txns' import { useUser } from 'web/hooks/use-user' import { transact } from 'web/lib/firebase/fn-call' import { track } from 'web/lib/service/analytics' import { Row } from './layout/row' import { Tooltip } from './tooltip' // xth triangle number * 5 = 5 + 10 + 15 + ... + (x * 5) const quad = (x: number) => (5 / 2) * x * (x + 1) // inverse (see https://math.stackexchange.com/questions/2041988/how-to-get-inverse-of-formula-for-sum-of-integers-from-1-to-nsee ) const invQuad = (y: number) => Math.sqrt((2 / 5) * y + 1 / 4) - 1 / 2 export function Tipper(prop: { comment: Comment; tips: CommentTips }) { const { comment, tips } = prop const me = useUser() const myId = me?.id ?? '' const savedTip = tips[myId] as number | undefined // optimistically increase the tip count, but debounce the update const [localTip, setLocalTip] = useState(savedTip ?? 0) const initialized = useRef(false) useEffect(() => { if (savedTip && !initialized.current) { setLocalTip(savedTip) initialized.current = true } }, [savedTip]) const score = useMemo(() => { const tipVals = Object.values({ ...tips, [myId]: localTip }) return sumBy(tipVals, invQuad) }, [localTip, tips, myId]) // declare debounced function only on first render const [saveTip] = useState(() => debounce(async (user: User, change: number) => { if (change === 0) { return } await transact({ amount: change, fromId: user.id, fromType: 'USER', toId: comment.userId, toType: 'USER', token: 'M$', category: 'TIP', data: { contractId: comment.contractId, commentId: comment.id, }, description: `${user.name} tipped M$ ${change} to ${comment.userName} for a comment`, }) track('send comment tip', { contractId: comment.contractId, commentId: comment.id, amount: change, fromId: user.id, toId: comment.userId, }) }, 1500) ) // instant save on unrender useEffect(() => () => void saveTip.flush(), [saveTip]) const changeTip = (tip: number) => { setLocalTip(tip) me && saveTip(me, tip - (savedTip ?? 0)) } return ( {Math.floor(score)} {localTip === 0 ? ( '' ) : ( 0 ? 'text-primary' : 'text-red-400' )} > ({formatMoney(localTip)} tip) )} ) } function DownTip(prop: { value: number onChange: (tip: number) => void disabled?: boolean }) { const { onChange, value, disabled } = prop const marginal = 5 * invQuad(value) return ( ) } function UpTip(prop: { value: number onChange: (tip: number) => void disabled?: boolean }) { const { onChange, value, disabled } = prop const marginal = 5 * invQuad(value) + 5 return ( ) }