Consistent tips (#984)

* consistent tip button

* hide tips for self

* prettier
This commit is contained in:
mantikoros 2022-10-01 15:51:08 -05:00 committed by GitHub
parent aeeb47bdbe
commit cb613705e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 124 deletions

View File

@ -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}

View File

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

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

View File

@ -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>

View File

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

View File

@ -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>