From 5730a8a33185aa9ac809a67dbec47e15ca6820b8 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Sat, 16 Dec 2023 23:29:08 +0700 Subject: [PATCH 1/5] feat: add bc-pay-button web component --- README.md | 10 +++++ dev/vite/index.html | 5 +++ package.json | 2 +- src/components/bc-pay-button.ts | 67 ++++++++++++++++++++++++++++++ src/components/flows/bc-payment.ts | 1 + src/index.ts | 1 + 6 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/components/bc-pay-button.ts diff --git a/README.md b/README.md index f1af843..9689a7f 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,15 @@ _Use another popular framework? please let us know or feel free to create a PR f Bitcoin Connect exposes the following web components for allowing users to connect their desired Lightning wallet: - `` - launches the Bitcoin Connect Modal on click + - Arguments: + - `title` - (optional) change the title of the button +- `` - launches the Bitcoin Connect Payment Modal on click + - Arguments: + - `invoice` - BOLT11 invoice + - `title` - (optional) change the title of the button + - `preimage` - (optional) set this if you received an external payment +- - Events: + - `bc:onpaid` **Experimental** - fires event with WebLN payment response in `event.detail` (contains `preimage`) - `` - render connect wallet UI without modal - `` - render a payment request UI without modal - Arguments: @@ -565,6 +574,7 @@ This project is powered by Lit. See [Get started](https://lit.dev/docs/getting-started/) on the Lit site for more information. ## BOLT FUN + Bitcoin Connect is a BOLT FUN Legends of Lightning vol.2 finalist. [Follow our project and journey](https://bolt.fun/project/bitcoin-connect). ## License diff --git a/dev/vite/index.html b/dev/vite/index.html index 3cb4790..dd99045 100644 --- a/dev/vite/index.html +++ b/dev/vite/index.html @@ -33,6 +33,9 @@ document .getElementById('send-payment') .setAttribute('invoice', invoice.paymentRequest); + document + .getElementById('pay-button') + .setAttribute('invoice', invoice.paymentRequest); document .getElementById('payment-flow') .setAttribute('invoice', invoice.paymentRequest); @@ -200,6 +203,8 @@

Payment Flow

Components

Button

+

Pay Button

+

Modal header

Example Title diff --git a/package.json b/package.json index 31a2eca..f5a6a9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@getalby/bitcoin-connect", - "version": "3.0.0", + "version": "3.1.0-beta.0", "description": "Web components to connect to a lightning wallet and power a website with WebLN", "type": "module", "source": "src/index.ts", diff --git a/src/components/bc-pay-button.ts b/src/components/bc-pay-button.ts new file mode 100644 index 0000000..4a583dc --- /dev/null +++ b/src/components/bc-pay-button.ts @@ -0,0 +1,67 @@ +import {PropertyValues, html} from 'lit'; +import {customElement, property} from 'lit/decorators.js'; +import {BitcoinConnectElement} from './BitcoinConnectElement.js'; +import {bcIcon} from './icons/bcIcon.js'; +import {withTwind} from './twind/withTwind.js'; +import {loadingIcon} from './icons/loadingIcon.js'; +import {launchPaymentModal} from '../api.js'; +import './bc-balance.js'; +import {SendPaymentResponse} from '@webbtc/webln-types'; + +/** + * A button that when clicked launches a modal to pay an invoice. + */ +@customElement('bc-pay-button') +export class PayButton extends withTwind()(BitcoinConnectElement) { + @property() + override title = 'Pay Now'; + + @property() + invoice?: string; + + @property({}) + preimage?: string; + + private _setPaid?: (response: SendPaymentResponse) => void; + + protected override updated(changedProperties: PropertyValues): void { + super.updated(changedProperties); + + if (changedProperties.has('preimage') && this.preimage) { + this._setPaid?.({ + preimage: this.preimage, + }); + } + } + + override render() { + const isLoading = this._connecting || (!this._connected && this._modalOpen); + + return html`
+ + ${isLoading + ? html`${loadingIcon}` + : html`${bcIcon}`} + + ${isLoading ? html`Waiting...` : html`${this.title}`} + + +
`; + } + + private _onClick() { + if (!this.invoice) { + throw new Error('No invoice'); + } + const {setPaid} = launchPaymentModal({ + invoice: this.invoice, + }); + this._setPaid = setPaid; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'bc-pay-button': PayButton; + } +} diff --git a/src/components/flows/bc-payment.ts b/src/components/flows/bc-payment.ts index 8f590de..c7ad003 100644 --- a/src/components/flows/bc-payment.ts +++ b/src/components/flows/bc-payment.ts @@ -33,6 +33,7 @@ export class SendPaymentFlow extends withTwind()(BitcoinConnectElement) { }) invoice?: string; + // TODO: change to preimage @property({ type: Boolean, }) diff --git a/src/index.ts b/src/index.ts index 93dc20c..c83febc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import {LnbitsWebLNProvider} from './connectors/LnbitsConnector'; import './state/boot'; export * from './components/bc-button'; +export * from './components/bc-pay-button'; export * from './components/bc-modal'; export * from './components/bc-connector-list'; export * from './components/pages/bc-send-payment'; From 6603f584f90644aa29990fd988f4f8a6fa307629 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 20 Dec 2023 14:11:36 +0700 Subject: [PATCH 2/5] feat: allow invoice to be set after pay button is clicked --- README.md | 5 +++-- dev/vite/index.html | 11 ++++++++--- package.json | 2 +- src/components/bc-pay-button.ts | 27 +++++++++++++++++++++++---- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9689a7f..4b1318a 100644 --- a/README.md +++ b/README.md @@ -191,10 +191,11 @@ Bitcoin Connect exposes the following web components for allowing users to conne - `title` - (optional) change the title of the button - `` - launches the Bitcoin Connect Payment Modal on click - Arguments: - - `invoice` - BOLT11 invoice + - `invoice` - BOLT11 invoice. Modal will only open if an invoice is set - `title` - (optional) change the title of the button - `preimage` - (optional) set this if you received an external payment -- - Events: + - Events: + - `click` - fires when the button is clicked. You can load an invoice here and set it on the button using `setAttribute('invoice', 'lnbc...')` which will then automatically launch the modal - `bc:onpaid` **Experimental** - fires event with WebLN payment response in `event.detail` (contains `preimage`) - `` - render connect wallet UI without modal - `` - render a payment request UI without modal diff --git a/dev/vite/index.html b/dev/vite/index.html index dd99045..5d1c42e 100644 --- a/dev/vite/index.html +++ b/dev/vite/index.html @@ -33,9 +33,14 @@ document .getElementById('send-payment') .setAttribute('invoice', invoice.paymentRequest); - document - .getElementById('pay-button') - .setAttribute('invoice', invoice.paymentRequest); + document.getElementById('pay-button').addEventListener('click', () => { + setTimeout(() => { + // fake a delay fetching an invoice + document + .getElementById('pay-button') + .setAttribute('invoice', invoice.paymentRequest); + }, 1000); + }); document .getElementById('payment-flow') .setAttribute('invoice', invoice.paymentRequest); diff --git a/package.json b/package.json index f5a6a9c..dedea0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@getalby/bitcoin-connect", - "version": "3.1.0-beta.0", + "version": "3.1.0-beta.1", "description": "Web components to connect to a lightning wallet and power a website with WebLN", "type": "module", "source": "src/index.ts", diff --git a/src/components/bc-pay-button.ts b/src/components/bc-pay-button.ts index 4a583dc..6c8d331 100644 --- a/src/components/bc-pay-button.ts +++ b/src/components/bc-pay-button.ts @@ -1,5 +1,5 @@ import {PropertyValues, html} from 'lit'; -import {customElement, property} from 'lit/decorators.js'; +import {customElement, property, state} from 'lit/decorators.js'; import {BitcoinConnectElement} from './BitcoinConnectElement.js'; import {bcIcon} from './icons/bcIcon.js'; import {withTwind} from './twind/withTwind.js'; @@ -22,11 +22,22 @@ export class PayButton extends withTwind()(BitcoinConnectElement) { @property({}) preimage?: string; + @state() + _waitingForInvoice = false; + private _setPaid?: (response: SendPaymentResponse) => void; protected override updated(changedProperties: PropertyValues): void { super.updated(changedProperties); + if ( + changedProperties.has('invoice') && + this.invoice && + this._waitingForInvoice + ) { + this._launchModal(); + } + if (changedProperties.has('preimage') && this.preimage) { this._setPaid?.({ preimage: this.preimage, @@ -35,7 +46,7 @@ export class PayButton extends withTwind()(BitcoinConnectElement) { } override render() { - const isLoading = this._connecting || (!this._connected && this._modalOpen); + const isLoading = this._waitingForInvoice || this._modalOpen; return html`
@@ -43,15 +54,23 @@ export class PayButton extends withTwind()(BitcoinConnectElement) { ? html`${loadingIcon}` : html`${bcIcon}`} - ${isLoading ? html`Waiting...` : html`${this.title}`} + ${isLoading ? html`Loading...` : html`${this.title}`}
`; } private _onClick() { + this._waitingForInvoice = true; + if (this.invoice) { + this._launchModal(); + } + } + + private _launchModal() { + this._waitingForInvoice = false; if (!this.invoice) { - throw new Error('No invoice'); + throw new Error('No invoice available'); } const {setPaid} = launchPaymentModal({ invoice: this.invoice, From d597c046a11e4f48ab2c257d107337d04eb8e5c0 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 20 Dec 2023 14:36:59 +0700 Subject: [PATCH 3/5] feat add paid state to pay button --- package.json | 2 +- src/components/bc-pay-button.ts | 16 +++++++++++++++- src/components/icons/checkIcon.ts | 7 +++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/components/icons/checkIcon.ts diff --git a/package.json b/package.json index dedea0c..b443da3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@getalby/bitcoin-connect", - "version": "3.1.0-beta.1", + "version": "3.1.0-beta.3", "description": "Web components to connect to a lightning wallet and power a website with WebLN", "type": "module", "source": "src/index.ts", diff --git a/src/components/bc-pay-button.ts b/src/components/bc-pay-button.ts index 6c8d331..2b60023 100644 --- a/src/components/bc-pay-button.ts +++ b/src/components/bc-pay-button.ts @@ -7,6 +7,7 @@ import {loadingIcon} from './icons/loadingIcon.js'; import {launchPaymentModal} from '../api.js'; import './bc-balance.js'; import {SendPaymentResponse} from '@webbtc/webln-types'; +import {checkIcon} from './icons/checkIcon.js'; /** * A button that when clicked launches a modal to pay an invoice. @@ -25,6 +26,9 @@ export class PayButton extends withTwind()(BitcoinConnectElement) { @state() _waitingForInvoice = false; + @state() + _paid = false; + private _setPaid?: (response: SendPaymentResponse) => void; protected override updated(changedProperties: PropertyValues): void { @@ -52,15 +56,22 @@ export class PayButton extends withTwind()(BitcoinConnectElement) { ${isLoading ? html`${loadingIcon}` + : this._paid + ? html`${checkIcon}` : html`${bcIcon}`} - ${isLoading ? html`Loading...` : html`${this.title}`} + ${isLoading + ? html`Loading...` + : html`${this._paid ? 'Paid' : this.title}`} `; } private _onClick() { + if (this._paid) { + return; + } this._waitingForInvoice = true; if (this.invoice) { this._launchModal(); @@ -73,6 +84,9 @@ export class PayButton extends withTwind()(BitcoinConnectElement) { throw new Error('No invoice available'); } const {setPaid} = launchPaymentModal({ + onPaid: () => { + this._paid = true; + }, invoice: this.invoice, }); this._setPaid = setPaid; diff --git a/src/components/icons/checkIcon.ts b/src/components/icons/checkIcon.ts new file mode 100644 index 0000000..e880023 --- /dev/null +++ b/src/components/icons/checkIcon.ts @@ -0,0 +1,7 @@ +import {svg} from 'lit'; + +export const checkIcon = svg` + + + +`; From ffce3db42b452104900bc1e3b4233c1046ce5caf Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 20 Dec 2023 15:21:02 +0700 Subject: [PATCH 4/5] feat: add PayButton component to react wrapper --- README.md | 17 ++++++++---- demos/react/package.json | 2 +- demos/react/src/App.tsx | 26 +++++++++++++----- demos/react/yarn.lock | 18 ++++++------ react/package.json | 4 +-- react/src/components/PayButton.tsx | 44 ++++++++++++++++++++++++++++++ react/src/components/Payment.tsx | 23 ++++------------ react/src/hooks/useOnPaid.ts | 27 ++++++++++++++++++ react/src/index.ts | 1 + react/yarn.lock | 8 +++--- src/api.ts | 2 +- src/components/flows/bc-payment.ts | 3 +- 12 files changed, 127 insertions(+), 48 deletions(-) create mode 100644 react/src/components/PayButton.tsx create mode 100644 react/src/hooks/useOnPaid.ts diff --git a/README.md b/README.md index 4b1318a..74cfc05 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ _Continue further down for the full Bitcoin Connect API._ ### React ```jsx -import {Button, init, launchModal, launchPaymentModal, closeModal, requestProvider, Connect, SendPayment} from '@getalby/bitcoin-connect-react'; +import {Button, PayButton, init, launchModal, launchPaymentModal, closeModal, requestProvider, Connect, SendPayment} from '@getalby/bitcoin-connect-react'; // Initialize Bitcoin Connect init({ @@ -93,6 +93,14 @@ init({ const {preimage} = await provider.sendPayment("lnbc..."); }}/> +// render a "Pay Now" button +// invoice can be unset initially - using the onClick function is a good time to fetch the invoice +// set the `payment` prop to override the payment status if a payment was made externally +