94cce9155d
* 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
143 lines
4.3 KiB
JavaScript
143 lines
4.3 KiB
JavaScript
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
|
import { LNbitsProvider, useLNbits } from './lnbits'
|
|
import { NWCProvider, useNWC } from './nwc'
|
|
import { LNCProvider, useLNC } from './lnc'
|
|
|
|
const WebLNContext = createContext({})
|
|
|
|
const isEnabled = p => [Status.Enabled, Status.Locked].includes(p?.status)
|
|
|
|
const syncProvider = (array, provider) => {
|
|
const idx = array.findIndex(({ name }) => provider.name === name)
|
|
const enabled = isEnabled(provider)
|
|
if (idx === -1) {
|
|
// add provider to end if enabled
|
|
return enabled ? [...array, provider] : array
|
|
}
|
|
return [
|
|
...array.slice(0, idx),
|
|
// remove provider if not enabled
|
|
...enabled ? [provider] : [],
|
|
...array.slice(idx + 1)
|
|
]
|
|
}
|
|
|
|
const storageKey = 'webln:providers'
|
|
|
|
export const Status = {
|
|
Initialized: 'Initialized',
|
|
Enabled: 'Enabled',
|
|
Locked: 'Locked',
|
|
Error: 'Error'
|
|
}
|
|
|
|
export function migrateLocalStorage (oldStorageKey, newStorageKey) {
|
|
const item = window.localStorage.getItem(oldStorageKey)
|
|
if (item) {
|
|
window.localStorage.setItem(newStorageKey, item)
|
|
window.localStorage.removeItem(oldStorageKey)
|
|
}
|
|
return item
|
|
}
|
|
|
|
function RawWebLNProvider ({ children }) {
|
|
const lnbits = useLNbits()
|
|
const nwc = useNWC()
|
|
const lnc = useLNC()
|
|
const availableProviders = [lnbits, nwc, lnc]
|
|
const [enabledProviders, setEnabledProviders] = useState([])
|
|
|
|
// restore order on page reload
|
|
useEffect(() => {
|
|
const storedOrder = window.localStorage.getItem(storageKey)
|
|
if (!storedOrder) return
|
|
const providerNames = JSON.parse(storedOrder)
|
|
setEnabledProviders(providers => {
|
|
return providerNames.map(name => {
|
|
for (const p of availableProviders) {
|
|
if (p.name === name) return p
|
|
}
|
|
console.warn(`Stored provider with name ${name} not available`)
|
|
return null
|
|
})
|
|
})
|
|
}, [])
|
|
|
|
// keep list in sync with underlying providers
|
|
useEffect(() => {
|
|
setEnabledProviders(providers => {
|
|
// Sync existing provider state with new provider state
|
|
// in the list while keeping the order they are in.
|
|
// If provider does not exist but is enabled, it is just added to the end of the list.
|
|
// This can be the case if we're syncing from a page reload
|
|
// where the providers are initially not enabled.
|
|
// If provider is no longer enabled, it is removed from the list.
|
|
const isInitialized = p => [Status.Enabled, Status.Locked, Status.Initialized].includes(p.status)
|
|
const newProviders = availableProviders.filter(isInitialized).reduce(syncProvider, providers)
|
|
const newOrder = newProviders.map(({ name }) => name)
|
|
window.localStorage.setItem(storageKey, JSON.stringify(newOrder))
|
|
return newProviders
|
|
})
|
|
}, [...availableProviders])
|
|
|
|
// first provider in list is the default provider
|
|
// TODO: implement fallbacks via provider priority
|
|
const provider = enabledProviders[0]
|
|
|
|
const setProvider = useCallback((defaultProvider) => {
|
|
// move provider to the start to set it as default
|
|
setEnabledProviders(providers => {
|
|
const idx = providers.findIndex(({ name }) => defaultProvider.name === name)
|
|
if (idx === -1) {
|
|
console.warn(`tried to set unenabled provider ${defaultProvider.name} as default`)
|
|
return providers
|
|
}
|
|
return [defaultProvider, ...providers.slice(0, idx), ...providers.slice(idx + 1)]
|
|
})
|
|
}, [setEnabledProviders])
|
|
|
|
const clearConfig = useCallback(async () => {
|
|
lnbits.clearConfig()
|
|
nwc.clearConfig()
|
|
await lnc.clearConfig()
|
|
}, [])
|
|
|
|
const value = useMemo(() => ({
|
|
provider: isEnabled(provider)
|
|
? { name: provider.name, sendPayment: provider.sendPayment }
|
|
: null,
|
|
enabledProviders,
|
|
setProvider,
|
|
clearConfig
|
|
}), [provider, enabledProviders, setProvider])
|
|
|
|
return (
|
|
<WebLNContext.Provider value={value}>
|
|
{children}
|
|
</WebLNContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function WebLNProvider ({ children }) {
|
|
return (
|
|
<LNbitsProvider>
|
|
<NWCProvider>
|
|
<LNCProvider>
|
|
<RawWebLNProvider>
|
|
{children}
|
|
</RawWebLNProvider>
|
|
</LNCProvider>
|
|
</NWCProvider>
|
|
</LNbitsProvider>
|
|
)
|
|
}
|
|
|
|
export function useWebLN () {
|
|
const { provider } = useContext(WebLNContext)
|
|
return provider
|
|
}
|
|
|
|
export function useWebLNConfigurator () {
|
|
return useContext(WebLNContext)
|
|
}
|