From 570c842934860eaba45526f1671d3f09d2f8022e Mon Sep 17 00:00:00 2001 From: ekzyis Date: Mon, 18 Nov 2024 23:46:24 +0100 Subject: [PATCH] Wallet send+recv status derived from logs (#1559) * Derive wallet status from logs * Add send/recv icons * Set status individually for send and recv * Move status logic into own function * Add LNbits, Blink, CLN, LND, phoenixd logo * Fix wallet.status.any not using Status enum * Fix WebLN being weird * Use phoenixd logo with text * Also use wallet logo on config page * Also poll logs for wallet status * Use logger.info for logs not relevant for wallet status * Remove no longer used wallet badges * Crop LND logo like other logos * Fix all wallets show 'configure' * Fix wallet status not respecting enabled * Fix wallet.def.requiresConfig undefined * Fix banner shown for WebLN * Fix attach shown when configured * Filter by context.status to determine wallet status * Fix +- shown without context * Fix missing theme support for wallet logos --- api/resolvers/wallet.js | 5 ++- components/log-message.js | 32 +++++++++----- components/wallet-buttonbar.js | 2 +- components/wallet-card.js | 61 ++++++++++++++------------- components/wallet-logger.js | 5 ++- pages/settings/wallets/[wallet].js | 12 ++++-- public/wallets/blink-dark.svg | 10 +++++ public/wallets/blink.svg | 10 +++++ public/wallets/cln-dark.svg | 65 +++++++++++++++++++++++++++++ public/wallets/cln.svg | 65 +++++++++++++++++++++++++++++ public/wallets/lnbits-dark.svg | 13 ++++++ public/wallets/lnbits.svg | 13 ++++++ public/wallets/lnd-dark.png | Bin 0 -> 8579 bytes public/wallets/lnd.png | Bin 0 -> 8579 bytes public/wallets/phoenixd-dark.png | Bin 0 -> 1048 bytes public/wallets/phoenixd.png | Bin 0 -> 4780 bytes scripts/genwallet.sh | 1 - styles/wallet.module.css | 9 ++-- svgs/arrow-left-down-line.svg | 1 + svgs/arrow-right-up-line.svg | 1 + wallets/README.md | 4 +- wallets/blink/index.js | 2 +- wallets/cln/index.js | 2 +- wallets/common.js | 48 +++++++++++++++++++-- wallets/config.js | 2 +- wallets/index.js | 27 +++++++++--- wallets/lightning-address/index.js | 3 +- wallets/lnbits/index.js | 2 +- wallets/lnc/client.js | 4 +- wallets/lnc/index.js | 3 +- wallets/lnd/index.js | 2 +- wallets/nwc/index.js | 3 +- wallets/phoenixd/index.js | 2 +- wallets/webln/index.js | 3 +- 34 files changed, 333 insertions(+), 79 deletions(-) create mode 100644 public/wallets/blink-dark.svg create mode 100644 public/wallets/blink.svg create mode 100644 public/wallets/cln-dark.svg create mode 100644 public/wallets/cln.svg create mode 100644 public/wallets/lnbits-dark.svg create mode 100644 public/wallets/lnbits.svg create mode 100644 public/wallets/lnd-dark.png create mode 100644 public/wallets/lnd.png create mode 100644 public/wallets/phoenixd-dark.png create mode 100644 public/wallets/phoenixd.png create mode 100644 svgs/arrow-left-down-line.svg create mode 100644 svgs/arrow-right-up-line.svg diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js index 766bab1a..79765993 100644 --- a/api/resolvers/wallet.js +++ b/api/resolvers/wallet.js @@ -678,9 +678,12 @@ export const walletLogger = ({ wallet, models }) => { payment_hash: decoded.id, created_at: decoded.created_at, expires_at: decoded.expires_at, - description: decoded.description + description: decoded.description, + // payments should affect wallet status + status: true } } + context.recv = true await models.walletLog.create({ data: { diff --git a/components/log-message.js b/components/log-message.js index b1593027..73f76263 100644 --- a/components/log-message.js +++ b/components/log-message.js @@ -19,7 +19,16 @@ export default function LogMessage ({ showWallet, wallet, level, message, contex className = 'text-info' } - const hasContext = context && Object.keys(context).length > 0 + const filtered = context + ? Object.keys(context) + .filter(key => !['send', 'recv', 'status'].includes(key)) + .reduce((obj, key) => { + obj[key] = context[key] + return obj + }, {}) + : {} + + const hasContext = context && Object.keys(filtered).length > 0 const handleClick = () => { if (hasContext) { setShow(show => !show) } @@ -37,16 +46,17 @@ export default function LogMessage ({ showWallet, wallet, level, message, contex {message} {indicator} - {show && hasContext && Object.entries(context).map(([key, value], i) => { - const last = i === Object.keys(context).length - 1 - return ( - - - {key} - {value} - - ) - })} + {show && hasContext && Object.entries(filtered) + .map(([key, value], i) => { + const last = i === Object.keys(filtered).length - 1 + return ( + + + {key} + {value} + + ) + })} ) } diff --git a/components/wallet-buttonbar.js b/components/wallet-buttonbar.js index 76b60e92..46572919 100644 --- a/components/wallet-buttonbar.js +++ b/components/wallet-buttonbar.js @@ -11,7 +11,7 @@ export default function WalletButtonBar ({ return (
- {isConfigured(wallet) && + {isConfigured(wallet) && wallet.def.requiresConfig && } {children}
diff --git a/components/wallet-card.js b/components/wallet-card.js index eff71d06..a964f8d6 100644 --- a/components/wallet-card.js +++ b/components/wallet-card.js @@ -1,23 +1,34 @@ -import { Badge, Card } from 'react-bootstrap' +import { Card } from 'react-bootstrap' import styles from '@/styles/wallet.module.css' import Plug from '@/svgs/plug.svg' import Gear from '@/svgs/settings-5-fill.svg' import Link from 'next/link' import { Status, isConfigured } from '@/wallets/common' import DraggableIcon from '@/svgs/draggable.svg' +import RecvIcon from '@/svgs/arrow-left-down-line.svg' +import SendIcon from '@/svgs/arrow-right-up-line.svg' +import useDarkMode from './dark-mode' +import { useEffect, useState } from 'react' + +const statusToClass = status => { + switch (status) { + case Status.Enabled: return styles.success + case Status.Disabled: return styles.disabled + case Status.Error: return styles.error + case Status.Warning: return styles.warning + } +} export default function WalletCard ({ wallet, draggable, onDragStart, onDragEnter, onDragEnd, onTouchStart, sourceIndex, targetIndex, index }) { - const { card: { title, badges } } = wallet.def + const [dark] = useDarkMode() + const { card: { title, image } } = wallet.def + const [imgSrc, setImgSrc] = useState(image?.src) - let indicator = styles.disabled - switch (wallet.status) { - case Status.Enabled: - indicator = styles.success - break - default: - indicator = styles.disabled - break - } + useEffect(() => { + if (!imgSrc) return + // wallet.png <-> wallet-dark.png + setImgSrc(dark ? image?.src.replace(/\.([a-z]{3})$/, '-dark.$1') : image?.src) + }, [dark]) return (
- {wallet.status === Status.Enabled && } -
+
+ {wallet.status.any !== Status.Disabled && } + {wallet.support.recv && } + {wallet.support.send && } +
- {title} - - {badges?.map( - badge => { - let style = '' - switch (badge) { - case 'receive': style = styles.receive; break - case 'send': style = styles.send; break - } - return ( - - {badge} - - ) - })} - +
+ {image + ? {title} + : {title}} +
diff --git a/components/wallet-logger.js b/components/wallet-logger.js index e709443c..7beadeb0 100644 --- a/components/wallet-logger.js +++ b/components/wallet-logger.js @@ -174,9 +174,12 @@ export function useWalletLogger (wallet, setLogs) { payment_hash: decoded.tagsObject.payment_hash, description: decoded.tagsObject.description, created_at: new Date(decoded.timestamp * 1000).toISOString(), - expires_at: new Date(decoded.timeExpireDate * 1000).toISOString() + expires_at: new Date(decoded.timeExpireDate * 1000).toISOString(), + // payments should affect wallet status + status: true } } + context.send = true appendLog(wallet, level, message, context) console[level !== 'error' ? 'info' : 'error'](`[${tag(wallet)}]`, message) diff --git a/pages/settings/wallets/[wallet].js b/pages/settings/wallets/[wallet].js index 891307ce..cbaa0173 100644 --- a/pages/settings/wallets/[wallet].js +++ b/pages/settings/wallets/[wallet].js @@ -67,10 +67,16 @@ export default function WalletSettings () { } }, [wallet.def]) + const { card: { image, title, subtitle } } = wallet?.def || { card: {} } + return ( -

{wallet?.def.card.title}

-
{wallet?.def.card.subtitle}
+ {image + ? typeof image === 'object' + ? {title} + : {title} + :

{title}

} +
{subtitle}
} diff --git a/public/wallets/blink-dark.svg b/public/wallets/blink-dark.svg new file mode 100644 index 00000000..b62d73f3 --- /dev/null +++ b/public/wallets/blink-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/wallets/blink.svg b/public/wallets/blink.svg new file mode 100644 index 00000000..01dadddd --- /dev/null +++ b/public/wallets/blink.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/wallets/cln-dark.svg b/public/wallets/cln-dark.svg new file mode 100644 index 00000000..4a51b36b --- /dev/null +++ b/public/wallets/cln-dark.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/wallets/cln.svg b/public/wallets/cln.svg new file mode 100644 index 00000000..0e92b7e4 --- /dev/null +++ b/public/wallets/cln.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/wallets/lnbits-dark.svg b/public/wallets/lnbits-dark.svg new file mode 100644 index 00000000..53120791 --- /dev/null +++ b/public/wallets/lnbits-dark.svg @@ -0,0 +1,13 @@ + + Group 6 + + + + + + + + + + + \ No newline at end of file diff --git a/public/wallets/lnbits.svg b/public/wallets/lnbits.svg new file mode 100644 index 00000000..97a2ec17 --- /dev/null +++ b/public/wallets/lnbits.svg @@ -0,0 +1,13 @@ + + Group 6 + + + + + + + + + + + \ No newline at end of file diff --git a/public/wallets/lnd-dark.png b/public/wallets/lnd-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..169d277219c26cf0234d68ec229fad2c5f441167 GIT binary patch literal 8579 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 BL>~YE literal 0 HcmV?d00001 diff --git a/public/wallets/lnd.png b/public/wallets/lnd.png new file mode 100644 index 0000000000000000000000000000000000000000..169d277219c26cf0234d68ec229fad2c5f441167 GIT binary patch literal 8579 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 BL>~YE literal 0 HcmV?d00001 diff --git a/public/wallets/phoenixd-dark.png b/public/wallets/phoenixd-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..93aa93366211af81d7eff20a57b5ca52ce2539bf GIT binary patch literal 1048 zcmeAS@N?(olHy`uVBq!ia0y~yU@Qf)Jvi8aBxhbKKTx1K)7d$|)7e>}peR2rGbfdS zL1SX=L|c!;4l+mMgIzC*u^V?-q^$5OTdCBymCJR7Kvrv*tdHK`CN*8puI|MTq6$7F zZ+!LO(VErGs}(rv8Z4|WGGKh#jUjK6wsuFJywbV6Q+ZZiz`hAz zBOdQL8oB;T-qhFiF!^GvYlC;)|EYTFnk;M!Q+`=Ht$S`Y;1W=H@#M9T6{RKC#h`E63v&4F!kYtH#M2T~LZf9(d1Jy2SbY$gOe=7LcWFoc7az69Hk(E#${^v&-m!9 zvY5F1bt$73SPZ79qG!9J!l_8}iF!&(^Hk4Yd~O)DlG#M*3Zqo!?_WzF`S~z_)jPQF zif*}38hBhXlw=7d|jU%#^td_JeLChU6DP9w$a=UeYq+D)8%dDGm>uYRV~^o3oo zySevWz>|;R$Me7b&?;?kYq0`4&0z(z!WG6D#=FY)yR5wRSEz8^&0R|CN)%Gwblupy z+CSyF$F|>P>FMX+@y%Otf2Wu10>OoR7dqzlGyP9vf;iz%>hs0RHtjY0B=vf8&+Mh) z`}5Q9n(p2`VP7FH(3WS&wp`x!JM|fN*|EH4jnI8>9^bq3(fUXJ9^Eh=%Pjw85hL5Z!m#U3;|tI4pM0+LxlYVJ>T)+ddq0ECi~#lwl{a6#k9OLq zRYQV@;dk%M>uRyr16Th0rTVJdenAk_i{QX&*tASd-nH}u14I}SDNeEt0cCbtKF0-t eqCjOKAGk|7|E#VHyV?UxAq<|belF{r5}E*iSjGJS literal 0 HcmV?d00001 diff --git a/public/wallets/phoenixd.png b/public/wallets/phoenixd.png new file mode 100644 index 0000000000000000000000000000000000000000..59d1c7cccc4c62fa1b56bc3d7cd0dcac043ddbc1 GIT binary patch literal 4780 zcmeHLeN+_J6`y6R;38rGqi7Pxu}AS^c6W9M*d16A0b$i82wR~xe$39yvZL(Ix;wZF zK|&QH!YL-CshHA8G+HBR&Jl`LNorb2H6PmcsKpv{B$`A628iY;*s8rV%V*6wJ)G10 z$8g@hH}~E9yZ8OxeRubSk5ip~)MzuX=ig z`oY#YVdl!J{q3vWJP6O3MrhhIuD4c0O9J!fhr)*Xfq$ceVJ+vT)tm z4OPf%u3zuWv#vOh`$2WCxIbK3`+of=3z^KCYYyWdyFS0(xu!P1dC|QaH&^PPRF+_y z%A*fj-c3$y;%C&|+qrF_rlIvl>-9NnUg~|hv+}f1=T(O$>K$_;@3bb{h_=>ckxlQu z^~AH0JFJ%9mdyI{$VWw0O{*3*-8c1xDxdpgPV$)~Vip~>Z2b*h*@FRfkmo2bwU zmv^=C>*`V(H?<{=% zqbF~!zEj-Lef>K{Mt1JEGuqoHPy2Y~-Qp^rFYeym?mfjXBz<_?^WjZhgdXzP&s+1v zJj)7-5u9q86>OZ^?{v%Cn3&{u)67OrL~UF??=oTCEhjOQXH8gctVL^ar*IBFqu9gc z6lYqQ;*E@v#gZ1O68#haI609<{mufHm-3r18JB{wB-UW4tRikSVR?{kDT0SXNj0g~ z;%R<L+*{-l zX+Q4r&Xyno7^$3>@$hbu7hI@>N!x@%(S%_zj}FA=bXzQg@GfsZ3&4lQPrEgQTB~t7 zHN!2uV%lZ^=@008ExgtuH>b(wyh5Re;nFs9E^+p72$mVNcNcmJmR3tPENf3zaG`g;IFt zR1+ror&xjESxO#~HiomYdJ<>i7$Z&^X%?pqMiw^^T9VchB+bSehe4TLUXgY&oCF2n zY94TG1QAc$4J1zM7zQUvJAoTxX%3Gibv90GGsGKW4Z|Rw_3)6DbiwebBq$c3^m-jQ zwh?%ooh5OSf$T6s6gW%Lwpd!vavWVT_B5HyHJCq>czITgF?(dAyK`5~f^X zWpim;C|C>-OG|kQ0J0p`Ldj)R@K^<*z=RFf=D}$T94D3*=~Srd06JK$tpnA1ftH}O zf$$|bi@@^sqW`5W9Ue3h^kK_0c&{jUieypXMCEXs0&juW0$x5!C@LQcie>^!@Y0(( zR*n;}0#l5GcI9(WJ^IsiK+b<(Dv(CTuC;M^97kwzlBKn{kp1!PdXpcZNdi(x-!5bqE9^zX$F=>kQ^oQ#U!F}lX+8WjVhQXb2$F}g;@ zz^IhRvg`jwmul!b#kt^DkPj|P)3miFxN42JEl*EHdXRr2%@sf1_c}->xHEq0h5sK! zN=<>hRyPfV!J^rc7JM@_VoL0UV&#jcKol*e<%ubRQ~FhoJa*^K{rRM+aOG|{rzTtd zm%sUJW#cw<;>nZ>Dxu0|(+d~S=mfR|d`d579V%dHB%!HX2Fa9Dw;nWuKa&P+2 zu5K6qVmx#~F+Z{+uxto%edd>4yZ6Lj+TYXHecL{>?W@YGe`@GEcWDaq&D64%h+&G= z&Fea-`}Rj?Hf`R!L%4x$sK$KgiSx}Rb2psccc$64@13URbx7B;L&~U#zFzKhcu~nM z1$cv{Ed0l(SD050cOJU+u$PNk6c6DIG7jwQ1=6D~&Qg{w8?hJHF7-JCsv3Eff;iFl a<}N5rI \ No newline at end of file diff --git a/svgs/arrow-right-up-line.svg b/svgs/arrow-right-up-line.svg new file mode 100644 index 00000000..93d2f337 --- /dev/null +++ b/svgs/arrow-right-up-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wallets/README.md b/wallets/README.md index 166900f4..9ad23f8a 100644 --- a/wallets/README.md +++ b/wallets/README.md @@ -152,9 +152,9 @@ The card title. The subtitle that is shown below the title if you enter the configuration form of a wallet. -- `badges: string[]` +- `image: { src: string, ... }` -The badges that are shown inside the card. +The image props that will be used to show an image inside the card. Should contain at least the `src` property. ### client.js diff --git a/wallets/blink/index.js b/wallets/blink/index.js index 65304369..d870592c 100644 --- a/wallets/blink/index.js +++ b/wallets/blink/index.js @@ -67,5 +67,5 @@ export const fields = [ export const card = { title: 'Blink', subtitle: 'use [Blink](https://blink.sv/) for payments', - badges: ['send', 'receive'] + image: { src: '/wallets/blink.svg' } } diff --git a/wallets/cln/index.js b/wallets/cln/index.js index 14525f97..dc8523d1 100644 --- a/wallets/cln/index.js +++ b/wallets/cln/index.js @@ -63,5 +63,5 @@ export const fields = [ export const card = { title: 'CLN', subtitle: 'autowithdraw to your Core Lightning node via [CLNRest](https://docs.corelightning.org/docs/rest)', - badges: ['receive'] + image: { src: '/wallets/cln.svg' } } diff --git a/wallets/common.js b/wallets/common.js index c88b4655..c36d70aa 100644 --- a/wallets/common.js +++ b/wallets/common.js @@ -2,7 +2,9 @@ import walletDefs from '@/wallets/client' export const Status = { Enabled: 'Enabled', - Disabled: 'Disabled' + Disabled: 'Disabled', + Error: 'Error', + Warning: 'Warning' } export function getWalletByName (name) { @@ -89,12 +91,24 @@ function isReceiveConfigured ({ def, config }) { return fields.length > 0 && checkFields({ fields, config }) } +export function supportsSend ({ def, config }) { + return !!def.sendPayment +} + +export function supportsReceive ({ def, config }) { + return def.fields.some(f => f.serverOnly) +} + export function canSend ({ def, config }) { - return !!def.sendPayment && isSendConfigured({ def, config }) + return ( + supportsSend({ def, config }) && + isSendConfigured({ def, config }) && + (def.requiresConfig || config?.enabled) + ) } export function canReceive ({ def, config }) { - return def.fields.some(f => f.serverOnly) && isReceiveConfigured({ def, config }) + return supportsReceive({ def, config }) && isReceiveConfigured({ def, config }) } export function siftConfig (fields, config) { @@ -161,3 +175,31 @@ export async function saveWalletLocally (name, config, userId) { const storageKey = getStorageKey(name, userId) window.localStorage.setItem(storageKey, JSON.stringify(config)) } + +export const statusFromLog = (wallet, logs) => { + if (wallet.status.any === Status.Disabled) return wallet + + // override status depending on if there have been warnings or errors in the logs recently + // find first log from which we can derive status (logs are sorted by recent first) + const walletLogs = logs.filter(l => l.wallet === wallet.def.name) + const sendLevel = walletLogs.find(l => l.context?.status && l.context?.send)?.level + const recvLevel = walletLogs.find(l => l.context?.status && l.context?.recv)?.level + + const levelToStatus = (level) => { + switch (level?.toLowerCase()) { + case 'ok': + case 'success': return Status.Enabled + case 'error': return Status.Error + case 'warn': return Status.Warning + } + } + + return { + ...wallet, + status: { + ...wallet.status, + send: levelToStatus(sendLevel) || wallet.status.send, + recv: levelToStatus(recvLevel) || wallet.status.recv + } + } +} diff --git a/wallets/config.js b/wallets/config.js index 13011613..6227605c 100644 --- a/wallets/config.js +++ b/wallets/config.js @@ -54,7 +54,7 @@ export function useWalletConfigurator (wallet) { if (transformedConfig) { serverConfig = Object.assign(serverConfig, transformedConfig) } - } else { + } else if (wallet.def.requiresConfig) { throw new Error('configuration must be able to send or receive') } diff --git a/wallets/index.js b/wallets/index.js index 719e230f..c2e83eed 100644 --- a/wallets/index.js +++ b/wallets/index.js @@ -3,9 +3,9 @@ import { SET_WALLET_PRIORITY, WALLETS } from '@/fragments/wallet' import { SSR } from '@/lib/constants' import { useApolloClient, useMutation, useQuery } from '@apollo/client' import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend, isConfigured, upsertWalletVariables, siftConfig, saveWalletLocally } from './common' +import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend, isConfigured, upsertWalletVariables, siftConfig, saveWalletLocally, canReceive, supportsReceive, supportsSend, statusFromLog } from './common' import useVault from '@/components/vault/use-vault' -import { useWalletLogger } from '@/components/wallet-logger' +import { useWalletLogger, useWalletLogs } from '@/components/wallet-logger' import { decode as bolt11Decode } from 'bolt11' import walletDefs from '@/wallets/client' import { generateMutation } from './graphql' @@ -67,6 +67,7 @@ export function WalletsProvider ({ children }) { const [setWalletPriority] = useMutation(SET_WALLET_PRIORITY) const [serverWallets, setServerWallets] = useState([]) const client = useApolloClient() + const { logs } = useWalletLogs() const { data, refetch } = useQuery(WALLETS, SSR ? {} : { nextFetchPolicy: 'cache-and-network' }) @@ -111,7 +112,10 @@ export function WalletsProvider ({ children }) { const merged = {} for (const wallet of [...walletDefsOnly, ...localWallets, ...serverWallets]) { merged[wallet.def.name] = { - def: wallet.def, + def: { + ...wallet.def, + requiresConfig: wallet.def.fields.length > 0 + }, config: { ...merged[wallet.def.name]?.config, ...Object.fromEntries( @@ -128,8 +132,21 @@ export function WalletsProvider ({ children }) { // sort by priority, then add status field return Object.values(merged) .sort(walletPrioritySort) - .map(w => ({ ...w, status: w.config?.enabled ? Status.Enabled : Status.Disabled })) - }, [serverWallets, localWallets]) + .map(w => { + return { + ...w, + support: { + recv: supportsReceive(w), + send: supportsSend(w) + }, + status: { + any: w.config?.enabled && isConfigured(w) ? Status.Enabled : Status.Disabled, + send: w.config?.enabled && canSend(w) ? Status.Enabled : Status.Disabled, + recv: w.config?.enabled && canReceive(w) ? Status.Enabled : Status.Disabled + } + } + }).map(w => statusFromLog(w, logs)) + }, [serverWallets, localWallets, logs]) const settings = useMemo(() => { return { diff --git a/wallets/lightning-address/index.js b/wallets/lightning-address/index.js index e4c16bc1..e2bb7e52 100644 --- a/wallets/lightning-address/index.js +++ b/wallets/lightning-address/index.js @@ -22,6 +22,5 @@ export const fields = [ export const card = { title: 'lightning address', - subtitle: 'autowithdraw to a lightning address', - badges: ['receive'] + subtitle: 'autowithdraw to a lightning address' } diff --git a/wallets/lnbits/index.js b/wallets/lnbits/index.js index edcadead..492086f7 100644 --- a/wallets/lnbits/index.js +++ b/wallets/lnbits/index.js @@ -55,5 +55,5 @@ export const fields = [ export const card = { title: 'LNbits', subtitle: 'use [LNbits](https://lnbits.com/) for payments', - badges: ['send', 'receive'] + image: { src: '/wallets/lnbits.svg' } } diff --git a/wallets/lnc/client.js b/wallets/lnc/client.js index 72eab585..03f04152 100644 --- a/wallets/lnc/client.js +++ b/wallets/lnc/client.js @@ -38,11 +38,11 @@ export async function testSendPayment (credentials, { logger }) { logger.info('connecting ...') await lnc.connect() - logger.ok('connected') + logger.info('connected') logger.info('validating permissions ...') await validateNarrowPerms(lnc) - logger.ok('permissions ok') + logger.info('permissions ok') return lnc.credentials.credentials } finally { diff --git a/wallets/lnc/index.js b/wallets/lnc/index.js index 395762bf..c039678b 100644 --- a/wallets/lnc/index.js +++ b/wallets/lnc/index.js @@ -60,6 +60,5 @@ export const fields = [ export const card = { title: 'LNC', - subtitle: 'use Lightning Node Connect for LND payments', - badges: ['send', 'budgetable'] + subtitle: 'use Lightning Node Connect for LND payments' } diff --git a/wallets/lnd/index.js b/wallets/lnd/index.js index e1d6395c..dc55aa84 100644 --- a/wallets/lnd/index.js +++ b/wallets/lnd/index.js @@ -50,5 +50,5 @@ export const fields = [ export const card = { title: 'LND', subtitle: 'autowithdraw to your Lightning Labs node', - badges: ['receive'] + image: { src: '/wallets/lnd.png' } } diff --git a/wallets/nwc/index.js b/wallets/nwc/index.js index 49794d92..5bab2300 100644 --- a/wallets/nwc/index.js +++ b/wallets/nwc/index.js @@ -30,8 +30,7 @@ export const fields = [ export const card = { title: 'NWC', - subtitle: 'use Nostr Wallet Connect for payments', - badges: ['send', 'receive', 'budgetable'] + subtitle: 'use Nostr Wallet Connect for payments' } export async function nwcCall ({ nwcUrl, method, params }, { logger, timeout } = {}) { diff --git a/wallets/phoenixd/index.js b/wallets/phoenixd/index.js index 7bbec9eb..ad7f19ee 100644 --- a/wallets/phoenixd/index.js +++ b/wallets/phoenixd/index.js @@ -38,5 +38,5 @@ export const fields = [ export const card = { title: 'phoenixd', subtitle: 'use [phoenixd](https://phoenix.acinq.co/server) for payments', - badges: ['send', 'receive'] + image: { src: '/wallets/phoenixd.png' } } diff --git a/wallets/webln/index.js b/wallets/webln/index.js index e59f51bc..cce91750 100644 --- a/wallets/webln/index.js +++ b/wallets/webln/index.js @@ -12,6 +12,5 @@ export const fields = [] export const card = { title: 'WebLN', - subtitle: 'use a [WebLN provider](https://www.webln.guide/ressources/webln-providers) for payments', - badges: ['send'] + subtitle: 'use a [WebLN provider](https://www.webln.guide/ressources/webln-providers) for payments' }