Prepare product layout and reusable Flutter theme
Relocate the existing Astro website and preserve its assets and hosting configuration. Add the consumer/backend scaffold and themed Flutter package with consumer tests. Related to #1.
This commit is contained in:
1 parent
ef65a40213
commit
d9318053e8
46 files changed
+703
-1
No files matched your search
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
calculateQuote,
|
||||
parseAmount,
|
||||
type Crypto,
|
||||
type Fiat,
|
||||
} from '../lib/quote';
|
||||
const amount = document.querySelector<HTMLInputElement>('#send-amount')!;
|
||||
const crypto = document.querySelector<HTMLSelectElement>('#crypto')!;
|
||||
const fiat = document.querySelector<HTMLSelectElement>('#fiat')!;
|
||||
const output = document.querySelector<HTMLOutputElement>('#receive-amount')!;
|
||||
const error = document.querySelector<HTMLElement>('#amount-error')!;
|
||||
const lang = document.documentElement.lang;
|
||||
const money = new Intl.NumberFormat(lang === 'ru' ? 'ru-RU' : 'en-GB', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
const rateFormat = new Intl.NumberFormat(lang === 'ru' ? 'ru-RU' : 'en-GB', {
|
||||
maximumFractionDigits: 4,
|
||||
});
|
||||
const submit = document.querySelector<HTMLButtonElement>('.exchange-submit')!;
|
||||
function updateQuote() {
|
||||
const coin = document.querySelector<HTMLElement>('#coin-icon')!;
|
||||
coin.textContent = { BTC: '₿', ETH: 'Ξ', USDT: '₮' }[crypto.value]!;
|
||||
coin.className = `coin coin-${crypto.value.toLowerCase()}`;
|
||||
document.querySelector('#fiat-icon')!.textContent =
|
||||
fiat.value === 'EUR' ? '€' : '$';
|
||||
try {
|
||||
const result = calculateQuote(
|
||||
parseAmount(amount.value),
|
||||
crypto.value as Crypto,
|
||||
fiat.value as Fiat,
|
||||
);
|
||||
output.textContent = money.format(result.receive);
|
||||
output.classList.toggle('long-amount', output.textContent.length > 12);
|
||||
document.querySelector('#quote-rate')!.textContent =
|
||||
`1 ${crypto.value} ≈ ${rateFormat.format(result.rate)} ${fiat.value}`;
|
||||
document.querySelector('#quote-fee')!.textContent =
|
||||
`${money.format(result.fee)} ${fiat.value}`;
|
||||
error.hidden = true;
|
||||
amount.setAttribute('aria-invalid', 'false');
|
||||
submit.disabled = false;
|
||||
return result;
|
||||
} catch {
|
||||
output.textContent = '—';
|
||||
document.querySelector('#quote-fee')!.textContent = '—';
|
||||
document.querySelector('#quote-rate')!.textContent = '—';
|
||||
error.hidden = false;
|
||||
amount.setAttribute('aria-invalid', 'true');
|
||||
submit.disabled = true;
|
||||
}
|
||||
}
|
||||
amount.addEventListener('input', updateQuote);
|
||||
crypto.addEventListener('change', updateQuote);
|
||||
fiat.addEventListener('change', updateQuote);
|
||||
updateQuote();
|
||||
const dialog = document.querySelector<HTMLDialogElement>('#account-dialog')!;
|
||||
const shell = document.querySelector<HTMLElement>('.hero-shell')!;
|
||||
let opener: HTMLElement | null = null;
|
||||
document.querySelectorAll<HTMLButtonElement>('[data-auth]').forEach((button) =>
|
||||
button.addEventListener('click', () => {
|
||||
const destination =
|
||||
button.dataset.auth === 'login'
|
||||
? shell.dataset.loginUrl
|
||||
: shell.dataset.registerUrl;
|
||||
if (destination) {
|
||||
const url = new URL(destination);
|
||||
url.searchParams.set('lang', lang);
|
||||
window.location.assign(url.toString());
|
||||
return;
|
||||
}
|
||||
opener = button;
|
||||
dialog.showModal();
|
||||
document.body.classList.add('modal-open');
|
||||
}),
|
||||
);
|
||||
dialog
|
||||
.querySelectorAll('[data-close]')
|
||||
.forEach((button) => button.addEventListener('click', () => dialog.close()));
|
||||
dialog.addEventListener('click', (e) => {
|
||||
if (e.target !== dialog) return;
|
||||
const r = dialog.getBoundingClientRect();
|
||||
if (
|
||||
e.clientX < r.left ||
|
||||
e.clientX > r.right ||
|
||||
e.clientY < r.top ||
|
||||
e.clientY > r.bottom
|
||||
)
|
||||
dialog.close();
|
||||
});
|
||||
dialog.addEventListener('close', () => {
|
||||
document.body.classList.remove('modal-open');
|
||||
opener?.focus();
|
||||
});
|
||||
document
|
||||
.querySelectorAll<HTMLAnchorElement>('.language-switch a, .footer-locales a')
|
||||
.forEach((a) =>
|
||||
a.addEventListener('click', () => {
|
||||
a.hash = window.location.hash;
|
||||
}),
|
||||
);
|
||||
Reference in new issue
Block a user