Skip to content

Commit

Permalink
Merge pull request getAlby#249 from matjaz/currency-selector
Browse files Browse the repository at this point in the history
currency selector UI getAlby#199
  • Loading branch information
rolznz authored Aug 31, 2024
2 parents 6cd6829 + b52c47e commit 9ea813f
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 62 deletions.
22 changes: 18 additions & 4 deletions src/components/bc-balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export class Balance extends withTwind()(BitcoinConnectElement) {
@state()
_balanceSats: number | undefined;

@state()
_loading = false;

@state() _selectedCurrency: string | undefined;

constructor() {
Expand Down Expand Up @@ -52,24 +55,35 @@ export class Balance extends withTwind()(BitcoinConnectElement) {
}

private async _convertBalance() {
if (this._balanceSats === undefined) {
if (this._loading || this._balanceSats === undefined) {
return;
}

if (this._selectedCurrency && this._selectedCurrency !== 'sats') {
const currency = this._selectedCurrency || 'sats';

if (currency === 'BTC') {
const balanceSats = this._balanceSats / 1e8;
this._balance =
balanceSats.toLocaleString(undefined, {
minimumFractionDigits: 8,
useGrouping: true,
}) + ' BTC';
} else if (currency !== 'sats') {
try {
this._loading = true;
const fiatValue = await fiat.getFiatValue({
satoshi: this._balanceSats,
currency: this._selectedCurrency,
currency,
});
const convertedValue = parseFloat(fiatValue.toFixed(2));
this._balance = new Intl.NumberFormat(undefined, {
style: 'currency',
currency: this._selectedCurrency,
currency,
}).format(convertedValue);
} catch (error) {
console.error(error);
}
this._loading = false;
} else {
this._balance =
this._balanceSats.toLocaleString(undefined, {
Expand Down
128 changes: 70 additions & 58 deletions src/components/bc-currency-switcher.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
import {customElement, state} from 'lit/decorators.js';
import {BitcoinConnectElement} from './BitcoinConnectElement';
import {withTwind} from './twind/withTwind';
import {html} from 'lit';
import {css, 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';
import {getCurrencies} from '../utils/currencies';

@customElement('bc-currency-switcher')
export class CurrencySwitcher extends withTwind()(BitcoinConnectElement) {
@state() _isSwitchingCurrency = false;
@state() _selectedCurrency: string | undefined;

static override styles = [
...super.styles,
css`
.currencies-list {
mask-image: linear-gradient(
to bottom,
black calc(100% - 96px),
transparent 100%
);
}
/* width */
::-webkit-scrollbar {
width: 6px;
height: 18px;
}
/* Track */
::-webkit-scrollbar-track {
background: #66666666;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
`,
];

constructor() {
super();
this._selectedCurrency = store.getState().currency;
Expand All @@ -25,68 +58,47 @@ export class CurrencySwitcher extends withTwind()(BitcoinConnectElement) {
}

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',
});
if (!this._isSwitchingCurrency) {
return html`<div class="flex justify-center items-center gap-2">
<div
class="${classes.interactive}"
@click=${this._showSelectVisibility}
>
<slot></slot>
</div>
</div>`;
}
return html`<div class="flex justify-center items-center gap-2">
${this._isSwitchingCurrency
? html`
<br />
<select
class="${classes[
'text-brand-mixed'
]} bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
@change=${(e: Event) => this._handleCurrencyChange(e)}
>
${currencies.map(
(currency) => html`
<option
value=${currency.value}
?selected=${this._selectedCurrency === currency.value}
>
${currency.name}
</option>
`
)}
</select>
<div
class="${classes.interactive} ${classes['text-neutral-tertiary']}"
@click=${this._toggleSelectVisibility}
>
${crossIcon}
</div>
`
: html`<div
class="${classes.interactive}"
@click=${this._toggleSelectVisibility}

const currencies = getCurrencies();
const selectedCurrency = this._selectedCurrency || 'sats';

return html`<ul
class="h-48 overflow-y-scroll px-4 grid grid-cols-2 gap-3 currencies-list -mb-10"
>
${currencies.map(
(currency) => html`
<li
class="${selectedCurrency === currency.value
? 'bg-blue-500 text-white'
: ''} flex items-center justify-center py-2 px-4 hover:text-white hover:bg-blue-500 rounded-lg hover:border-blue-500 cursor-pointer"
@click=${() => this._selectCurrency(currency.value)}
>
<slot></slot>
</div>`}
</div>`;
<span class="text-orange-400 inline-block mr-2 text-xl"
>${currency.flag}</span
><span class="text-xl">${currency.value}</span>
</li>
`
)}
</ul>`;
}

private _toggleSelectVisibility() {
this._isSwitchingCurrency = !this._isSwitchingCurrency;
private _showSelectVisibility() {
this._isSwitchingCurrency = true;
}
private _handleCurrencyChange(e: Event) {
const selectedCurrency = (e.target as HTMLSelectElement).value;

private _selectCurrency(selectedCurrency: string) {
store.getState().setCurrency(selectedCurrency);
this._isSwitchingCurrency = false;
}
}

Expand Down
171 changes: 171 additions & 0 deletions src/utils/currencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
const getFlag = (countryCode: string): string =>
String.fromCodePoint(
...countryCode
.split('')
.map((letter) => 127397 + letter.toUpperCase().charCodeAt(0))
);

export const getCurrencies = () => {
const currencies: {name: string; value: string; flag: string}[] = [
{name: 'SATS', value: 'sats', flag: '₿'},
{name: 'BTC', value: 'BTC', flag: '₿'},
{name: 'USD', value: 'USD', flag: getFlag('US')},
{name: 'EUR', value: 'EUR', flag: '🇪🇺'},
];

// Cleaned up list from https://github.com/annexare/Countries
const ISOMapping = {
JP: 'JPY',
CN: 'CNY',
GB: 'GBP',
CH: 'CHF',
IN: 'INR',
AE: 'AED',
AF: 'AFN',
AL: 'ALL',
DZ: 'DZD',
AO: 'AOA',
AR: 'ARS',
AM: 'AMD',
AW: 'AWG',
AU: 'AUD',
AZ: 'AZN',
BS: 'BSD',
BH: 'BHD',
BD: 'BDT',
BB: 'BBD',
BY: 'BYN',
BZ: 'BZD',
BM: 'BMD',
BT: 'BTN',
BO: 'BOB',
BA: 'BAM',
BW: 'BWP',
BR: 'BRL',
BN: 'BND',
BG: 'BGN',
BI: 'BIF',
CV: 'CVE',
KH: 'KHR',
CA: 'CAD',
KY: 'KYD',
CF: 'XAF',
CL: 'CLP',
CO: 'COU',
KM: 'KMF',
CD: 'CDF',
CR: 'CRC',
CU: 'CUC',
CW: 'ANG',
CZ: 'CZK',
DK: 'DKK',
DJ: 'DJF',
DM: 'XCD',
DO: 'DOP',
EG: 'EGP',
ER: 'ERN',
ET: 'ETB',
FJ: 'FJD',
PF: 'XPF',
GM: 'GMD',
GE: 'GEL',
GH: 'GHS',
GI: 'GIP',
GT: 'GTQ',
GN: 'GNF',
GY: 'GYD',
HN: 'HNL',
HK: 'HKD',
HU: 'HUF',
IS: 'ISK',
ID: 'IDR',
IR: 'XDR',
IQ: 'IQD',
IL: 'ILS',
JM: 'JMD',
JO: 'JOD',
KZ: 'KZT',
KE: 'KES',
KR: 'KRW',
KW: 'KWD',
KG: 'KGS',
LA: 'LAK',
LB: 'LBP',
LR: 'LRD',
LY: 'LYD',
MO: 'MOP',
MG: 'MGA',
MW: 'MWK',
MY: 'MYR',
MV: 'MVR',
MR: 'MRU',
MU: 'MUR',
MX: 'MXN',
MD: 'MDL',
MN: 'MNT',
MA: 'MAD',
MZ: 'MZN',
MM: 'MMK',
NP: 'NPR',
NZ: 'NZD',
NI: 'NIO',
NE: 'XOF',
NG: 'NGN',
MK: 'MKD',
NO: 'NOK',
OM: 'OMR',
PK: 'PKR',
PG: 'PGK',
PY: 'PYG',
PE: 'PEN',
PH: 'PHP',
PL: 'PLN',
QA: 'QAR',
RO: 'RON',
RU: 'RUB',
RW: 'RWF',
SH: 'SHP',
WS: 'WST',
ST: 'STN',
SA: 'SAR',
RS: 'RSD',
SC: 'SCR',
SL: 'SLL',
SG: 'SGD',
SB: 'SBD',
SO: 'SOS',
ZA: 'ZAR',
SS: 'SSP',
LK: 'LKR',
SD: 'SDG',
SR: 'SRD',
SE: 'SEK',
SY: 'SYP',
TW: 'TWD',
TJ: 'TJS',
TZ: 'TZS',
TH: 'THB',
TO: 'TOP',
TT: 'TTD',
TN: 'TND',
TR: 'TRY',
TM: 'TMT',
UG: 'UGX',
UA: 'UAH',
UY: 'UYU',
UZ: 'UZS',
VU: 'VUV',
VN: 'VND',
YE: 'YER',
ZM: 'ZMW',
ZW: 'ZWL',
};
currencies.push(
...Object.entries(ISOMapping).map(([name, value]) => ({
name,
value,
flag: getFlag(name),
}))
);
return currencies;
};

0 comments on commit 9ea813f

Please sign in to comment.