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 (
)
}