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 | ||||
|       type={type} | ||||
|       className={clsx( | ||||
|         className, | ||||
|         'font-md items-center justify-center rounded-md border border-transparent shadow-sm transition-colors disabled:cursor-not-allowed', | ||||
|         sizeClasses, | ||||
|         color === 'green' && | ||||
|  | @ -66,7 +65,8 @@ export function Button(props: { | |||
|         color === 'gray-white' && | ||||
|           'text-greyscale-6 hover:bg-greyscale-2 border-none shadow-none disabled:opacity-50', | ||||
|         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} | ||||
|       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 { Contract } from 'common/contract' | ||||
| import { User } from 'common/user' | ||||
|  | @ -8,12 +6,10 @@ import toast from 'react-hot-toast' | |||
| import { formatMoney } from 'common/util/format' | ||||
| import { likeContract } from 'web/lib/firebase/likes' | ||||
| 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 { useMarketTipTxns } from 'web/hooks/use-tip-txns' | ||||
| import { sum } from 'lodash' | ||||
| import { Tooltip } from '../tooltip' | ||||
| import { TipButton } from './tip-button' | ||||
| 
 | ||||
| export function LikeMarketButton(props: { | ||||
|   contract: Contract | ||||
|  | @ -45,45 +41,16 @@ export function LikeMarketButton(props: { | |||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <Tooltip | ||||
|       text={`Tip ${formatMoney(LIKE_TIP_AMOUNT)}`} | ||||
|       placement="bottom" | ||||
|       noTap | ||||
|       noFade | ||||
|     > | ||||
|       <Button | ||||
|         size={'sm'} | ||||
|         className={'max-w-xs self-center'} | ||||
|         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> | ||||
|     <TipButton | ||||
|       onClick={onLike} | ||||
|       tipAmount={LIKE_TIP_AMOUNT} | ||||
|       totalTipped={totalTipped} | ||||
|       userTipped={ | ||||
|         !!user && | ||||
|         (isLiking || | ||||
|           userLikedContractIds?.includes(contract.id) || | ||||
|           (!likes && !!contract.likedByUserIds?.includes(user.id))) | ||||
|       } | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										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 | ||||
|         /> | ||||
|         <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 && ( | ||||
|             <button | ||||
|               className="font-bold hover:underline" | ||||
|  | @ -189,6 +185,10 @@ export function FeedComment(props: { | |||
|               Reply | ||||
|             </button> | ||||
|           )} | ||||
|           {tips && <Tipper comment={comment} tips={tips} />} | ||||
|           {(contract.openCommentBounties ?? 0) > 0 && ( | ||||
|             <AwardBountyButton comment={comment} contract={contract} /> | ||||
|           )} | ||||
|         </Row> | ||||
|       </div> | ||||
|     </Row> | ||||
|  |  | |||
|  | @ -1,20 +1,14 @@ | |||
| import { | ||||
|   ChevronDoubleRightIcon, | ||||
|   ChevronLeftIcon, | ||||
|   ChevronRightIcon, | ||||
| } from '@heroicons/react/solid' | ||||
| import clsx from 'clsx' | ||||
| import { debounce } from 'lodash' | ||||
| import { useEffect, useRef, useState } from 'react' | ||||
| 
 | ||||
| import { Comment } from 'common/comment' | ||||
| 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 { useUser } from 'web/hooks/use-user' | ||||
| import { transact } from 'web/lib/firebase/api' | ||||
| import { track } from 'web/lib/service/analytics' | ||||
| import { TipButton } from './contract/tip-button' | ||||
| import { Row } from './layout/row' | ||||
| import { Tooltip } from './tooltip' | ||||
| 
 | ||||
| const TIP_SIZE = 10 | ||||
| 
 | ||||
|  | @ -26,6 +20,7 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) { | |||
|   const savedTip = tips[myId] ?? 0 | ||||
| 
 | ||||
|   const [localTip, setLocalTip] = useState(savedTip) | ||||
| 
 | ||||
|   // listen for user being set
 | ||||
|   const initialized = useRef(false) | ||||
|   useEffect(() => { | ||||
|  | @ -35,8 +30,6 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) { | |||
|     } | ||||
|   }, [tips, myId]) | ||||
| 
 | ||||
|   const total = sum(Object.values(tips)) - savedTip + localTip | ||||
| 
 | ||||
|   // declare debounced function only on first render
 | ||||
|   const [saveTip] = useState(() => | ||||
|     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) | ||||
|   } | ||||
| 
 | ||||
|   const canDown = me && localTip > savedTip | ||||
|   const canUp = me && me.id !== comment.userId && me.balance >= localTip + 5 | ||||
|   if (me && comment.userId === me.id) { | ||||
|     return <></> | ||||
|   } | ||||
| 
 | ||||
|   const canUp = me && me.balance >= localTip + TIP_SIZE | ||||
| 
 | ||||
|   return ( | ||||
|     <Row className="items-center gap-0.5"> | ||||
|       <DownTip onClick={canDown ? () => addTip(-TIP_SIZE) : undefined} /> | ||||
|       <span className="font-bold">{Math.floor(total)}</span> | ||||
|       <UpTip | ||||
|         onClick={canUp ? () => addTip(+TIP_SIZE) : undefined} | ||||
|         value={localTip} | ||||
|       <TipButton | ||||
|         tipAmount={TIP_SIZE} | ||||
|         totalTipped={localTip} | ||||
|         onClick={() => addTip(+TIP_SIZE)} | ||||
|         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> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
|         /> | ||||
|         <Row className="mt-2 items-center gap-6 text-xs text-gray-500"> | ||||
|           <Tipper comment={comment} tips={tips ?? {}} /> | ||||
|           {onReplyClick && ( | ||||
|             <button | ||||
|               className="font-bold hover:underline" | ||||
|  | @ -163,6 +162,7 @@ export function PostComment(props: { | |||
|               Reply | ||||
|             </button> | ||||
|           )} | ||||
|           <Tipper comment={comment} tips={tips ?? {}} /> | ||||
|         </Row> | ||||
|       </div> | ||||
|     </Row> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user