import { LightningConsumer } from './lightning' import UpBolt from '../svgs/bolt.svg' import styles from './upvote.module.css' import { gql, useMutation } from '@apollo/client' import { signIn } from 'next-auth/client' import { useFundError } from './fund-error' import ActionTooltip from './action-tooltip' import { useItemAct } from './item-act' import { useMe } from './me' import Rainbow from '../lib/rainbow' import { useRef, useState } from 'react' import LongPressable from 'react-longpressable' import { Overlay, Popover } from 'react-bootstrap' const getColor = (meSats) => { if (!meSats || meSats <= 10) { return 'var(--secondary)' } const idx = Math.min( Math.floor((Math.log(meSats) / Math.log(10000)) * (Rainbow.length - 1)), Rainbow.length - 1) return Rainbow[idx] } const UpvotePopover = ({ target, show, handleClose }) => { const me = useMe() return ( <Overlay show={show} target={target} placement='right' > <Popover id='popover-basic'> <Popover.Title className='d-flex justify-content-between alert-dismissible' as='h3'>Tipping <button type='button' className='close' onClick={handleClose}><span aria-hidden='true'>×</span><span className='sr-only'>Close alert</span></button> </Popover.Title> <Popover.Content> <div className='mb-2'>Press the bolt again to tip {me?.tipDefault || 1} more sat{me?.tipDefault > 1 ? 's' : ''}.</div> <div>Repeatedly press the bolt to tip more sats.</div> </Popover.Content> </Popover> </Overlay> ) } const TipPopover = ({ target, show, handleClose }) => ( <Overlay show={show} target={target} placement='right' > <Popover id='popover-basic'> <Popover.Title className='d-flex justify-content-between alert-dismissible' as='h3'>Press and hold <button type='button' class='close' onClick={handleClose}><span aria-hidden='true'>×</span><span class='sr-only'>Close alert</span></button> </Popover.Title> <Popover.Content> <div className='mb-2'>Press and hold bolt to tip a custom amount.</div> <div>As you tip more, the bolt color follows the rainbow.</div> </Popover.Content> </Popover> </Overlay> ) export default function UpVote ({ item, className }) { const { setError } = useFundError() const { setItem } = useItemAct() const [voteShow, _setVoteShow] = useState(false) const [tipShow, _setTipShow] = useState(false) const ref = useRef() const me = useMe() const [setWalkthrough] = useMutation( gql` mutation setWalkthrough($upvotePopover: Boolean, $tipPopover: Boolean) { setWalkthrough(upvotePopover: $upvotePopover, tipPopover: $tipPopover) }` ) const setVoteShow = (yes) => { if (!me) return // if they haven't seen the walkthrough and they have sats if (yes && !me.upvotePopover && me.sats) { _setVoteShow(true) } if (voteShow && !yes) { _setVoteShow(false) setWalkthrough({ variables: { upvotePopover: true } }) } } const setTipShow = (yes) => { if (!me) return // if we want to show it, yet we still haven't shown if (yes && !me.tipPopover && me.sats) { _setTipShow(true) } // if it's currently showing and we want to hide it if (tipShow && !yes) { _setTipShow(false) setWalkthrough({ variables: { tipPopover: true } }) } } const [act] = useMutation( gql` mutation act($id: ID!, $sats: Int!) { act(id: $id, sats: $sats) { vote, sats } }`, { update (cache, { data: { act: { vote, sats } } }) { cache.modify({ id: `Item:${item.id}`, fields: { sats (existingSats = 0) { return existingSats + sats }, meSats (existingSats = 0) { if (existingSats === 0) { setVoteShow(true) } else { setTipShow(true) } return existingSats + sats }, upvotes (existingUpvotes = 0) { return existingUpvotes + vote } } }) } } ) const overlayText = () => { if (me?.tipDefault) { return `${me.tipDefault} sat${me.tipDefault > 1 ? 's' : ''}` } return '1 sat' } const color = getColor(item?.meSats) return ( <LightningConsumer> {({ strike }) => <div ref={ref} className='upvoteParent'> <LongPressable onLongPress={ async (e) => { if (!item) return // we can't tip ourselves if (item?.mine) { return } setTipShow(false) setItem({ itemId: item.id, act, strike }) } } onShortPress={ me ? async (e) => { if (!item) return // we can't tip ourselves if (item?.mine) { return } if (item?.meSats) { setVoteShow(false) } strike() try { await act({ variables: { id: item.id, sats: me.tipDefault || 1 }, optimisticResponse: { act: { id: `Item:${item.id}`, sats: me.tipDefault || 1, vote: 0 } } }) } catch (error) { if (error.toString().includes('insufficient funds')) { setError(true) return } throw new Error({ message: error.toString() }) } } : signIn } > <ActionTooltip notForm disable={item?.mine} overlayText={overlayText()}> <div className={`${item?.mine ? styles.noSelfTips : ''} ${styles.upvoteWrapper}`} > <UpBolt width={24} height={24} className={ `${styles.upvote} ${className || ''} ${item?.mine ? styles.noSelfTips : ''} ${item?.meSats ? styles.voted : ''}` } style={item?.meSats ? { fill: color, filter: `drop-shadow(0 0 6px ${color}90)` } : undefined} /> </div> </ActionTooltip> </LongPressable> <TipPopover target={ref.current} show={tipShow} handleClose={() => setTipShow(false)} /> <UpvotePopover target={ref.current} show={voteShow} handleClose={() => setVoteShow(false)} /> </div>} </LightningConsumer> ) }