aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-12-20 14:41:19 -0800
committerAdam Soltys <[email protected]>2023-12-20 14:41:19 -0800
commitfd137d961392ebd8f6da9f86e57a1d7dfe07eef1 (patch)
treec24455fd10f8632f1b1e1331379afb9902fcf075
parent19a8c100da3b4281b68e9b8afc837e6f93b53230 (diff)
downloadcoinos-ui-fd137d961392ebd8f6da9f86e57a1d7dfe07eef1.tar.gz
coinos-ui-fd137d961392ebd8f6da9f86e57a1d7dfe07eef1.zip
new on-chain send ui
-rwxr-xr-xbun.lockbbin0 -> 119377 bytes
-rw-r--r--package.json26
-rw-r--r--src/components/Numpad.svelte5
-rw-r--r--src/components/Toggle.svelte2
-rw-r--r--src/lib/utils.js5
-rw-r--r--src/locales/en.json2
-rw-r--r--src/routes/(app)/+layout.svelte6
-rw-r--r--src/routes/(app)/[username]/payments/[...page]/+page.svelte2
-rw-r--r--src/routes/(app)/payment/[id]/+page.svelte27
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/+page.svelte56
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js40
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte162
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.js36
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte134
-rw-r--r--src/routes/+page.svelte2
15 files changed, 269 insertions, 236 deletions
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 00000000..3216c9fd
--- /dev/null
+++ b/bun.lockb
Binary files differ
diff --git a/package.json b/package.json
index 96741f0a..1a3f9638 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,10 @@
},
"devDependencies": {
"@scure/base": "^1.1.1",
+ "@scure/bip39": "^1.2.1",
"@sveltejs/adapter-node": "^2.0.0",
"@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/line-clamp": "^0.4.2",
"@zerodevx/svelte-toast": "^0.7.2",
"autoprefixer": "^10.4.13",
@@ -24,6 +26,7 @@
"html5-qrcode": "^2.3.8",
"leaflet": "^1.9.3",
"leaflet.markercluster": "^1.5.3",
+ "maplibre-gl": "^3.3.1",
"nostr-tools": "^1.14.2",
"postcss": "^8.4.20",
"prettier": "2",
@@ -34,30 +37,17 @@
"rollup": "^2.79.1",
"screenfull": "^6.0.2",
"svelte": "^4.0.0",
+ "svelte-adapter-bun": "^0.5.1",
"svelte-outclick": "^3.3.3",
"svelte-preprocess": "^5.0.3",
"svelte-virtual-scroll-list": "^1.1.0",
"sveltekit-i18n": "^2.2.2",
"tailwind-scrollbar": "^2.1.0",
"tailwindcss": "^3.2.4",
+ "uint8array-extras": "^1.0.0",
+ "unique-names-generator": "^4.7.1",
"uuid": "^9.0.0",
- "vite": "^5.0.0",
- "@sveltejs/vite-plugin-svelte": "^3.0.0"
- },
- "pnpm": {
- "peerDependencyRules": {
- "allowedVersions": {
- "vite": "4"
- }
- }
+ "vite": "^5.0.0"
},
- "type": "module",
- "dependencies": {
- "@faker-js/faker": "^8.2.0",
- "@scure/bip39": "^1.2.1",
- "maplibre-gl": "^3.3.1",
- "svelte-adapter-bun": "^0.5.1",
- "uint8array-extras": "^0.5.0",
- "unique-names-generator": "^4.7.1"
- }
+ "type": "module"
}
diff --git a/src/components/Numpad.svelte b/src/components/Numpad.svelte
index 7006d7aa..72dad375 100644
--- a/src/components/Numpad.svelte
+++ b/src/components/Numpad.svelte
@@ -118,6 +118,8 @@
.replace(/\./g, "")
.replace("F", ".");
+ if (!clean) clean = "&nbsp;";
+
if (fiat) clean = clean.replace(",", "");
if (clean !== html) {
html = clean;
@@ -171,8 +173,9 @@
}
};
- let keydown = (e) =>
+ let keydown = (e) => {
e.key === "Enter" && (e.preventDefault() || submit.click());
+ };
</script>
<div class="flex justify-center items-center mb-3 px-3">
diff --git a/src/components/Toggle.svelte b/src/components/Toggle.svelte
index 1e4cf4c4..2f92837d 100644
--- a/src/components/Toggle.svelte
+++ b/src/components/Toggle.svelte
@@ -12,6 +12,6 @@
class="sr-only peer"
/>
<div
- class="w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700 peer-focus:ring-4 peer-focus:ring-gray-300 dark:peer-focus:ring-gray-800 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-gray-600"
+ class="w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700 peer-focus:ring-4 peer-focus:ring-gray-300 dark:peer-focus:ring-gray-800 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-500"
/>
</label>
diff --git a/src/lib/utils.js b/src/lib/utils.js
index f55b47b6..5303ec35 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -281,3 +281,8 @@ export function shuffleArray(array) {
[array[i], array[j]] = [array[j], array[i]];
}
}
+
+ export let closest = (a, n) =>
+ a.reduce((prev, curr) =>
+ Math.abs(curr - n) < Math.abs(prev - n) ? curr : prev
+ );
diff --git a/src/locales/en.json b/src/locales/en.json
index ac8b0075..cb42fa78 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -272,6 +272,8 @@
}
},
"payments": {
+ "networkFee": "Network fee",
+ "ourFee": "Our fee",
"create": "Create",
"fund": "Fund",
"addFunds": "Add Funds",
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
index e439d43e..0480fd79 100644
--- a/src/routes/(app)/+layout.svelte
+++ b/src/routes/(app)/+layout.svelte
@@ -18,7 +18,7 @@
import Password from "$comp/Password.svelte";
import Request from "$comp/Request.svelte";
import { warning, protectedRoutes } from "$lib/utils";
- import { t, locale } from "$lib/translations";
+ import { t, locale, loading } from "$lib/translations";
import { goto, onNavigate } from "$app/navigation";
export let data;
@@ -129,7 +129,9 @@
<SvelteToast options={{ reversed: true, intro: { y: 192 } }} />
<main class="pb-20">
- <slot />
+ {#if !$loading}
+ <slot />
+ {/if}
</main>
{#if $invoice}
diff --git a/src/routes/(app)/[username]/payments/[...page]/+page.svelte b/src/routes/(app)/[username]/payments/[...page]/+page.svelte
index 317f776b..1f20ea5c 100644
--- a/src/routes/(app)/[username]/payments/[...page]/+page.svelte
+++ b/src/routes/(app)/[username]/payments/[...page]/+page.svelte
@@ -238,7 +238,7 @@
{format(new Date(p.created), "h:mm aaa")}
</div>
<div>
- {format(new Date(p.created), "MMM d")}
+ {format(new Date(p.created), "MMM d, yyyy")}
</div>
</div>
</div>
diff --git a/src/routes/(app)/payment/[id]/+page.svelte b/src/routes/(app)/payment/[id]/+page.svelte
index a1c7bbed..15c17a00 100644
--- a/src/routes/(app)/payment/[id]/+page.svelte
+++ b/src/routes/(app)/payment/[id]/+page.svelte
@@ -1,5 +1,7 @@
<script>
- import { t } from "$lib/translations";
+ import { browser } from "$app/environment";
+ import { onMount, tick } from "svelte";
+ import { t, loading } from "$lib/translations";
import {
back,
copy,
@@ -30,9 +32,14 @@
fee = fee || 0;
- let direction = amount > 0 ? $t("payments.from") : $t("payments.to");
- direction =
- direction[0].toUpperCase() + direction.substr(1, direction.length);
+ let direction = "";
+ onMount(() => {
+ direction = amount > 0 ? $t("payments.from") : $t("payments.to");
+
+ if (direction)
+ direction =
+ direction[0].toUpperCase() + direction.substr(1, direction.length);
+ });
</script>
<a href={`/${username}/payments`}>
@@ -124,8 +131,8 @@
<div>
<span class="text-lg text-secondary">Date</span>
<div>
- {format(new Date(created), "MMMM d")},
- {format(new Date(created), "h:mm aaa")}
+ {format(new Date(created), "h:mmaaa")}
+ {format(new Date(created), "MMM d, yyyy")}
</div>
</div>
@@ -140,8 +147,6 @@
class="flex font-bold hover:opacity-80 mb-auto my-auto"
on:click={() => copy(id)}
><Icon icon="copy" style="ml-2 w-20 my-auto" />
-
-
</button>
</div>
</div>
@@ -151,7 +156,8 @@
<div>
<button
class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60"
- on:click={print}>
+ on:click={print}
+ >
<div class="mx-auto flex">
<Icon icon="printer" style="my-auto h-6 mr-2" />
<div class="my-auto mt-1 text-base">{$t("payments.print")}</div>
@@ -162,7 +168,8 @@
<div>
<a href={`/payment/${id}/plain`}>
<button
- class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60">
+ class="rounded-full border py-3 px-6 hover:opacity-80 flex w-full md:w-60"
+ >
<div class="mx-auto flex">
<Icon icon="printer" style="my-auto h-6 mr-2" />
<div class="my-auto mt-1 text-base">{$t("payments.print")}</div>
diff --git a/src/routes/(app)/send/bitcoin/[address]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/+page.svelte
new file mode 100644
index 00000000..7648d41f
--- /dev/null
+++ b/src/routes/(app)/send/bitcoin/[address]/+page.svelte
@@ -0,0 +1,56 @@
+<script>
+ import { t } from "$lib/translations";
+ import Icon from "$comp/Icon.svelte";
+ import Numpad from "$comp/Numpad.svelte";
+ import { page } from "$app/stores";
+ import { rate } from "$lib/store";
+ import { back, s } from "$lib/utils";
+
+ export let data;
+
+ let { rates } = data;
+ let { address } = $page.params;
+ let { balance, currency } = data.user;
+
+ let amount = 0;
+ let submit, fiat;
+
+ $: update(data);
+ let update = () => ($rate = data.rates[currency]);
+
+ let setMax = () => {
+ fiat = false;
+ amount = balance;
+ };
+</script>
+
+<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
+ <Icon icon="arrow-left" style="w-10" />
+</button>
+
+<div class="container px-4 max-w-xl mx-auto space-y-5 text-center">
+ <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t("payments.send")}</h1>
+
+ <div class="text-lg text-secondary break-all">{address}</div>
+
+ <Numpad bind:amount bind:fiat {currency} {submit} bind:rate={$rate} />
+
+ <div class="flex justify-center gap-2">
+ <button
+ type="button"
+ class="hover:opacity-80 bg-black text-white rounded-2xl py-3 px-4 mt-2 border font-bold"
+ on:click|preventDefault={setMax}
+ on:keydown={setMax}>Max ⚡️{s(balance)}</button
+ >
+
+ <form action={`./${address}/${amount}`}>
+ <button
+ bind:this={submit}
+ type="submit"
+ class="opacity-100 hover:opacity-80 rounded-2xl border py-3 font-bold mt-2 bg-black text-white px-4 w-24"
+ >
+ {$t("payments.next")}
+ </button>
+ </form>
+ </div>
+</div>
diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js
deleted file mode 100644
index 2998f22a..00000000
--- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.server.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { fail, redirect } from "@sveltejs/kit";
-import { auth, post, fd } from "$lib/utils";
-
-let seen = {};
-export const actions = {
- default: async ({ cookies, request }) => {
- try {
- let {
- address,
- amount,
- confirmed,
- feeRate,
- subtract,
- pin,
- stale,
- ts,
- } = await await fd(request);
- if (seen[ts]) return fail(400, { message: "Duplicate form submission" });
-
- let estimate = await post(
- "/bitcoin/fee",
- { address, amount, feeRate, subtract },
- auth(cookies)
- );
-
- let { tx } = estimate;
-
- if (stale || !confirmed) return fail(400, estimate);
-
- seen[ts] = 1;
- setTimeout(() => delete seen[ts], 30000);
-
- await post("/bitcoin/send", { pin, subtract, tx }, auth(cookies));
- } catch (e) {
- return fail(400, { message: e.message });
- }
-
- redirect(307, "/sent");
- },
-};
diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
deleted file mode 100644
index a8b86718..00000000
--- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
+++ /dev/null
@@ -1,162 +0,0 @@
-<script>
- import { tick } from "svelte";
- import { t } from "$lib/translations";
- import { enhance } from "$app/forms";
- import Toggle from "$comp/Toggle.svelte";
- import Slider from "$comp/Slider.svelte";
- import Icon from "$comp/Icon.svelte";
- import Numpad from "$comp/Numpad.svelte";
- import Spinner from "$comp/Spinner.svelte";
- import { page } from "$app/stores";
- import { fiat as toFiat, f, back, s, sat } from "$lib/utils";
- import { pin } from "$lib/store";
-
- export let data;
- export let form;
-
- let { rates } = data;
- let { address, amount } = $page.params;
- let { balance, currency } = data.user;
- let loading,
- submit,
- fiat,
- confirmed,
- feeRate,
- min,
- max,
- fee,
- ourfee,
- stale,
- subtract,
- adjusting;
-
- $: rate = rates[currency];
-
- let toggle = () => (loading = true);
- let toggleAdjusting = () => (adjusting = !adjusting);
-
- $: update(form);
- let update = () => {
- loading = false;
- if (form?.message?.includes("pin")) $pin = undefined;
- if (form) ({ min, max, feeRate, fee, ourfee, subtract } = form);
- if (!ourfee) ourfee = 0;
- if (feeRate) confirmed = true;
- stale = false;
- };
-
- let setMax = () => {
- fiat = false;
- amount = balance;
- subtract = true;
- };
-
- $: if (amount + fee > balance) subtract = true;
-
- let timeout;
- let handle = (e) => {
- stale = true;
- feeRate = e.target.value;
- clearTimeout(timeout);
- timeout = setTimeout(() => {
- tick().then(() => submit.click());
- }, 200);
- };
-</script>
-
-<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
- <Icon icon="arrow-left" style="w-10" />
-</button>
-
-{#if form?.message}
- <div class="text-red-600 text-center">
- {form.message}
- </div>
-{/if}
-
-<div class="container px-4 mt-20 max-w-xl mx-auto space-y-5 text-center">
- <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t("payments.send")}</h1>
-
- <div class="text-lg text-secondary break-all">{address}</div>
-
- {#if confirmed}
- <div class="text-center">
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(toFiat(amount, rate), currency)}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">⚡️{s(amount)}</h3>
- </div>
-
- <div class="text-center">
- <h2 class="text-secondary mb-5 text-lg">with fee</h2>
-
- <div class="relative">
- <h2 class="text-xl font-semibold">
- {f(toFiat(fee + ourfee, rate), currency)}
- </h2>
- <h3 class="text-secondary mb-6 mt-1">⚡️{s(fee + ourfee)}</h3>
- <button
- class="absolute right-1/4 top-0 text-secondary p-4"
- on:click={toggleAdjusting}
- ><Icon icon="settings" style="opacity-30" /></button
- >
- </div>
- </div>
- {:else}
- <Numpad bind:amount bind:fiat {currency} {submit} bind:rate />
- {/if}
-
- <form method="POST" use:enhance on:submit={toggle}>
- <input name="ts" value={Date.now()} type="hidden" />
- <input name="address" value={address} type="hidden" />
- <input name="amount" value={amount} type="hidden" />
- <input name="pin" value={$pin} type="hidden" />
- <input name="confirmed" value={confirmed} type="hidden" />
- <input name="feeRate" value={feeRate} type="hidden" />
- <input name="stale" value={stale} type="hidden" />
- <input name="subtract" value={subtract} type="hidden" />
-
- {#if confirmed && adjusting}
- <div class="relative mt-10 mb-20">
- <div class="absolute top-5 left-0 text-secondary">
- Slower (~40 blocks)
- </div>
- <div class="absolute top-5 right-0 text-secondary">
- Faster (~1 block)
- </div>
- <Slider bind:value={feeRate} {handle} {min} {max} />
- </div>
- <div class="flex justify-center gap-2 mb-10">
- <div class="my-auto">
- <Toggle id="subtract" checked={subtract} bind:value={subtract} />
- </div>
- <label for="subtract" class="text-secondary text-lg my-auto mb-2"
- >Take fee from amount</label
- >
- </div>
- {/if}
-
- <div class="flex justify-center gap-2">
- {#if !confirmed}
- <button
- type="button"
- class="hover:opacity-80 bg-black text-white rounded-2xl py-3 px-4 mt-2 border font-bold"
- on:click|preventDefault={setMax}
- on:keydown={setMax}>Max ⚡️{s(balance)}</button
- >
- {/if}
-
- <button
- bind:this={submit}
- type="submit"
- class="opacity-100 hover:opacity-80 rounded-2xl border py-3 font-bold mt-2 bg-black text-white px-4 w-24"
- disabled={loading}>
- {#if confirmed}
- {$t("payments.send")}
- {:else}
- {$t("payments.next")}
- {/if}
- </button>
- </div>
- </form>
-</div>
diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.js b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.js
new file mode 100644
index 00000000..08741296
--- /dev/null
+++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.js
@@ -0,0 +1,36 @@
+import { fail, redirect } from "@sveltejs/kit";
+import { auth, post, fd } from "$lib/utils";
+
+let seen = {};
+
+export async function load({ params: { address, amount, feeRate }, cookies }) {
+ try {
+ let fees = await fetch(
+ `https://mempool.space/api/v1/fees/recommended`
+ ).then((r) => r.json());
+
+ if (!feeRate) feeRate = fees.halfHourFee;
+
+ let { fee, ourfee, hex } = await post(
+ "/bitcoin/fee",
+ { address, amount, feeRate: feeRate },
+ auth(cookies)
+ );
+
+ return { amount, address, fee, fees, feeRate, ourfee, hex };
+ } catch (e) {
+ throw redirect(307, `/send/bitcoin/${address}`);
+ }
+}
+
+export const actions = {
+ default: async ({ cookies, request }) => {
+ try {
+ let r = await post("/bitcoin/send", await fd(request), auth(cookies));
+ } catch (e) {
+ return fail(400, { message: e.message });
+ }
+
+ redirect(307, "/sent");
+ },
+};
diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte
new file mode 100644
index 00000000..d583024c
--- /dev/null
+++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte
@@ -0,0 +1,134 @@
+<script>
+ import { t } from "$lib/translations";
+ import { enhance } from "$app/forms";
+ import Toggle from "$comp/Toggle.svelte";
+ import Icon from "$comp/Icon.svelte";
+ import Spinner from "$comp/Spinner.svelte";
+ import { page } from "$app/stores";
+ import { fiat as toFiat, f, back, s, sat, closest } from "$lib/utils";
+ import { pin } from "$lib/store";
+ import { goto, invalidate } from "$app/navigation";
+ import { rate } from "$lib/store";
+
+ export let data;
+ export let form;
+
+ let { amount, address, fee, fees, feeRate, ourfee, rates, hex } = data;
+
+ $: reload(data);
+ let reload = () => {
+ ({ amount, address, fee, fees, feeRate, ourfee, rates, hex } = data);
+ feeRate = closest(Object.values(fees), feeRate);
+ if (!$rate) $rate = rates[currency];
+ };
+
+ let { balance, currency } = data.user;
+ let submitting, submit, showSettings;
+
+ let feeNames = {
+ fastestFee: "Fastest",
+ halfHourFee: "Fast",
+ hourFee: "Medium",
+ economyFee: "Slow",
+ minimumFee: "Slowest",
+ };
+
+ let toggleSettings = () => (showSettings = !showSettings);
+
+ $: update(form);
+ let update = () => {
+ submitting = false;
+ if (form?.message?.includes("pin")) $pin = undefined;
+ };
+
+ let setFee = () => goto(`/send/bitcoin/${address}/${amount}/${feeRate}`);
+</script>
+
+<button class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80" on:click={back}>
+ <Icon icon="arrow-left" style="w-10" />
+</button>
+
+<div
+ class="container px-4 max-w-xl mx-auto space-y-5 text-center no-transition"
+>
+ {#if form?.message}
+ <div class="mb-5">
+ <div class="text-red-600">{form.message}</div>
+ </div>
+ {/if}
+
+ <h1 class="text-3xl md:text-4xl font-semibold mb-2">{$t("payments.send")}</h1>
+
+ <div class="text-xl text-secondary break-all">{address}</div>
+
+ <div class="text-center">
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(toFiat(amount, $rate), currency)}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">⚡️{s(amount)}</h3>
+ </div>
+
+ <div class="text-center">
+ <h2 class="text-secondary text-lg">{$t("payments.networkFee")}</h2>
+
+ <div class="flex flex-wrap gap-4 justify-center">
+ <select
+ bind:value={feeRate}
+ on:change={setFee}
+ class="border text-lg bg-white p-4 rounded-2xl text-center my-auto"
+ >
+ {#each Object.keys(fees) as feeName}
+ <option value={fees[feeName]}
+ >{feeNames[feeName]} &mdash; ⚡️{fees[feeName]}/vb</option
+ >
+ {/each}
+ </select>
+ <div class="my-auto">
+ <h2 class="text-xl">
+ {f(toFiat(fee, $rate), currency)}
+ </h2>
+ <h3 class="text-secondary">⚡️{s(fee)}</h3>
+ </div>
+ </div>
+ </div>
+
+ <div class="text-center">
+ <h2 class="text-secondary text-lg">{$t("payments.ourFee")}</h2>
+
+ <div class="flex flex-wrap gap-4 justify-center">
+ <div class="my-auto">
+ <h2 class="text-xl">
+ {f(toFiat(ourfee, $rate), currency)}
+ </h2>
+ <h3 class="text-secondary">⚡️{s(ourfee)}</h3>
+ </div>
+ </div>
+ </div>
+
+ <form method="POST" use:enhance on:submit={() => (submitting = true)}>
+ <input name="pin" value={$pin} type="hidden" />
+ <input name="hex" value={hex} type="hidden" />
+ <input name="rate" value={$rate} type="hidden" />
+
+ <div class="flex justify-center gap-2">
+ <button
+ bind:this={submit}
+ type="submit"
+ class="opacity-100 hover:opacity-80 rounded-2xl border py-3 font-bold mt-2 bg-black text-white px-4 w-24"
+ disabled={submitting}
+ >
+ {#if submitting}
+ <Spinner />
+ {:else}
+ {$t("payments.send")}
+ {/if}
+ </button>
+ </div>
+ </form>
+</div>
+
+<style>
+ .no-transition {
+ view-transition-name: fee;
+ }
+</style>
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 3cffea21..23aaacfa 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,4 +1,4 @@
-<script>
+ <script>
import LandingHeader from "$comp/LandingHeader.svelte";
import LandingHero from "$comp/LandingHero.svelte";
import LandingInfoCard from "$comp/LandingInfoCard.svelte";