aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-24 19:39:50 -0700
committerAdam Soltys <[email protected]>2026-06-24 19:39:50 -0700
commit9e2737e6736f9dd582d083c6d6fd669a28a99316 (patch)
tree94e45183fdc14a165b2e442a7bdfaf4dded5b648 /src/app.js
parent83e559684eed07d1a11fd0748ff571f8b8896921 (diff)
downloadcoinosv3-9e2737e6736f9dd582d083c6d6fd669a28a99316.tar.gz
coinosv3-9e2737e6736f9dd582d083c6d6fd669a28a99316.zip
Add dark mode; gift amount unit toggle; multi-input gifts
- Dark mode: theme variables + a footer "Dark mode/Light mode" toggle, persisted and applied pre-paint via a small head script (defaults to the OS preference). Hardcoded surfaces moved to CSS variables. - Gift amount: the unit label is now the clickable sats/BTC toggle, and the amount is parsed in the selected unit. - Gifts can now span multiple coins: createGift selects several confirmed coins (first input SIGHASH_SINGLE to lock the change, the rest SIGHASH_NONE), so a gift larger than any single coin works. Claim/preview sum all inputs and size the fee by input count. Verified end-to-end. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/app.js')
-rw-r--r--src/app.js28
1 files changed, 24 insertions, 4 deletions
diff --git a/src/app.js b/src/app.js
index 466524d..76474f5 100644
--- a/src/app.js
+++ b/src/app.js
@@ -123,12 +123,32 @@ function footer() {
installPrompt
? h('span', {}, h('span', { class: 'faint' }, ' · '),
h('button', { class: 'linklike', style: 'font-weight:400', onClick: triggerInstall }, t('installApp')))
- : null
+ : null,
+ h('span', { class: 'faint' }, ' · '),
+ h('button', { class: 'linklike', style: 'font-weight:400', onClick: toggleTheme }, resolvedTheme() === 'dark' ? t('lightMode') : t('darkMode'))
),
h('div', { style: 'margin-top:8px' }, languagePicker())
);
}
+const THEME_KEY = 'btc-wallet-theme';
+function resolvedTheme() {
+ try {
+ const s = localStorage.getItem(THEME_KEY);
+ if (s === 'dark' || s === 'light') return s;
+ } catch {}
+ try { return matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } catch {}
+ return 'light';
+}
+function applyTheme() {
+ try { document.documentElement.dataset.theme = resolvedTheme(); } catch {}
+}
+function toggleTheme() {
+ try { localStorage.setItem(THEME_KEY, resolvedTheme() === 'dark' ? 'light' : 'dark'); } catch {}
+ applyTheme();
+ render();
+}
+
// PWA install. Chrome fires beforeinstallprompt when the app qualifies; we stash
// the event and reveal an "Install app" link, then replay it on a user tap (the
// browser requires a gesture). beforeinstallprompt only fires when not already
@@ -958,7 +978,7 @@ function giftRate() {
function createGiftLink() {
const rate = giftRate();
const min = giftMinimum(rate);
- const amt = Math.round(Number(ui.giftAmount));
+ const amt = parseAmount(ui.giftAmount, unit); // entered in the current display unit
if (!amt || amt < min) { ui.giftError = t('giftAmountInvalid', { n: fmtAmount(min) + ' ' + unitLabel() }); render(); return; }
try {
ui.giftCode = wallet.createGift(amt, rate).code;
@@ -987,9 +1007,9 @@ function giftCard() {
)
: h('div', { class: 'col gap6' },
h('div', { class: 'input-group' },
- h('input', { type: 'number', min: String(giftMinimum(giftRate())), inputmode: 'numeric', placeholder: t('giftAmountLabel'),
+ h('input', { type: 'number', step: unit === 'sats' ? '1' : '0.00000001', min: '0', inputmode: 'decimal', placeholder: t('giftAmountLabel'),
value: ui.giftAmount, onInput: (e) => (ui.giftAmount = e.target.value) }),
- h('span', { class: 'small muted', style: 'align-self:center' }, unitLabel())
+ h('div', { style: 'display:flex;align-items:center' }, unitTag())
),
h('div', { class: 'small faint' }, t('giftMinNote', { n: fmtAmount(giftMinimum(giftRate())) + ' ' + unitLabel() })),
ui.giftError && h('div', { class: 'notice err' }, ui.giftError),