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)}`} |       onClick={onLike} | ||||||
|       placement="bottom" |       tipAmount={LIKE_TIP_AMOUNT} | ||||||
|       noTap |       totalTipped={totalTipped} | ||||||
|       noFade |       userTipped={ | ||||||
|     > |         !!user && | ||||||
|       <Button |         (isLiking || | ||||||
|         size={'sm'} |           userLikedContractIds?.includes(contract.id) || | ||||||
|         className={'max-w-xs self-center'} |           (!likes && !!contract.likedByUserIds?.includes(user.id))) | ||||||
|         color={'gray-white'} |       } | ||||||
|         onClick={onLike} |     /> | ||||||
|       > |  | ||||||
|         <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' : '', |  | ||||||
|               user && |  | ||||||
|                 (isLiking || |  | ||||||
|                   userLikedContractIds?.includes(contract.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