aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-11-09 11:28:04 -0800
committerAdam Soltys <[email protected]>2024-11-09 11:28:04 -0800
commit542c24b17643bc1a743c6d5eca3e3a29947c72b5 (patch)
tree300ecabce7e1b85c1aad64780e8404c3542dfad7 /src
parentd47895ec4d54feef1114029ba11188362fa1222d (diff)
downloadcoinos-ui-542c24b17643bc1a743c6d5eca3e3a29947c72b5.tar.gz
coinos-ui-542c24b17643bc1a743c6d5eca3e3a29947c72b5.zip
fix 2fa
Diffstat (limited to 'src')
-rw-r--r--src/components/Balance.svelte20
-rw-r--r--src/components/Pinpad.svelte13
-rw-r--r--src/lib/utils.ts20
-rw-r--r--src/routes/(app)/payments/[...page]/+page.svelte46
-rw-r--r--src/routes/(static)/login/+page.server.ts8
-rw-r--r--src/routes/(static)/login/+page.svelte81
6 files changed, 109 insertions, 79 deletions
diff --git a/src/components/Balance.svelte b/src/components/Balance.svelte
index e5f62d19..f92cd5ef 100644
--- a/src/components/Balance.svelte
+++ b/src/components/Balance.svelte
@@ -1,9 +1,6 @@
<script>
- import { preventDefault } from "svelte/legacy";
-
- import Icon from "$comp/Icon.svelte";
import Pin from "$comp/Pin.svelte";
- import { f, s, sat, sats } from "$lib/utils";
+ import { f, s, si, sat, sats } from "$lib/utils";
import { pin } from "$lib/store";
import { t } from "$lib/translations";
@@ -23,20 +20,21 @@
<div>
{#if user.haspin && !$pin}
<button onclick={toggleShow} class="flex gap-2 text-xl">
- <Icon icon="eye-off" style="w-8 my-auto" />
+ <iconify-icon icon="ph:eye-slash-bold" width="32"></iconify-icon>
<div class="my-auto">{$t("user.showBalance")}</div>
</button>
{:else}
- <div class="text-4xl font-bold tabular-nums">
+ <div class="text-4xl font-bold flex items-center">
+ <iconify-icon icon="ph:lightning-fill" width="32" class="text-yellow-300"></iconify-icon>
+ {s(balance)}
+ </div>
+
+ <div class="text-secondary text-xl">
{#if isNaN(rate)}
<div class="text-gray-200">&mdash;</div>
{:else}
- {f((balance * rate) / sats, user.currency)}
+ {si(((balance * rate) / sats).toFixed(2))} {user.currency}
{/if}
</div>
-
- <div class="text-secondary text-xl">
- ⚡️{s(balance)}
- </div>
{/if}
</div>
diff --git a/src/components/Pinpad.svelte b/src/components/Pinpad.svelte
index 93f31d08..72aaac81 100644
--- a/src/components/Pinpad.svelte
+++ b/src/components/Pinpad.svelte
@@ -1,5 +1,5 @@
<script>
- import { preventDefault, stopPropagation } from 'svelte/legacy';
+ import { preventDefault, stopPropagation } from "svelte/legacy";
import Left from "$comp/Left.svelte";
import Icon from "$comp/Icon.svelte";
@@ -64,7 +64,7 @@
width="32"
onclick={stopPropagation(preventDefault(() => (hide = !hide)))}
class="cursor-pointer"
-></iconify-icon>
+ ></iconify-icon>
</label>
<div class="grid grid-cols-3 gap-2 w-[300px] mx-auto grayscale">
@@ -73,7 +73,9 @@
<button
type="button"
class="btn"
- onclick={stopPropagation(preventDefault(() => handleInput(value)))}
+ onclick={((e) => e.preventDefault(),
+ e.stopPropagation(),
+ handleInput(value))}
>
<Left />
</button>
@@ -81,8 +83,9 @@
<button
type="button"
class="btn"
- onclick={stopPropagation(preventDefault(() => handleInput(value)))}
- >{value}</button
+ onclick={((e) => e.preventDefault(),
+ e.stopPropagation(),
+ handleInput(value))}>{value}</button
>
{/if}
{/each}
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index b27ce5f1..2779e34a 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -216,19 +216,21 @@ export const f = (s: number, currency: string): string => {
export const s = (s: number): string =>
new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(s);
-export const sat = (s: number): string => `⚡️${si(Math.abs(s)).toString()}`;
-
-export const si = (s: number) => {
- if (s < 1000) return s.toString(); // Return the original number if it's less than 1000
+export const sat = (s: number): string => `${si(Math.abs(s)).toString()}`;
+export const si = (n: number, minimumFractionDigits = 1, maximumFractionDigits = 2) => {
const units = ["", "K", "M", "G", "T", "P", "E", "Z"];
- const d = Math.floor(Math.log10(s) / 3); // Calculate the unit index
- const scaled = s / 1000 ** d; // Scale the number to the correct unit
+
+ let s = n;
+ let d = 0;
+ if (s >= 1000) {
+ d = Math.floor(Math.log10(s) / 3); // Calculate the unit index
+ s = n / 1000 ** d; // Scale the number to the correct unit
+ }
return (
- new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(
- scaled,
- ) + units[d]
+ new Intl.NumberFormat("en-US", { minimumFractionDigits, maximumFractionDigits }).format(s) +
+ units[d]
);
};
diff --git a/src/routes/(app)/payments/[...page]/+page.svelte b/src/routes/(app)/payments/[...page]/+page.svelte
index 2735491c..41433757 100644
--- a/src/routes/(app)/payments/[...page]/+page.svelte
+++ b/src/routes/(app)/payments/[...page]/+page.svelte
@@ -1,12 +1,12 @@
<script>
- import { run } from 'svelte/legacy';
+ import { run } from "svelte/legacy";
import Avatar from "$comp/Avatar.svelte";
import { onMount } from "svelte";
import { format } from "date-fns";
import { newPayment } from "$lib/store";
import { t } from "$lib/translations";
- import { get, f, s, sat, sats, types } from "$lib/utils";
+ import { get, f, s, si, sat, sats, types } from "$lib/utils";
import { page } from "$app/stores";
import { differenceInDays, getUnixTime, sub } from "date-fns";
import { goto, invalidate } from "$app/navigation";
@@ -46,11 +46,14 @@
},
]);
- let selection = $derived(start
- ? presets.findIndex(
- (p) => Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1,
- )
- : 0);
+ let selection = $derived(
+ start
+ ? presets.findIndex(
+ (p) =>
+ Math.abs(differenceInDays(new Date(start * 1000), p.start)) < 1,
+ )
+ : 0,
+ );
let p = $state(),
incoming = $state(),
@@ -68,9 +71,11 @@
$newPayment && invalidate(`/users/${user.username}`);
});
- let path = $derived($page.params.page
- ? $page.url.pathname.substring(0, $page.url.pathname.lastIndexOf("/"))
- : $page.url.pathname);
+ let path = $derived(
+ $page.params.page
+ ? $page.url.pathname.substring(0, $page.url.pathname.lastIndexOf("/"))
+ : $page.url.pathname,
+ );
let csv = async () => {
let url = `/payments`;
@@ -206,8 +211,17 @@
class:border-b-0={i === payments.length - 1}
>
<div class="whitespace-nowrap my-auto col-span-3">
- <div class="font-bold" class:text-red-800={p.amount < 0}>
- {f(Math.abs(p.amount) * (p.rate / sats), p.currency)}
+ <div
+ class="font-bold flex items-center"
+ class:text-red-800={p.amount < 0}
+ >
+ <iconify-icon
+ icon="ph:lightning-fill"
+ width="24"
+ class="text-yellow-300"
+ ></iconify-icon>
+ {si(Math.abs(p.amount) + (p.tip || 0), 0, 1)}
+
{#if p.tip}
<span class="text-sm text-secondary">
+{Math.round((p.tip / Math.abs(p.amount)) * 100)}%
@@ -215,8 +229,9 @@
{/if}
</div>
- <div class="text-secondary">
- {sat(Math.abs(p.amount) + (p.tip || 0))}
+ <div class="text-secondary flex items-center">
+ {si(Math.abs(p.amount) * (p.rate / sats), 2)}
+ {p.currency}
</div>
</div>
@@ -249,7 +264,8 @@
{:else if p.type === types.ecash}
<img src="/images/cash.png" class="w-12" />
{:else if p.type === types.reconcile}
- <iconify-icon icon="ph:scales-bold" width="32"></iconify-icon>
+ <iconify-icon icon="ph:scales-bold" width="32"
+ ></iconify-icon>
{:else if p.type === types.bitcoin}
<div class="my-auto">
<img
diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts
index f9108c50..d06c4895 100644
--- a/src/routes/(static)/login/+page.server.ts
+++ b/src/routes/(static)/login/+page.server.ts
@@ -12,14 +12,16 @@ export const actions = {
let { username, password, token, loginRedirect } = form;
const user = { username, password, token };
- console.log("LOGIN", user);
+ console.log("LOGIN", user, form);
if (loginRedirect === "undefined") loginRedirect = undefined;
try {
await login(user, cookies, request.headers.get("cf-connecting-ip"));
- } catch (e: any) {
- return fail(400, { error: "Login failed", message: e.message, ...form });
+ } catch (e) {
+ const { message } = e as Error;
+ console.log("message", message);
+ return fail(400, { error: "Login failed", message, ...form });
}
redirect(307, loginRedirect || `/${user.username}`);
diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte
index d50e0cdc..17b69f5e 100644
--- a/src/routes/(static)/login/+page.svelte
+++ b/src/routes/(static)/login/+page.svelte
@@ -1,14 +1,13 @@
<script>
- import { run, preventDefault } from 'svelte/legacy';
+ import { run, preventDefault } from "svelte/legacy";
+ import handler from "$lib/handler";
import { onMount } from "svelte";
import { applyAction, deserialize } from "$app/forms";
import { tick } from "svelte";
import { fly } from "svelte/transition";
import { enhance } from "$app/forms";
- import Pin from "$comp/Pin.svelte";
- import Icon from "$comp/Icon.svelte";
- import Spinner from "$comp/Spinner.svelte";
+ import Pinpad from "$comp/Pinpad.svelte";
import { focus, fail } from "$lib/utils";
import { password, pin, loginRedirect } from "$lib/store";
import { t } from "$lib/translations";
@@ -27,7 +26,7 @@
let { form } = $props();
- let token = $state(), formElement;
+ let token = $state("");
let code = [];
let redirect;
@@ -35,16 +34,20 @@
let cancel = () => (need2fa = false);
- let username = $state(), email, btn = $state();
-
- let update = (form) => form && ({ username, password: $password } = form);
+ let username = $state(),
+ email,
+ btn = $state();
+ $effect(() => form && ({ username, password: $password } = form));
+ let need2fa = $derived(form?.message === "2fa");
+ $effect(() => {
+ if (need2fa && form.token === token) token = "";
+ });
let revealPassword = $state(false);
- let loading = $state();
async function handleSubmit(e) {
- loading = true;
+ console.log("SUBMITTING");
let data = new FormData(this);
let user = Object.fromEntries(data);
@@ -65,39 +68,29 @@
}
applyAction(result);
- loading = false;
}
- let need2fa;
- run(() => {
- need2fa = form?.message === "2fa";
- });
- run(() => {
- if (form?.message === "2fa" && form.token === token) token = "";
- });
- run(() => {
- update(form);
- });
- run(() => {
- token && token?.length === 6 && tick().then(() => btn.click());
+
+ $effect(() => {
+ console.log("TOKEN", token);
+ token?.length === 6 &&
+ tick().then(() => {
+ btn.click();
+ });
});
</script>
-{#if need2fa}
- <Pin bind:value={token} title="Enter 2FA Code" {cancel} notify={false} />
-{/if}
-
<div
class="mx-auto md:shadow-xl rounded-3xl max-w-xl w-full md:w-[480px] md:p-8 mb-20"
>
<h1 class="text-2xl font-bold text-center">{$t("login.signIn")}</h1>
- {#if form?.error}
+ {#if form?.error && !form?.message.includes("2fa")}
<div class="text-red-600 text-center" in:fly>
{form.error}
</div>
{/if}
- <form class="space-y-5" onsubmit={preventDefault(handleSubmit)} method="POST">
+ <form use:enhance class="space-y-5" onsubmit={handleSubmit} method="POST">
<input
type="hidden"
name="loginRedirect"
@@ -145,7 +138,7 @@
onclick={() => (revealPassword = !revealPassword)}
icon={revealPassword ? "ph:eye-bold" : "ph:eye-slash-bold"}
width="32"
-></iconify-icon>
+ ></iconify-icon>
</label>
<div class="flex justify-end items-center">
@@ -154,12 +147,8 @@
>
</div>
- <button type="submit" class="btn" disabled={loading} bind:this={btn}>
- {#if loading}
- <Spinner />
- {:else}
- {$t("login.signIn")}
- {/if}
+ <button type="submit" class="btn" bind:this={btn}>
+ {$t("login.signIn")}
</button>
<p class="text-secondary text-center font-medium">
@@ -173,3 +162,23 @@
</p>
</form>
</div>
+
+{#if need2fa && token.length < 6}
+ <div
+ class="fixed bg-base-100 bg-opacity-90 inset-0 h-full w-full z-50 cursor-default"
+ onclick={(e) => e.stopPropagation()}
+ >
+ <div
+ class="mx-auto p-5 border shadow-lg rounded-md bg-base-100 space-y-5 max-w-lg"
+ >
+ <h1 class="text-center text-2xl font-semibold">2FA</h1>
+ <Pinpad bind:v={token} {cancel} />
+
+ <div class="w-full flex">
+ <button class="btn" onclick={cancel}>
+ <div class="my-auto">Cancel</div>
+ </button>
+ </div>
+ </div>
+ </div>
+{/if}