115 lines
4.6 KiB
JavaScript
115 lines
4.6 KiB
JavaScript
// Generated, do not edit. sha256:29c2ecea7f7203cf2a789f574d5f90a158b4c2ec943bae44c680cc1e970776b5
|
|
import { t, localeTag } from '../i18n/index.js';
|
|
import { listAssets } from '../data/assets.js';
|
|
import { liveSpotFor, basePriceOf } from '../price/providers.js';
|
|
import { fmtMoney, fmtInt, currencySymbol, currencyName, parseLocaleNumber } from '../ui/format.js';
|
|
import { baseCurrency } from '../app-settings.js';
|
|
import { setPageHint } from '../ui/page-hint.js';
|
|
|
|
const TO_BTC = { btc: 1, mbtc: 1e-3, bit: 1e-6, sat: 1e-8, msat: 1e-11 };
|
|
const UNITS = ['fiat', 'btc', 'mbtc', 'bit', 'sat', 'msat'];
|
|
|
|
export const title = () => t('conversion.title');
|
|
|
|
const parseNumber = str => parseLocaleNumber(str) || 0;
|
|
|
|
function formatUnit(unit, value) {
|
|
if (value === 0) return '';
|
|
const nl = localeTag();
|
|
switch (unit) {
|
|
case 'fiat': return value.toLocaleString(nl, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
case 'btc': return value.toLocaleString(nl, { maximumFractionDigits: 8 });
|
|
case 'mbtc': return value.toLocaleString(nl, { maximumFractionDigits: 5 });
|
|
case 'bit': return value.toLocaleString(nl, { maximumFractionDigits: 2 });
|
|
case 'sat':
|
|
case 'msat': return Math.round(value).toLocaleString(nl);
|
|
}
|
|
}
|
|
|
|
export async function render(el) {
|
|
const n = fmtInt;
|
|
el.innerHTML = `
|
|
<div class="card conv-card">
|
|
<div class="conv-head">
|
|
<div class="t-label price-label">${t('conversion.btcRate')}</div>
|
|
<div class="t-value price-val" id="price-display">€ -</div>
|
|
<div class="price-meta" id="price-meta">${t('conversion.loading')}</div>
|
|
</div>
|
|
<div class="conv-grid">
|
|
${field('fiat', t('conversion.fiat'), '€ EUR', t('conversion.fiatHint'), true)}
|
|
${field('btc', 'Bitcoin', '₿ BTC', t('conversion.btcHint'))}
|
|
${field('mbtc', 'Millibitcoin', 'mBTC', `1 BTC = ${n(1e3)} mBTC`)}
|
|
${field('bit', 'Bit (µBTC)', 'bit', `1 BTC = ${n(1e6)} bits`)}
|
|
${field('sat', 'Satoshi', 'sat', `1 BTC = ${n(1e8)} sats`)}
|
|
${field('msat', 'Millisatoshi', 'msat', `1 sat = ${n(1e3)} msats · Lightning`)}
|
|
</div>
|
|
</div>`;
|
|
setPageHint(el, { title: t('conversion.hierarchy'), rows: [
|
|
`1 BTC = ${n(1e3)} mBTC = ${n(1e6)} bits = ${n(1e8)} sats = ${n(1e11)} msats`,
|
|
`1 mBTC = ${n(1e3)} bits = ${n(1e5)} sats`,
|
|
`1 bit = 100 sats · 1 sat = ${n(1e3)} msats`,
|
|
t('conversion.satNote'),
|
|
] });
|
|
|
|
const q = id => el.querySelector('#' + id);
|
|
const currency = baseCurrency();
|
|
let livePrice = null;
|
|
|
|
function updateAll(sourceUnit, sourceValue) {
|
|
const rate = livePrice;
|
|
let btcValue;
|
|
if (sourceUnit === 'fiat') {
|
|
if (!rate) return;
|
|
btcValue = sourceValue / rate;
|
|
} else {
|
|
btcValue = sourceValue * TO_BTC[sourceUnit];
|
|
}
|
|
UNITS.forEach(unit => {
|
|
if (unit === sourceUnit) return;
|
|
const value = unit === 'fiat' ? (rate ? btcValue * rate : 0) : btcValue / TO_BTC[unit];
|
|
q(unit).value = formatUnit(unit, value);
|
|
});
|
|
}
|
|
function recalcFromFiat() {
|
|
const f = q('fiat');
|
|
if (f.value) updateAll('fiat', parseNumber(f.value));
|
|
}
|
|
function renderPrice() {
|
|
const rate = livePrice;
|
|
q('price-display').textContent = rate ? fmtMoney(rate, currency, { decimals: 0 }) : `${currencySymbol(currency)} -`;
|
|
}
|
|
function applyCurrencyUI() {
|
|
q('fiat-name').textContent = currencyName(currency);
|
|
q('fiat-badge').textContent = `${currencySymbol(currency)} ${currency}`;
|
|
renderPrice();
|
|
recalcFromFiat();
|
|
}
|
|
|
|
UNITS.forEach(unit => q(unit).addEventListener('input', function () {
|
|
updateAll(unit, parseNumber(this.value));
|
|
}));
|
|
applyCurrencyUI();
|
|
const btc = (await listAssets()).find(a => a.symbol === 'BTC');
|
|
Promise.resolve(btc ? liveSpotFor(btc) : null).then(p => {
|
|
if (!p) { q('price-meta').textContent = t('conversion.rateUnavailable'); return; }
|
|
livePrice = basePriceOf(p);
|
|
renderPrice(); recalcFromFiat();
|
|
const now = new Date().toLocaleTimeString(localeTag(), { hour: '2-digit', minute: '2-digit' });
|
|
q('price-meta').textContent = `${t('conversion.updatedAt')} ${now}`;
|
|
}).catch(() => { q('price-meta').textContent = t('conversion.rateUnavailable'); });
|
|
}
|
|
|
|
function field(id, name, badge, hint, isFiat = false) {
|
|
const nameAttr = isFiat ? ' id="fiat-name"' : '';
|
|
const badgeAttr = isFiat ? ' id="fiat-badge"' : '';
|
|
return `
|
|
<label class="field">
|
|
<div class="field-head">
|
|
<span class="field-name"${nameAttr}>${name}</span>
|
|
<span class="badge"${badgeAttr}>${badge}</span>
|
|
</div>
|
|
<input type="text" class="field-input" id="${id}" placeholder="0" inputmode="decimal">
|
|
<span class="field-hint">${hint}</span>
|
|
</label>`;
|
|
}
|