Consistent tips (#984)
* consistent tip button * hide tips for self * prettier
This commit is contained in:
parent
aeeb47bdbe
commit
cb613705e9
|
@ -46,7 +46,6 @@ export function Button(props: {
|
||||||
<button
|
<button
|
||||||
type={type}
|
type={type}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
className,
|
|
||||||
'font-md items-center justify-center rounded-md border border-transparent shadow-sm transition-colors disabled:cursor-not-allowed',
|
'font-md items-center justify-center rounded-md border border-transparent shadow-sm transition-colors disabled:cursor-not-allowed',
|
||||||
sizeClasses,
|
sizeClasses,
|
||||||
color === 'green' &&
|
color === 'green' &&
|
||||||
|
@ -66,7 +65,8 @@ export function Button(props: {
|
||||||
color === 'gray-white' &&
|
color === 'gray-white' &&
|
||||||
'text-greyscale-6 hover:bg-greyscale-2 border-none shadow-none disabled:opacity-50',
|
'text-greyscale-6 hover:bg-greyscale-2 border-none shadow-none disabled:opacity-50',
|
||||||
color === 'highlight-blue' &&
|
color === 'highlight-blue' &&
|
||||||
'text-highlight-blue disabled:bg-greyscale-2 border-none shadow-none'
|
'text-highlight-blue disabled:bg-greyscale-2 border-none shadow-none',
|
||||||
|
className
|
||||||
)}
|
)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { HeartIcon } from '@heroicons/react/outline'
|
|
||||||
import { Button } from 'web/components/button'
|
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
|
@ -8,12 +6,10 @@ import toast from 'react-hot-toast'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { likeContract } from 'web/lib/firebase/likes'
|
import { likeContract } from 'web/lib/firebase/likes'
|
||||||
import { LIKE_TIP_AMOUNT } from 'common/like'
|
import { LIKE_TIP_AMOUNT } from 'common/like'
|
||||||
import clsx from 'clsx'
|
|
||||||
import { Col } from 'web/components/layout/col'
|
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
import { firebaseLogin } from 'web/lib/firebase/users'
|
||||||
import { useMarketTipTxns } from 'web/hooks/use-tip-txns'
|
import { useMarketTipTxns } from 'web/hooks/use-tip-txns'
|
||||||
import { sum } from 'lodash'
|
import { sum } from 'lodash'
|
||||||
import { Tooltip } from '../tooltip'
|
import { TipButton } from './tip-button'
|
||||||
|
|
||||||
export function LikeMarketButton(props: {
|
export function LikeMarketButton(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -45,45 +41,16 @@ export function LikeMarketButton(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<TipButton
|
||||||
text={`Tip ${formatMoney(LIKE_TIP_AMOUNT)}`}
|
|
||||||
placement="bottom"
|
|
||||||
noTap
|
|
||||||
noFade
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size={'sm'}
|
|
||||||
className={'max-w-xs self-center'}
|
|
||||||
color={'gray-white'}
|
|
||||||
onClick={onLike}
|
onClick={onLike}
|
||||||
>
|
tipAmount={LIKE_TIP_AMOUNT}
|
||||||
<Col className={'relative items-center sm:flex-row'}>
|
totalTipped={totalTipped}
|
||||||
<HeartIcon
|
userTipped={
|
||||||
className={clsx(
|
!!user &&
|
||||||
'h-5 w-5 sm:h-6 sm:w-6',
|
|
||||||
totalTipped > 0 ? 'mr-2' : '',
|
|
||||||
user &&
|
|
||||||
(isLiking ||
|
(isLiking ||
|
||||||
userLikedContractIds?.includes(contract.id) ||
|
userLikedContractIds?.includes(contract.id) ||
|
||||||
(!likes && contract.likedByUserIds?.includes(user.id)))
|
(!likes && !!contract.likedByUserIds?.includes(user.id)))
|
||||||
? 'fill-red-500 text-red-500'
|
}
|
||||||
: ''
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
{totalTipped > 0 && (
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
'bg-greyscale-6 absolute ml-3.5 mt-2 h-4 w-4 rounded-full align-middle text-white sm:mt-3 sm:h-5 sm:w-5 sm:px-1',
|
|
||||||
totalTipped > 99
|
|
||||||
? 'text-[0.4rem] sm:text-[0.5rem]'
|
|
||||||
: 'sm:text-2xs text-[0.5rem]'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{totalTipped}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Col>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
57
web/components/contract/tip-button.tsx
Normal file
57
web/components/contract/tip-button.tsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { HeartIcon } from '@heroicons/react/outline'
|
||||||
|
import { Button } from 'web/components/button'
|
||||||
|
import { formatMoney } from 'common/util/format'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import { Col } from 'web/components/layout/col'
|
||||||
|
import { Tooltip } from '../tooltip'
|
||||||
|
|
||||||
|
export function TipButton(props: {
|
||||||
|
tipAmount: number
|
||||||
|
totalTipped: number
|
||||||
|
onClick: () => void
|
||||||
|
userTipped: boolean
|
||||||
|
isCompact?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
}) {
|
||||||
|
const { tipAmount, totalTipped, userTipped, isCompact, onClick, disabled } =
|
||||||
|
props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
text={`Tip ${formatMoney(tipAmount)}`}
|
||||||
|
placement="bottom"
|
||||||
|
noTap
|
||||||
|
noFade
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
size={'sm'}
|
||||||
|
className={clsx('max-w-xs self-center', isCompact && 'px-0 py-0')}
|
||||||
|
color={'gray-white'}
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<Col className={'relative items-center sm:flex-row'}>
|
||||||
|
<HeartIcon
|
||||||
|
className={clsx(
|
||||||
|
'h-5 w-5 sm:h-6 sm:w-6',
|
||||||
|
totalTipped > 0 ? 'mr-2' : '',
|
||||||
|
userTipped ? 'fill-red-500 text-red-500' : ''
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{totalTipped > 0 && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'bg-greyscale-6 absolute ml-3.5 mt-2 h-4 w-4 rounded-full align-middle text-white sm:mt-3 sm:h-5 sm:w-5 sm:px-1',
|
||||||
|
totalTipped > 99
|
||||||
|
? 'text-[0.4rem] sm:text-[0.5rem]'
|
||||||
|
: 'sm:text-2xs text-[0.5rem]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{totalTipped}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
|
@ -177,10 +177,6 @@ export function FeedComment(props: {
|
||||||
smallImage
|
smallImage
|
||||||
/>
|
/>
|
||||||
<Row className="mt-2 items-center gap-6 text-xs text-gray-500">
|
<Row className="mt-2 items-center gap-6 text-xs text-gray-500">
|
||||||
{tips && <Tipper comment={comment} tips={tips} />}
|
|
||||||
{(contract.openCommentBounties ?? 0) > 0 && (
|
|
||||||
<AwardBountyButton comment={comment} contract={contract} />
|
|
||||||
)}
|
|
||||||
{onReplyClick && (
|
{onReplyClick && (
|
||||||
<button
|
<button
|
||||||
className="font-bold hover:underline"
|
className="font-bold hover:underline"
|
||||||
|
@ -189,6 +185,10 @@ export function FeedComment(props: {
|
||||||
Reply
|
Reply
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{tips && <Tipper comment={comment} tips={tips} />}
|
||||||
|
{(contract.openCommentBounties ?? 0) > 0 && (
|
||||||
|
<AwardBountyButton comment={comment} contract={contract} />
|
||||||
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
import {
|
import { debounce } from 'lodash'
|
||||||
ChevronDoubleRightIcon,
|
import { useEffect, useRef, useState } from 'react'
|
||||||
ChevronLeftIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
} from '@heroicons/react/solid'
|
|
||||||
import clsx from 'clsx'
|
|
||||||
import { Comment } from 'common/comment'
|
import { Comment } from 'common/comment'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import { formatMoney } from 'common/util/format'
|
|
||||||
import { debounce, sum } from 'lodash'
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { CommentTips } from 'web/hooks/use-tip-txns'
|
import { CommentTips } from 'web/hooks/use-tip-txns'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { transact } from 'web/lib/firebase/api'
|
import { transact } from 'web/lib/firebase/api'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
import { TipButton } from './contract/tip-button'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { Tooltip } from './tooltip'
|
|
||||||
|
|
||||||
const TIP_SIZE = 10
|
const TIP_SIZE = 10
|
||||||
|
|
||||||
|
@ -26,6 +20,7 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) {
|
||||||
const savedTip = tips[myId] ?? 0
|
const savedTip = tips[myId] ?? 0
|
||||||
|
|
||||||
const [localTip, setLocalTip] = useState(savedTip)
|
const [localTip, setLocalTip] = useState(savedTip)
|
||||||
|
|
||||||
// listen for user being set
|
// listen for user being set
|
||||||
const initialized = useRef(false)
|
const initialized = useRef(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -35,8 +30,6 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) {
|
||||||
}
|
}
|
||||||
}, [tips, myId])
|
}, [tips, myId])
|
||||||
|
|
||||||
const total = sum(Object.values(tips)) - savedTip + localTip
|
|
||||||
|
|
||||||
// declare debounced function only on first render
|
// declare debounced function only on first render
|
||||||
const [saveTip] = useState(() =>
|
const [saveTip] = useState(() =>
|
||||||
debounce(async (user: User, comment: Comment, change: number) => {
|
debounce(async (user: User, comment: Comment, change: number) => {
|
||||||
|
@ -80,69 +73,22 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) {
|
||||||
me && saveTip(me, comment, localTip - savedTip + delta)
|
me && saveTip(me, comment, localTip - savedTip + delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
const canDown = me && localTip > savedTip
|
if (me && comment.userId === me.id) {
|
||||||
const canUp = me && me.id !== comment.userId && me.balance >= localTip + 5
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const canUp = me && me.balance >= localTip + TIP_SIZE
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="items-center gap-0.5">
|
<Row className="items-center gap-0.5">
|
||||||
<DownTip onClick={canDown ? () => addTip(-TIP_SIZE) : undefined} />
|
<TipButton
|
||||||
<span className="font-bold">{Math.floor(total)}</span>
|
tipAmount={TIP_SIZE}
|
||||||
<UpTip
|
totalTipped={localTip}
|
||||||
onClick={canUp ? () => addTip(+TIP_SIZE) : undefined}
|
onClick={() => addTip(+TIP_SIZE)}
|
||||||
value={localTip}
|
userTipped={localTip > 0}
|
||||||
|
disabled={!canUp}
|
||||||
|
isCompact
|
||||||
/>
|
/>
|
||||||
{localTip === 0 ? (
|
|
||||||
''
|
|
||||||
) : (
|
|
||||||
<span
|
|
||||||
className={clsx(
|
|
||||||
'ml-1 font-semibold',
|
|
||||||
localTip > 0 ? 'text-primary' : 'text-red-400'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
({formatMoney(localTip)} tip)
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DownTip(props: { onClick?: () => void }) {
|
|
||||||
const { onClick } = props
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
className="h-6 w-6"
|
|
||||||
placement="bottom"
|
|
||||||
text={onClick && `-${formatMoney(TIP_SIZE)}`}
|
|
||||||
noTap
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="hover:text-red-600 disabled:text-gray-100"
|
|
||||||
disabled={!onClick}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<ChevronLeftIcon className="h-6 w-6" />
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function UpTip(props: { onClick?: () => void; value: number }) {
|
|
||||||
const { onClick, value } = props
|
|
||||||
const IconKind = value > TIP_SIZE ? ChevronDoubleRightIcon : ChevronRightIcon
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
className="h-6 w-6"
|
|
||||||
placement="bottom"
|
|
||||||
text={onClick && `Tip ${formatMoney(TIP_SIZE)}`}
|
|
||||||
noTap
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="hover:text-primary disabled:text-gray-100"
|
|
||||||
disabled={!onClick}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<IconKind className={clsx('h-6 w-6', value ? 'text-primary' : '')} />
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -154,7 +154,6 @@ export function PostComment(props: {
|
||||||
smallImage
|
smallImage
|
||||||
/>
|
/>
|
||||||
<Row className="mt-2 items-center gap-6 text-xs text-gray-500">
|
<Row className="mt-2 items-center gap-6 text-xs text-gray-500">
|
||||||
<Tipper comment={comment} tips={tips ?? {}} />
|
|
||||||
{onReplyClick && (
|
{onReplyClick && (
|
||||||
<button
|
<button
|
||||||
className="font-bold hover:underline"
|
className="font-bold hover:underline"
|
||||||
|
@ -163,6 +162,7 @@ export function PostComment(props: {
|
||||||
Reply
|
Reply
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
<Tipper comment={comment} tips={tips ?? {}} />
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user