stacker.news/components/upvote.js

266 lines
8.0 KiB
JavaScript
Raw Normal View History

2021-04-22 22:14:32 +00:00
import { LightningConsumer } from './lightning'
2021-12-05 17:37:55 +00:00
import UpBolt from '../svgs/bolt.svg'
2021-04-22 22:14:32 +00:00
import styles from './upvote.module.css'
import { gql, useMutation } from '@apollo/client'
import { signIn } from 'next-auth/client'
2021-05-20 21:32:59 +00:00
import { useFundError } from './fund-error'
2021-07-08 18:42:57 +00:00
import ActionTooltip from './action-tooltip'
2021-09-10 18:55:36 +00:00
import { useItemAct } from './item-act'
2021-09-12 16:55:38 +00:00
import { useMe } from './me'
import Rainbow from '../lib/rainbow'
2021-12-09 20:40:40 +00:00
import { useRef, useState } from 'react'
2021-10-30 16:52:24 +00:00
import LongPressable from 'react-longpressable'
2021-12-09 20:40:40 +00:00
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)),
2021-12-09 20:40:40 +00:00
Rainbow.length - 1)
return Rainbow[idx]
}
const UpvotePopover = ({ 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'>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>
2022-01-20 21:07:38 +00:00
<div className='mb-2'>Press the bolt again to tip 1 more sat.</div>
<div>Repeatedly press the bolt to tip more sats.</div>
2021-12-09 20:40:40 +00:00
</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>
2021-12-09 20:40:40 +00:00
</Popover.Content>
</Popover>
</Overlay>
)
2021-09-10 21:13:52 +00:00
export default function UpVote ({ item, className }) {
2021-05-20 21:32:59 +00:00
const { setError } = useFundError()
2021-09-10 18:55:36 +00:00
const { setItem } = useItemAct()
const [voteLock, setVoteLock] = useState()
2021-12-09 20:40:40 +00:00
const [voteShow, _setVoteShow] = useState(false)
const [tipShow, _setTipShow] = useState(false)
const ref = useRef()
2021-09-12 16:55:38 +00:00
const me = useMe()
2021-12-09 20:40:40 +00:00
const [setWalkthrough] = useMutation(
gql`
mutation setWalkthrough($upvotePopover: Boolean, $tipPopover: Boolean) {
setWalkthrough(upvotePopover: $upvotePopover, tipPopover: $tipPopover)
}`
)
const setVoteShow = (yes) => {
if (!me) return
if (yes && !me.upvotePopover) {
_setVoteShow(yes)
}
if (voteShow && !yes) {
_setVoteShow(yes)
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) {
_setTipShow(yes)
}
// if it's currently showing and we want to hide it
if (tipShow && !yes) {
_setTipShow(yes)
setWalkthrough({ variables: { tipPopover: true } })
}
}
2021-09-08 21:51:23 +00:00
const [act] = useMutation(
gql`
2021-09-12 16:55:38 +00:00
mutation act($id: ID!, $act: ItemAct! $sats: Int!, $tipDefault: Boolean) {
act(id: $id, act: $act, sats: $sats, tipDefault: $tipDefault) {
2021-09-10 21:13:52 +00:00
act,
sats
}
}`, {
2021-09-10 21:13:52 +00:00
update (cache, { data: { act: { act, sats } } }) {
// read in the cached object so we don't use meSats prop
// which can be stale
2021-12-09 20:40:40 +00:00
if (act === 'VOTE') {
setVoteShow(true)
}
if (act === 'TIP') {
setTipShow(true)
}
cache.modify({
2021-09-10 21:13:52 +00:00
id: `Item:${item.id}`,
fields: {
2021-09-10 21:13:52 +00:00
meVote (existingMeVote = 0) {
if (act === 'VOTE') {
return existingMeVote + sats
}
return existingMeVote
},
meTip (existingMeTip = 0) {
if (act === 'TIP') {
return existingMeTip + sats
}
return existingMeTip
2021-06-27 03:09:39 +00:00
},
sats (existingSats = 0) {
2021-09-10 21:13:52 +00:00
if (act === 'VOTE') {
return existingSats + sats
}
return existingSats
2021-04-27 21:30:58 +00:00
},
2021-12-05 17:37:55 +00:00
meSats (existingSats = 0) {
if (act === 'VOTE' || act === 'TIP') {
return existingSats + sats
}
return existingSats
},
2021-04-27 21:30:58 +00:00
boost (existingBoost = 0) {
2021-09-10 21:13:52 +00:00
if (act === 'BOOST') {
return existingBoost + sats
}
return existingBoost
},
tips (existingTips = 0) {
if (act === 'TIP') {
return existingTips + sats
}
return existingTips
}
}
})
}
}
)
2021-04-22 22:14:32 +00:00
2021-09-12 16:55:38 +00:00
const overlayText = () => {
if (item?.meVote) {
if (me?.tipDefault) {
2021-12-05 17:37:55 +00:00
return `${me.tipDefault} sat${me.tipDefault > 1 ? 's' : ''}`
2021-09-12 16:55:38 +00:00
}
2021-12-10 20:10:59 +00:00
return '1 sat'
2021-09-12 16:55:38 +00:00
}
}
2021-12-05 17:37:55 +00:00
const noSelfTips = item?.meVote && item?.mine
const color = getColor(item?.meSats)
2021-04-22 22:14:32 +00:00
return (
<LightningConsumer>
{({ strike }) =>
<div ref={ref} className='upvoteParent'>
2021-12-09 20:40:40 +00:00
<LongPressable
onLongPress={
2021-10-30 16:52:24 +00:00
async (e) => {
if (!item || voteLock) return
// we can't tip ourselves
if (noSelfTips) {
return
}
2021-12-09 20:40:40 +00:00
setTipShow(false)
2021-10-30 16:52:24 +00:00
setItem({ itemId: item.id, act, strike })
}
}
2021-12-09 20:40:40 +00:00
onShortPress={
me
2021-06-24 23:56:01 +00:00
? async (e) => {
if (!item || voteLock) return
2021-09-12 16:55:38 +00:00
// we can't tip ourselves
if (noSelfTips) {
return
}
2021-09-10 21:13:52 +00:00
if (item?.meVote) {
2021-12-09 20:40:40 +00:00
setVoteShow(false)
2021-12-10 20:10:59 +00:00
try {
strike()
await act({ variables: { id: item.id, act: 'TIP', sats: me.tipDefault || 1 } })
} catch (e) {
console.log(e)
2021-09-12 16:55:38 +00:00
}
2021-09-10 18:55:36 +00:00
return
}
2021-06-24 23:56:01 +00:00
strike()
2021-09-10 18:55:36 +00:00
2021-05-20 21:32:59 +00:00
try {
setVoteLock(true)
2021-09-10 21:13:52 +00:00
await act({ variables: { id: item.id, act: 'VOTE', sats: 1 } })
2021-05-20 21:32:59 +00:00
} catch (error) {
if (error.toString().includes('insufficient funds')) {
setError(true)
return
}
throw new Error({ message: error.toString() })
} finally {
setVoteLock(false)
}
}
: signIn
}
2021-12-09 20:40:40 +00:00
>
<ActionTooltip notForm disable={noSelfTips} overlayText={overlayText()}>
<div
className={`${noSelfTips ? styles.noSelfTips : ''}
${styles.upvoteWrapper}`}
>
2021-12-06 15:32:33 +00:00
<UpBolt
width={24}
height={24}
className={
2021-12-09 20:40:40 +00:00
`${styles.upvote}
${className || ''}
${noSelfTips ? styles.noSelfTips : ''}
2022-01-19 23:00:20 +00:00
${item?.meSats ? styles.voted : ''}`
2021-12-09 20:40:40 +00:00
}
2022-01-19 23:00:20 +00:00
style={item?.meSats
2021-12-06 15:32:33 +00:00
? {
fill: color,
filter: `drop-shadow(0 0 6px ${color}90)`
}
: undefined}
/>
2021-12-09 20:40:40 +00:00
</div>
</ActionTooltip>
</LongPressable>
<TipPopover target={ref.current} show={tipShow} handleClose={() => setTipShow(false)} />
<UpvotePopover target={ref.current} show={voteShow} handleClose={() => setVoteShow(false)} />
</div>}
2021-04-22 22:14:32 +00:00
</LightningConsumer>
)
}