Skip to content

Commit

Permalink
Zap undos
Browse files Browse the repository at this point in the history
  • Loading branch information
ekzyis committed May 9, 2024
1 parent db65561 commit 2de9cd1
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 23 deletions.
5 changes: 3 additions & 2 deletions components/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import EyeClose from '@/svgs/eye-close-line.svg'
import Info from './info'
import { InvoiceCanceledError, usePayment } from './payment'
import { useMe } from './me'
import { ActCanceledError } from './item-act'

export class SessionRequiredError extends Error {
constructor () {
Expand Down Expand Up @@ -841,7 +842,7 @@ export function Form ({
}
let hash, hmac
if (invoiceable) {
revert = optimisticUpdate?.({ amount, ...values }, ...args);
revert = await optimisticUpdate?.({ amount, ...values }, ...args);
[{ hash, hmac }, cancel] = await payment.request(amount)
}
await onSubmit({ hash, hmac, amount, ...values }, ...args)
Expand All @@ -850,7 +851,7 @@ export function Form ({
}
} catch (err) {
revert?.()
if (err instanceof InvoiceCanceledError) {
if (err instanceof InvoiceCanceledError || err instanceof ActCanceledError) {
return
}
const msg = err.message || err.toString?.()
Expand Down
44 changes: 39 additions & 5 deletions components/item-act.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useMe } from './me'
import UpBolt from '@/svgs/bolt.svg'
import { amountSchema } from '@/lib/validate'
import { gql, useApolloClient, useMutation } from '@apollo/client'
import { useToast } from './toast'
import { TOAST_DEFAULT_DELAY_MS, useToast } from './toast'
import { useLightning } from './lightning'
import { nextTip } from './upvote'
import { InvoiceCanceledError, PaymentProvider, usePayment } from './payment'
Expand Down Expand Up @@ -47,6 +47,7 @@ export default function ItemAct ({ onClose, item, down, children }) {
const [oValue, setOValue] = useState()
const strike = useLightning()
const cache = useApolloClient().cache
const toaster = useToast()

useEffect(() => {
inputRef.current?.focus()
Expand All @@ -67,10 +68,19 @@ export default function ItemAct ({ onClose, item, down, children }) {
addCustomTip(Number(amount))
}, [me, act, down, item.id, strike, onClose])

const optimisticUpdate = useCallback(({ amount }) => {
const optimisticUpdate = useCallback(async ({ amount }) => {
onClose()
strike()
return actOptimisticUpdate(cache, { ...item, sats: Number(amount), act: down ? 'DONT_LIKE_THIS' : 'TIP' }, { me })
const revert = actOptimisticUpdate(cache, { ...item, sats: Number(amount), act: down ? 'DONT_LIKE_THIS' : 'TIP' }, { me })
if (zapUndoTrigger(me, amount)) {
try {
await zapUndo(toaster)
} catch (err) {
revert()
throw err
}
};
return revert
}, [cache, strike, onClose])

// we need to wrap with PaymentProvider here since modals don't have access to PaymentContext by default
Expand Down Expand Up @@ -276,12 +286,15 @@ export function useZap () {
try {
const variables = { path: item.path, id: item.id, sats, act: 'TIP' }
revert = zapOptimisticUpdate(cache, variables)
strike();
strike()
if (zapUndoTrigger(me, sats)) {
await zapUndo(toaster)
};
[{ hash, hmac }, cancel] = await payment.request(sats - meSats)
await zap({ variables: { ...variables, hash, hmac } })
} catch (error) {
revert?.()
if (error instanceof InvoiceCanceledError) {
if (error instanceof InvoiceCanceledError || error instanceof ActCanceledError) {
return
}
console.error(error)
Expand All @@ -290,3 +303,24 @@ export function useZap () {
}
}, [strike, payment])
}

export class ActCanceledError extends Error {
constructor () {
super('act canceled')
this.name = 'ActCanceledError'
}
}

const zapUndoTrigger = (me, amount) => {
if (!me) return false
const enabled = me.privates.zapUndos !== null
return enabled ? amount >= me.privates.zapUndos : false
}

const zapUndo = async (toaster) => {
return await new Promise((resolve, reject) => {
const delay = TOAST_DEFAULT_DELAY_MS
toaster.undo({ onClick: () => reject(new ActCanceledError()) })
setTimeout(resolve, delay)
})
}
54 changes: 42 additions & 12 deletions components/toast.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ export const ToastProvider = ({ children }) => {
...options
}
return dispatchToast(toast)
},
undo: (options) => {
const toast = {
body: (onClose) => <ToastUndo handleClick={options.onClick} onClose={onClose} />,
variant: 'undo',
autohide: true,
delay: TOAST_DEFAULT_DELAY_MS,
...options
}
return dispatchToast(toast)
}
}), [dispatchToast, removeToast])

Expand Down Expand Up @@ -137,18 +147,22 @@ export const ToastProvider = ({ children }) => {
key={toast.id} bg={toast.variant} show autohide={toast.autohide}
delay={toast.delay} className={`${styles.toast} ${styles[toast.variant]} ${textStyle}`} onClose={() => removeToast(toast)}
>
<ToastBody>
<div className='d-flex align-items-center'>
<div className='flex-grow-1'>{toast.body}</div>
<Button
variant={null}
className='p-0 ps-2'
aria-label='close'
onClick={onClose}
><div className={`${styles.toastClose} ${textStyle}`}>X</div>
</Button>
</div>
</ToastBody>
{typeof toast.body === 'function'
? toast.body(onClose)
: (
<ToastBody>
<div className='d-flex align-items-center'>
<div className='flex-grow-1'>{toast.body}</div>
<Button
variant={null}
className='p-0 ps-2'
aria-label='close'
onClick={onClose}
><div className={`${styles.toastClose} ${textStyle}`}>X</div>
</Button>
</div>
</ToastBody>
)}
{toast.progressBar && <div className={`${styles.progressBar} ${styles[toast.variant]}`} style={{ animationDuration, animationDelay }} />}
</Toast>
)
Expand All @@ -160,3 +174,19 @@ export const ToastProvider = ({ children }) => {
}

export const useToast = () => useContext(ToastContext)

const ToastUndo = ({ handleClick, onClose }) => {
// TODO: make more pretty
return (
<ToastBody
onClick={() => {
handleClick()
onClose()
}}
>
<div className='d-flex fw-bold align-items-center flex-right'>
undo
</div>
</ToastBody>
)
}
13 changes: 9 additions & 4 deletions components/toast.module.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.toastContainer {
transform: translate3d(0, 0, 0);
margin-bottom: 53px; /* padding for bottom nav bar on mobile */
}

.toast {
Expand Down Expand Up @@ -32,6 +33,12 @@
align-items: center;
}

.undo {
background-color: var(--bs-warning);
border-width: 0px;
color: black;
}

.progressBar {
width: 0;
height: 5px;
Expand All @@ -54,8 +61,6 @@
background-color: var(--bs-warning);
}



@keyframes progressBar {
0% {
width: 0;
Expand All @@ -71,7 +76,7 @@
}

@media screen and (min-width: 400px) {
.toast {
width: var(--bs-toast-max-width);
.toastContainer {
margin-bottom: 0px;
}
}

0 comments on commit 2de9cd1

Please sign in to comment.