stacker.news/components/upvote.js

242 lines
7.2 KiB
JavaScript
Raw Normal View History

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'
2021-07-08 18:42:57 +00:00
import ActionTooltip from './action-tooltip'
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
import ItemAct, { ZapUndoController, useZap } from './item-act'
2021-09-12 16:55:38 +00:00
import { useMe } from './me'
import getColor from '@/lib/rainbow'
2023-10-06 20:01:51 +00:00
import { useCallback, useMemo, useRef, useState } from 'react'
import LongPressable from './long-pressable'
2023-07-24 18:35:05 +00:00
import Overlay from 'react-bootstrap/Overlay'
import Popover from 'react-bootstrap/Popover'
import { useShowModal } from './modal'
import { numWithUnits } from '@/lib/format'
import { Dropdown } from 'react-bootstrap'
2021-12-09 20:40:40 +00:00
2022-04-12 21:09:12 +00:00
const UpvotePopover = ({ target, show, handleClose }) => {
const me = useMe()
return (
<Overlay
show={show}
target={target}
placement='right'
>
<Popover id='popover-basic'>
<Popover.Header className='d-flex justify-content-between alert-dismissible' as='h4'>Zapping
<button type='button' className='btn-close' onClick={handleClose}><span className='visually-hidden-focusable'>Close alert</span></button>
</Popover.Header>
2023-07-24 18:35:05 +00:00
<Popover.Body>
<div className='mb-2'>Press the bolt again to zap {me?.privates?.tipDefault || 1} more sat{me?.privates?.tipDefault > 1 ? 's' : ''}.</div>
2023-06-19 18:21:55 +00:00
<div>Repeatedly press the bolt to zap more sats.</div>
2023-07-24 18:35:05 +00:00
</Popover.Body>
2022-04-12 21:09:12 +00:00
</Popover>
</Overlay>
)
}
2021-12-09 20:40:40 +00:00
const TipPopover = ({ target, show, handleClose }) => (
<Overlay
show={show}
target={target}
placement='right'
>
<Popover id='popover-basic'>
<Popover.Header className='d-flex justify-content-between alert-dismissible' as='h4'>Press and hold
<button type='button' className='btn-close' onClick={handleClose}><span className='visually-hidden-focusable'>Close alert</span></button>
</Popover.Header>
2023-07-24 18:35:05 +00:00
<Popover.Body>
2023-06-19 18:21:55 +00:00
<div className='mb-2'>Press and hold bolt to zap a custom amount.</div>
<div>As you zap more, the bolt color follows the rainbow.</div>
2023-07-24 18:35:05 +00:00
</Popover.Body>
2021-12-09 20:40:40 +00:00
</Popover>
</Overlay>
)
export function DropdownItemUpVote ({ item }) {
const showModal = useShowModal()
return (
<Dropdown.Item
onClick={async () => {
showModal(onClose =>
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
<ItemAct onClose={onClose} item={item} />)
}}
>
<span className='text-success'>zap</span>
</Dropdown.Item>
)
}
export const nextTip = (meSats, { tipDefault, turboTipping }) => {
// what should our next tip be?
if (!turboTipping) return (tipDefault || 1)
let sats = tipDefault || 1
if (turboTipping) {
while (meSats >= sats) {
sats *= 10
}
// deduct current sats since turbo tipping is about total zap not making the next zap 10x
sats -= meSats
}
return sats
}
2023-12-27 02:27:52 +00:00
export default function UpVote ({ item, className }) {
const showModal = useShowModal()
const [voteShow, _setVoteShow] = useState(false)
const [tipShow, _setTipShow] = useState(false)
const ref = useRef()
const me = useMe()
const [hover, setHover] = useState(false)
const [setWalkthrough] = useMutation(
gql`
mutation setWalkthrough($upvotePopover: Boolean, $tipPopover: Boolean) {
setWalkthrough(upvotePopover: $upvotePopover, tipPopover: $tipPopover)
}`
)
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
const [controller, setController] = useState(null)
const pending = controller?.started && !controller.done
const setVoteShow = useCallback((yes) => {
if (!me) return
// if they haven't seen the walkthrough and they have sats
if (yes && !me.privates?.upvotePopover && me.privates?.sats) {
_setVoteShow(true)
}
if (voteShow && !yes) {
_setVoteShow(false)
setWalkthrough({ variables: { upvotePopover: true } })
}
}, [me, voteShow, setWalkthrough])
const setTipShow = useCallback((yes) => {
if (!me) return
// if we want to show it, yet we still haven't shown
if (yes && !me.privates?.tipPopover && me.privates?.sats) {
_setTipShow(true)
}
// if it's currently showing and we want to hide it
if (tipShow && !yes) {
_setTipShow(false)
setWalkthrough({ variables: { tipPopover: true } })
}
}, [me, tipShow, setWalkthrough])
2023-12-27 02:27:52 +00:00
const zap = useZap()
2023-08-28 14:40:29 +00:00
const disabled = useMemo(() => item?.mine || item?.meForward || item?.deletedAt,
[item?.mine, item?.meForward, item?.deletedAt])
const [meSats, overlayText, color, nextColor] = useMemo(() => {
2023-12-27 02:27:52 +00:00
const meSats = (item?.meSats || item?.meAnonSats || 0)
2022-12-09 19:25:38 +00:00
// what should our next tip be?
const sats = nextTip(meSats, { ...me?.privates })
2021-09-12 16:55:38 +00:00
return [
meSats, me ? numWithUnits(sats, { abbreviate: false }) : 'zap it',
getColor(meSats), getColor(meSats + sats)]
2023-12-27 02:27:52 +00:00
}, [item?.meSats, item?.meAnonSats, me?.privates?.tipDefault, me?.privates?.turboDefault])
2023-01-12 23:53:09 +00:00
const handleModalClosed = () => {
setHover(false)
}
const handleLongPress = (e) => {
if (!item) return
// we can't tip ourselves
if (disabled) {
return
}
setTipShow(false)
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
if (pending) {
controller.abort()
setController(null)
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
return
}
const c = new ZapUndoController()
setController(c)
showModal(onClose =>
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
<ItemAct onClose={onClose} item={item} abortSignal={c.signal} />, { onClose: handleModalClosed })
}
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
const handleShortPress = async () => {
if (me) {
if (!item) return
// we can't tip ourselves
if (disabled) {
return
}
if (meSats) {
setVoteShow(false)
} else {
setTipShow(true)
}
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
if (pending) {
controller.abort()
setController(null)
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
return
}
const c = new ZapUndoController()
setController(c)
await zap({ item, me, abortSignal: c.signal })
} else {
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
showModal(onClose => <ItemAct onClose={onClose} item={item} />, { onClose: handleModalClosed })
}
}
2024-05-07 19:20:22 +00:00
const fillColor = hover || pending ? nextColor : color
2024-05-07 19:20:22 +00:00
2021-04-22 22:14:32 +00:00
return (
2023-12-27 02:27:52 +00:00
<div ref={ref} className='upvoteParent'>
<LongPressable
onLongPress={handleLongPress}
onShortPress={handleShortPress}
2023-12-27 02:27:52 +00:00
>
<ActionTooltip notForm disable={disabled} overlayText={overlayText}>
<div
className={`${disabled ? styles.noSelfTips : ''} ${styles.upvoteWrapper}`}
2021-12-09 20:40:40 +00:00
>
2023-12-27 02:27:52 +00:00
<UpBolt
2024-03-02 00:32:40 +00:00
onPointerEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
2024-03-02 00:32:40 +00:00
onTouchEnd={() => setHover(false)}
2023-12-27 02:27:52 +00:00
width={26}
height={26}
className={
2021-12-09 20:40:40 +00:00
`${styles.upvote}
${className || ''}
2023-01-12 23:53:09 +00:00
${disabled ? styles.noSelfTips : ''}
Frontend payment UX cleanup (#1194) * Replace useInvoiceable with usePayment hook * Show WebLnError in QR code fallback * Fix missing removal of old zap undo code * Fix payment timeout message * Fix unused arg in super() * Also bail if invoice expired * Fix revert on reply error * Use JIT_INVOICE_TIMEOUT_MS constant * Remove unnecessary PaymentContext * Fix me as a dependency in FeeButtonContext * Fix anon sats added before act success * Optimistic updates for zaps * Fix modal not closed after custom zap * Optimistic update for custom zaps * Optimistic update for bounty payments * Consistent error handling for zaps and bounty payments * Optimistic update for poll votes * Use var balance in payment.request * Rename invoiceable to prepaid * Log cancelled invoices * Client notifications We now show notifications that are stored on the client to inform the user about following errors in the prepaid payment flow: - if a payment fails - if an invoice expires before it is paid - if a payment was interrupted (for example via page refresh) - if the action fails after payment * Remove unnecessary passing of act * Use AbortController for zap undos * Fix anon zap update not updating bolt color * Fix zap counted towards anon sats even if logged in * Fix duplicate onComplete call * Fix downzap type error * Fix "missing field 'path' while writing result" error * Pass full item in downzap props The previous commit fixed cache updates for downzaps but then the cache update for custom zaps failed because 'path' wasn't included in the server response. This commit is the proper fix. * Parse lnc rpc error messages * Add hash to InvoiceExpiredError
2024-05-28 17:18:54 +00:00
${meSats ? styles.voted : ''}
${pending ? styles.pending : ''}`
2021-12-09 20:40:40 +00:00
}
style={meSats || hover || pending
2023-12-27 02:27:52 +00:00
? {
2024-05-07 19:20:22 +00:00
fill: fillColor,
filter: `drop-shadow(0 0 6px ${fillColor}90)`
2023-12-27 02:27:52 +00:00
}
: undefined}
/>
</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
)
}