Skip to content

Commit

Permalink
refactor: Use fragments to undo cache updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ekzyis committed Jan 31, 2024
1 parent b00c23b commit 8f04159
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 17 deletions.
39 changes: 32 additions & 7 deletions components/invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,39 @@ export const useInvoiceable = (onSubmit, options = defaultOptions) => {
}
const inv = data.createInvoice

// If this is a zap, we need to manually be optimistic to have a consistent
// UX across custodial and WebLN zaps since WebLN zaps don't call GraphQL
// mutations which implement optimistic responses natively.
// Therefore, we check if this is a zap and then wrap the WebLN payment logic
// with manual cache update calls.
const itemId = optimisticResponse?.act?.id
const isZap = !!itemId
let _update
if (isZap && update) {
_update = () => {
const fragment = {
id: `Item:${itemId}`,
fragment: gql`
fragment ItemMeSats on Item {
sats
meSats
}
`
}
const item = client.cache.readFragment(fragment)
update(client.cache, { data: optimisticResponse })
// undo function
return () => client.cache.writeFragment({ ...fragment, data: item })
}
}

// wait until invoice is paid or modal is closed
const modalClose = await waitForPayment({
invoice: inv,
showModal,
provider,
pollInvoice,
updateCache: () => update?.(client.cache, { data: optimisticResponse }),
undoUpdate: () => update?.(client.cache, { data: { ...optimisticResponse }, undo: true })
gqlCacheUpdate: _update
})

const retry = () => onSubmit(
Expand Down Expand Up @@ -267,10 +292,10 @@ export const useInvoiceable = (onSubmit, options = defaultOptions) => {
}

const INVOICE_CANCELED_ERROR = 'invoice was canceled'
const waitForPayment = async ({ invoice, showModal, provider, pollInvoice, updateCache, undoUpdate }) => {
const waitForPayment = async ({ invoice, showModal, provider, pollInvoice, gqlCacheUpdate }) => {
if (provider.enabled) {
try {
return await waitForWebLNPayment({ provider, invoice, pollInvoice, updateCache, undoUpdate })
return await waitForWebLNPayment({ provider, invoice, pollInvoice, gqlCacheUpdate })
} catch (err) {
const INVOICE_CANCELED_ERROR = 'invoice was canceled'
// check for errors which mean that QR code will also fail
Expand All @@ -293,12 +318,13 @@ const waitForPayment = async ({ invoice, showModal, provider, pollInvoice, updat
})
}

const waitForWebLNPayment = async ({ provider, invoice, pollInvoice, updateCache, undoUpdate }) => {
const waitForWebLNPayment = async ({ provider, invoice, pollInvoice, gqlCacheUpdate }) => {
let undoUpdate
try {
// try WebLN provider first
return await new Promise((resolve, reject) => {
// be optimistic and pretend zap was already successful for consistent zapping UX
updateCache?.()
undoUpdate = gqlCacheUpdate?.()
// can't use await here since we might be paying HODL invoices
// and sendPaymentAsync is not supported yet.
// see https://www.webln.guide/building-lightning-apps/webln-reference/webln.sendpaymentasync
Expand Down Expand Up @@ -333,7 +359,6 @@ const waitForWebLNPayment = async ({ provider, invoice, pollInvoice, updateCache
}, 1000)
})
} catch (err) {
// undo attempt to make zapping UX consistent
undoUpdate?.()
console.error('WebLN payment failed:', err)
throw err
Expand Down
20 changes: 10 additions & 10 deletions components/item-act.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,22 @@ export function useAct ({ onUpdate } = {}) {
const me = useMe()

const update = useCallback((cache, args) => {
const { data: { act: { id, sats, path, act, amount } }, undo } = args
const { data: { act: { id, sats, path, act } } } = args

cache.modify({
id: `Item:${id}`,
fields: {
sats (existingSats = 0) {
if (act === 'TIP') {
return existingSats + (undo ? -amount : sats)
return existingSats + sats
}

return existingSats
},
meSats: me
? (existingSats = 0) => {
if (act === 'TIP') {
return existingSats + (undo ? -amount : sats)
return existingSats + sats
}

return existingSats
Expand All @@ -131,7 +131,7 @@ export function useAct ({ onUpdate } = {}) {
meDontLikeSats: me
? (existingSats = 0) => {
if (act === 'DONT_LIKE_THIS') {
return existingSats + (undo ? -amount : sats)
return existingSats + sats
}

return existingSats
Expand All @@ -148,7 +148,7 @@ export function useAct ({ onUpdate } = {}) {
id: `Item:${aId}`,
fields: {
commentSats (existingCommentSats = 0) {
return existingCommentSats + (undo ? -amount : sats)
return existingCommentSats + sats
}
}
})
Expand All @@ -173,7 +173,7 @@ export function useAct ({ onUpdate } = {}) {

export function useZap () {
const update = useCallback((cache, args) => {
const { data: { act: { id, sats, path, amount } }, undo } = args
const { data: { act: { id, sats, path } } } = args

// determine how much we increased existing sats by by checking the
// difference between result sats and meSats
Expand All @@ -191,15 +191,15 @@ export function useZap () {

const satsDelta = sats - item.meSats

if (satsDelta >= 0) {
if (satsDelta > 0) {
cache.modify({
id: `Item:${id}`,
fields: {
sats (existingSats = 0) {
return existingSats + (undo ? -amount : satsDelta)
return existingSats + satsDelta
},
meSats: () => {
return undo ? sats - amount : sats
return sats
}
}
})
Expand All @@ -211,7 +211,7 @@ export function useZap () {
id: `Item:${aId}`,
fields: {
commentSats (existingCommentSats = 0) {
return existingCommentSats + (undo ? -amount : satsDelta)
return existingCommentSats + satsDelta
}
}
})
Expand Down

0 comments on commit 8f04159

Please sign in to comment.