Files

114 lines
4.6 KiB
JavaScript
Raw Permalink Normal View History

2026-10-30 12:28:48 +01:00
// Generated, do not edit. sha256:29c2ecea7f7203cf2a789f574d5f90a158b4c2ec943bae44c680cc1e970776b5
2026-10-29 11:57:29 +01:00
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';
2026-10-30 12:28:48 +01:00
import { setPageHint } from '../ui/page-hint.js';
2026-10-29 11:57:29 +01:00
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 = `
2026-10-30 12:28:48 +01:00
<div class="card conv-card">
<div class="conv-head">
2026-10-29 11:57:29 +01:00
<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>
2026-10-30 12:28:48 +01:00
<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`)}
2026-10-29 11:57:29 +01:00
</div>
2026-10-30 12:28:48 +01:00
</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'),
] });
2026-10-29 11:57:29 +01:00
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>`;
}