From ff1b82e90fa7b7158c1c71b916e8745de7180df0 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Thu, 21 Dec 2023 23:02:22 +0700 Subject: [PATCH 1/2] feat: add currency switcher --- src/components/BitcoinConnectElement.ts | 18 ++--- src/components/bc-balance.ts | 57 ++++++++++++--- src/components/bc-currency-switcher.ts | 97 +++++++++++++++++++++++++ src/components/bc-start.ts | 5 +- src/index.ts | 2 + src/state/store.ts | 8 ++ 6 files changed, 168 insertions(+), 19 deletions(-) create mode 100644 src/components/bc-currency-switcher.ts diff --git a/src/components/BitcoinConnectElement.ts b/src/components/BitcoinConnectElement.ts index 57140a6..d2d0102 100644 --- a/src/components/BitcoinConnectElement.ts +++ b/src/components/BitcoinConnectElement.ts @@ -56,15 +56,15 @@ export class BitcoinConnectElement extends InternalElement { this._modalOpen = store.getState().modalOpen; // TODO: handle unsubscribe - store.subscribe((store) => { - this._connected = store.connected; - this._connecting = store.connecting; - this._connectorName = store.connectorName; - this._appName = store.appName; - this._filters = store.filters; - this._error = store.error; - this._route = store.route; - this._modalOpen = store.modalOpen; + store.subscribe((currentState) => { + this._connected = currentState.connected; + this._connecting = currentState.connecting; + this._connectorName = currentState.connectorName; + this._appName = currentState.appName; + this._filters = currentState.filters; + this._error = currentState.error; + this._route = currentState.route; + this._modalOpen = currentState.modalOpen; }); } diff --git a/src/components/bc-balance.ts b/src/components/bc-balance.ts index 6320e81..f0bc8d5 100644 --- a/src/components/bc-balance.ts +++ b/src/components/bc-balance.ts @@ -4,6 +4,7 @@ import {BitcoinConnectElement} from './BitcoinConnectElement.js'; import {withTwind} from './twind/withTwind.js'; import {classes} from './css/classes.js'; import store from '../state/store.js'; +import {fiat} from '@getalby/lightning-tools'; /** * Displays the balance of the connected wallet (could be sats or fiat) @@ -13,15 +14,26 @@ export class Balance extends withTwind()(BitcoinConnectElement) { @state() _balance: string | undefined; + @state() + _balanceSats: number | undefined; + + @state() _selectedCurrency: string | undefined; + constructor() { super(); this._loadBalance(); - store.subscribe((currentStore, prevStore) => { + this._selectedCurrency = store.getState().currency; + store.subscribe((currentState, prevState) => { + this._selectedCurrency = currentState.currency; + if (currentState.currency !== prevState.currency) { + this._convertBalance(); + } + if ( - currentStore.connected !== prevStore.connected && - currentStore.connected + currentState.connected !== prevState.connected && + currentState.connected ) { this._loadBalance(); } @@ -35,10 +47,37 @@ export class Balance extends withTwind()(BitcoinConnectElement) { 'text-brand-mixed' ]}" > - ${this._balance || 0}  sats${this._balance || 'Loading...'} `; } + private async _convertBalance() { + if (!this._balanceSats) { + return; + } + + if (this._selectedCurrency && this._selectedCurrency !== 'sats') { + try { + const fiatValue = await fiat.getFiatValue({ + satoshi: this._balanceSats, + currency: this._selectedCurrency, + }); + const convertedValue = parseFloat(fiatValue.toFixed(2)); + this._balance = new Intl.NumberFormat(undefined, { + style: 'currency', + currency: this._selectedCurrency, + }).format(convertedValue); + } catch (error) { + console.error(error); + } + } else { + this._balance = + this._balanceSats.toLocaleString(undefined, { + useGrouping: true, + }) + ' sats'; + } + } + private _loadBalance() { (async () => { try { @@ -54,11 +93,11 @@ export class Balance extends withTwind()(BitcoinConnectElement) { 'The current WebLN provider does not support getBalance' ); } - const balance = await provider.getBalance(); - - this._balance = balance?.balance.toLocaleString(undefined, { - useGrouping: true, - }); + const balanceResponse = await provider.getBalance(); + if (balanceResponse) { + this._balanceSats = balanceResponse.balance; + this._convertBalance(); + } } catch (error) { this._balance = '⚠️'; // FIXME: better error handling diff --git a/src/components/bc-currency-switcher.ts b/src/components/bc-currency-switcher.ts new file mode 100644 index 0000000..8f5357f --- /dev/null +++ b/src/components/bc-currency-switcher.ts @@ -0,0 +1,97 @@ +import {customElement, state} from 'lit/decorators.js'; +import {BitcoinConnectElement} from './BitcoinConnectElement'; +import {withTwind} from './twind/withTwind'; +import {html} from 'lit'; +import './internal/bci-button'; +import './bc-connector-list'; +import './bc-balance'; +import {classes} from './css/classes'; +import store from '../state/store'; +import {crossIcon} from './icons/crossIcon'; + +@customElement('bc-currency-switcher') +export class CurrencySwitcher extends withTwind()(BitcoinConnectElement) { + @state() _isSwitchingCurrency = false; + @state() _selectedCurrency: string | undefined; + + constructor() { + super(); + this._selectedCurrency = store.getState().currency; + + // TODO: handle unsubscribe + store.subscribe((currentState) => { + this._selectedCurrency = currentState.currency; + }); + } + + override render() { + const currencies: {name: string; value: string}[] = [ + {name: 'SATS', value: 'sats'}, + ]; + + try { + // In case Intl.supportedValuesOf('currency') is unsupported, + // a fallback will be used + currencies.push( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...((Intl as any).supportedValuesOf('currency') as string[]).map( + (value) => ({name: value, value}) + ) + ); + } catch (error) { + currencies.push({ + name: 'USD', + value: 'USD', + }); + } + return html`
+ ${this._isSwitchingCurrency + ? html` +
+ +
+ ${crossIcon} +
+ ` + : html`
+ +
`} +
`; + } + + private _toggleSelectVisibility() { + this._isSwitchingCurrency = !this._isSwitchingCurrency; + } + private _handleCurrencyChange(e: Event) { + const selectedCurrency = (e.target as HTMLSelectElement).value; + store.getState().setCurrency(selectedCurrency); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'bc-currency-switcher': CurrencySwitcher; + } +} diff --git a/src/components/bc-start.ts b/src/components/bc-start.ts index 4943886..3e67c38 100644 --- a/src/components/bc-start.ts +++ b/src/components/bc-start.ts @@ -8,6 +8,7 @@ import {classes} from './css/classes'; import {disconnectSection} from './templates/disconnectSection'; import './bc-balance'; import store from '../state/store'; +import './bc-currency-switcher'; // TODO: split up this component into disconnected and connected @customElement('bc-start') @@ -39,7 +40,9 @@ export class Start extends withTwind()(BitcoinConnectElement) { ]}" >Balance - ` + + + ` : html`