aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-11-26 22:35:28 -0800
committerAdam Soltys <[email protected]>2024-11-26 22:35:28 -0800
commitf99bd15fabd6d7b361794f756a69d6f05a50701f (patch)
tree9bb1b14519b34c343ad3bcd001fbd4681a53e447
parentf4eb6547d0db5edd0137fdefe461433500515040 (diff)
downloadcoinos-ui-f99bd15fabd6d7b361794f756a69d6f05a50701f.tar.gz
coinos-ui-f99bd15fabd6d7b361794f756a69d6f05a50701f.zip
warnings
-rw-r--r--src/components/Account.svelte16
-rw-r--r--src/components/AppHeader.svelte8
-rw-r--r--src/components/Code.svelte29
-rw-r--r--src/components/PasswordInput.svelte19
-rw-r--r--src/components/Pin.svelte9
-rw-r--r--src/locales/en.json13
-rw-r--r--src/routes/(app)/forgot/+page.svelte77
-rw-r--r--src/routes/(static)/docs/+page.svelte37
-rw-r--r--src/routes/(static)/forgot/+page.server.ts (renamed from src/routes/(app)/forgot/+page.server.ts)0
-rw-r--r--src/routes/(static)/forgot/+page.svelte66
-rw-r--r--src/routes/(static)/login/+page.svelte38
-rw-r--r--src/routes/(static)/register/+page.svelte50
12 files changed, 154 insertions, 208 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte
index 4db77066..e075b130 100644
--- a/src/components/Account.svelte
+++ b/src/components/Account.svelte
@@ -21,17 +21,25 @@
class="block space-y-2 border-primary py-4 cursor-pointer"
class:border-b-8={!last}
onclick={go}
+ aria-label="Payments"
>
<div class="flex">
<Balance {balance} {user} {rate} />
- <a href={`/payments`} class="contents" onclick={setAccount}>
+ <a
+ href={`/payments`}
+ class="contents"
+ onclick={setAccount}
+ aria-label="Payments"
+ >
<button class="ml-auto flex gap-1 mb-auto pb-4 pl-4">
- <iconify-icon icon="ph:clock-bold" width="32"></iconify-icon>
+ <iconify-icon icon="ph:clock-bold" width="32" aria-label="Payments"
+ ></iconify-icon>
</button>
</a>
- <a href={`/account/${id}`} class="contents">
+ <a href={`/account/${id}`} class="contents" aria-label="Settings">
<button class="flex gap-1 mb-auto pb-4 pl-4">
- <iconify-icon icon="ph:gear-bold" width="32"></iconify-icon>
+ <iconify-icon icon="ph:gear-bold" width="32" aria-label="Settings"
+ ></iconify-icon>
</button>
</a>
</div>
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte
index 495e855b..39f97d4e 100644
--- a/src/components/AppHeader.svelte
+++ b/src/components/AppHeader.svelte
@@ -27,19 +27,23 @@
{
href: `/${user?.username}`,
icon: "ph:house-bold",
+ label: "Home",
},
{
href: `/${user?.username}/receive`,
icon: "ph:hand-coins-bold",
flip: "horizontal",
+ label: "Receive",
},
{
href: `/payments`,
icon: "ph:clock-bold",
+ label: "Payments",
},
{
href: `/send`,
icon: "ph:paper-plane-right-bold",
+ label: "Send",
},
];
@@ -59,8 +63,8 @@
>
<nav class="flex justify-end items-center space-x-4 p-5">
{#if user}
- {#each links as { href, icon, flip }}
- <a {href} data-sveltekit-preload-code="eager">
+ {#each links as { href, icon, flip, label }}
+ <a {href} data-sveltekit-preload-code="eager" aria-label={label}>
<button class="btn-menu {opacity(href)}">
<iconify-icon {icon} width={w > 640 ? 32 : 24} {flip}
></iconify-icon>
diff --git a/src/components/Code.svelte b/src/components/Code.svelte
index 52c6bcf2..1c25feb8 100644
--- a/src/components/Code.svelte
+++ b/src/components/Code.svelte
@@ -4,28 +4,19 @@
let { sample } = $props();
</script>
+{#snippet cp(something)}
+ <button type="button" class="ml-auto" onclick={() => copy(something)}>
+ <iconify-icon icon="ph:copy-bold" width="42"></iconify-icon>
+ </button>
+{/snippet}
+
<div
class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 whitespace-pre-wrap font-mono items-center"
>
- <div class="overflow-x-scroll max-w-full scrollbar-hide">
+ <div
+ class="overflow-x-scroll max-w-full [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]"
+ >
<div class="break-all min-w-[800px]">{samples[sample]}</div>
</div>
- <iconify-icon
- icon="ph:copy-bold"
- onclick={() => copy(samples[sample])}
- width="42"
- class="ml-auto cursor-pointer"
- ></iconify-icon>
+ {@render cp(samples[sample])}
</div>
-
-<style>
- .scrollbar-hide::-webkit-scrollbar {
- display: none;
- }
-
- /* For IE, Edge and Firefox */
- .scrollbar-hide {
- -ms-overflow-style: none; /* IE and Edge */
- scrollbar-width: none; /* Firefox */
- }
-</style>
diff --git a/src/components/PasswordInput.svelte b/src/components/PasswordInput.svelte
index bac00134..eb5b0d6c 100644
--- a/src/components/PasswordInput.svelte
+++ b/src/components/PasswordInput.svelte
@@ -1,7 +1,11 @@
<script>
- let { value = $bindable(), focus = () => {} } = $props();
+ let {
+ show = $bindable(),
+ value = $bindable(),
+ focus = () => {},
+ placeholder,
+ } = $props();
let toggle = (e) => (show = !show);
- let show = $state(false);
let icon = $derived(show ? "ph:eye-bold" : "ph:eye-slash-bold");
</script>
@@ -10,9 +14,16 @@
class="input flex items-center justify-center gap-2 w-full"
>
{#if show}
- <input name="password" type="text" bind:value class="clean" />
+ <input name="password" type="text" bind:value class="clean" {placeholder} />
{:else}
- <input name="password" type="password" bind:value class="clean" use:focus />
+ <input
+ name="password"
+ type="password"
+ bind:value
+ class="clean"
+ use:focus
+ {placeholder}
+ />
{/if}
<button type="button" aria-label="Toggle" onclick={toggle} class="contents">
<iconify-icon {icon} width="32"></iconify-icon>
diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte
index fb17c612..a429e976 100644
--- a/src/components/Pin.svelte
+++ b/src/components/Pin.svelte
@@ -45,23 +45,22 @@
{#if loaded}
<div
+ role="dialog"
class="fixed bg-base-100 bg-opacity-90 inset-0 h-full w-full z-50 cursor-default"
on:click|preventDefault
+ aria-labelledby="title"
>
<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">{title}</h1>
+ <h1 id="title" class="text-center text-2xl font-semibold">{title}</h1>
<Pinpad bind:v={p} {cancel} />
<div>
<label for="locktime" class="font-bold"
>{$t("user.settings.rememberFor")}</label
>
- <select
- name="locktime"
- bind:value={locktime}
- >
+ <select name="locktime" bind:value={locktime}>
<option value={30}>30 {$t("user.settings.seconds")}</option>
<option value={5 * 60}>5 {$t("user.settings.minutes")}</option>
<option value={10 * 60}>10 {$t("user.settings.minutes")}</option>
diff --git a/src/locales/en.json b/src/locales/en.json
index c47bf051..10bccaba 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -146,7 +146,10 @@
},
"login": {
"forgot": "Forgot password?",
+ "resetSent": "A password reset link will be sent to your email if we have it on file.",
+ "continue": "Continue",
"reset": "Password reset",
+ "resetDesc": "We can send a reset link if you added and verified your email otherwise please reach out to",
"optional": "Optional",
"submit": "Submit",
"token": "2FA Token",
@@ -165,8 +168,8 @@
"failed": "Login failed"
},
"nav": {
- "dark": "Dark",
- "light": "Light",
+ "dark": "Dark",
+ "light": "Light",
"about": "About",
"docs": "Docs",
"messages": "Messages",
@@ -176,7 +179,7 @@
"profile": "Profile",
"register": "Register",
"account": "Account",
- "startInSeconds": "Start in Seconds",
+ "startInSeconds": "Start in Seconds",
"signIn": "Sign In",
"signOut": "Sign out",
"dashboard": "Dashboard",
@@ -244,7 +247,7 @@
"placeholder": "Enter an address, invoice or username"
},
"settings": {
- "pleaseEnterYourPassword": "Please enter your password",
+ "pleaseEnterYourPassword": "Please enter your password",
"memoPrompt": "Checkout instructions",
"memoPromptPlaceholder": "e.g. Please enter your name for the order",
"rememberFor": "Remember for",
@@ -359,7 +362,7 @@
"fundingAmount": "Funding amount",
"fast": "Fast",
"ecashDesc": "Ecash tokens are funded with bitcoin. They may be redeemed in exchange for a lightning payment equal to their value or loaded back into a coinos account at any time. They can also be transferred to other ecash wallets either wholly or partially. You might use them to give bitcoin to someone who doesn't have a wallet by texting the token to them or sharing a link to it. Learn more at",
- "ecashToken": "Ecash token",
+ "ecashToken": "Ecash token",
"medium": "Medium",
"maxFeeDesc": "Set an upper limit on the network routing fee. If set too low the payment may fail. You can set it higher and try again. We debit the full amount while attempting the payment but credit back any unused portion.",
"slow": "Slow",
diff --git a/src/routes/(app)/forgot/+page.svelte b/src/routes/(app)/forgot/+page.svelte
deleted file mode 100644
index eb981734..00000000
--- a/src/routes/(app)/forgot/+page.svelte
+++ /dev/null
@@ -1,77 +0,0 @@
-<script>
- import { focus } from "$lib/utils";
- import { enhance } from "$app/forms";
- import { t } from "$lib/translations";
- import Icon from "$comp/Icon.svelte";
- import Spinner from "$comp/Spinner.svelte";
- import { fly } from "svelte/transition";
-
- let { form } = $props();
- let email = $state();
- let loading;
-</script>
-
-<div class="pt-10">
- <div class="w-[243px] mx-auto mb-10">
- <a href="/">
- <Icon icon="logo" />
- </a>
- </div>
-
- <div class="flex justify-center items-center">
- <div
- class="shadow-xl rounded-3xl p-10 pb-12 space-y-5 w-full mx-5 md:mx-0 md:w-[400px]"
- >
- <h1 class="text-2xl font-bold text-center">{$t("login.reset")}</h1>
-
- {#if form?.error}
- <div class="text-red-600 text-center" in:fly>
- {form.error}
- </div>
- {/if}
-
- {#if form?.success}
- <div class="text-secondary">
- A password reset link will be sent to your email if we have it on
- file.
- </div>
-
- <div class="flex w-full">
- <a
- href="/login"
- class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80 text-center"
- >
- Continue
- </a>
- </div>
- {:else}
- <form class="space-y-5" method="POST" use:enhance>
- <div>
- <label for="email" class="font-semibold">{$t("login.email")}</label>
- <input
- name="email"
- type="text"
- required
- class="bg-primary"
- bind:value={email}
- use:focus
- autocapitalize="none"
- />
- </div>
-
- <button
- type="submit"
- class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
- disabled={loading}
- >
- {#if loading}
- <Spinner />
- {:else}
- {$t("login.submit")}
- {/if}
- </button>
- </form>
- {/if}
- </div>
- </div>
-</div>
diff --git a/src/routes/(static)/docs/+page.svelte b/src/routes/(static)/docs/+page.svelte
index 80052805..f4e2ffb9 100644
--- a/src/routes/(static)/docs/+page.svelte
+++ b/src/routes/(static)/docs/+page.svelte
@@ -18,12 +18,17 @@
: `export token=<your auth token>`;
</script>
+{#snippet cp(something)}
+ <button type="button" class="ml-auto" onclick={() => copy(something)}>
+ <iconify-icon icon="ph:copy-bold" width="42"></iconify-icon>
+ </button>
+{/snippet}
+
<div class="space-y-8 lg:text-xl mt-20 pt-20 md:pt-0 w-full max-w-full">
<h1 class="text-4xl">Documentation</h1>
<p class="text-secondary">
- Coinos is free and open source software. The code is
- available at <a
+ Coinos is free and open source software. The code is available at <a
href="https://github.com/coinos"
class="text-secondary underline">github.com/coinos</a
>
@@ -31,19 +36,18 @@
<p class="text-secondary">
Coinos has a REST API that can be used to register accounts and make
- payments. The following examples show how you can call the API
- with <a href="https://curl.se/" class="text-secondary underline">curl</a>
+ payments. The following examples show how you can call the API with <a
+ href="https://curl.se/"
+ class="text-secondary underline">curl</a
+ >
</p>
<h2 class="text-2xl">API Base URL</h2>
- <div class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center">
+ <div
+ class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center"
+ >
<div>{api}</div>
- <iconify-icon
- icon="ph:copy-bold"
- onclick={() => copy(api)}
- width="42"
- class="ml-auto cursor-pointer"
- ></iconify-icon>
+ {@render cp(api)}
</div>
<h2 class="text-2xl">Auth Token</h2>
@@ -58,14 +62,11 @@
Save it in a variable called <b>$token</b> to run the examples.
</p>
- <div class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center">
+ <div
+ class="bg-primary text-primary-content rounded-lg p-4 flex gap-4 items-center"
+ >
<div class="w-full break-all">{tokenSample}</div>
- <iconify-icon
- icon="ph:copy-bold"
- onclick={() => copy(tokenSample)}
- width="42"
- class="ml-auto cursor-pointer"
- ></iconify-icon>
+ {@render cp(tokenSample)}
</div>
<h2 class="text-2xl">POST /register</h2>
diff --git a/src/routes/(app)/forgot/+page.server.ts b/src/routes/(static)/forgot/+page.server.ts
index 55169a5e..55169a5e 100644
--- a/src/routes/(app)/forgot/+page.server.ts
+++ b/src/routes/(static)/forgot/+page.server.ts
diff --git a/src/routes/(static)/forgot/+page.svelte b/src/routes/(static)/forgot/+page.svelte
new file mode 100644
index 00000000..45180543
--- /dev/null
+++ b/src/routes/(static)/forgot/+page.svelte
@@ -0,0 +1,66 @@
+<script>
+ import { focus } from "$lib/utils";
+ import { enhance } from "$app/forms";
+ import { t } from "$lib/translations";
+ import Icon from "$comp/Icon.svelte";
+ import Spinner from "$comp/Spinner.svelte";
+ import { fly } from "svelte/transition";
+
+ let { form } = $props();
+ let email = $state();
+ let loading;
+</script>
+
+<div
+ class="mx-auto md:shadow-xl rounded-3xl max-w-xl w-full md:w-[480px] md:p-8 mb-20 space-y-5"
+>
+ <h1 class="text-2xl font-bold text-center">{$t("login.reset")}</h1>
+
+ {#if form?.error}
+ <div class="text-red-600 text-center" in:fly>
+ {form.error}
+ </div>
+ {/if}
+
+ {#if form?.success}
+ <div class="text-secondary">
+ {$t("login.resetSent")}
+ </div>
+
+ <div class="flex w-full">
+ <a href="/login" class="btn">
+ {$t("login.continue")}
+ </a>
+ </div>
+ {:else}
+ <form class="space-y-5" method="POST" use:enhance>
+ <div>
+ <input
+ placeholder={$t("login.email")}
+ name="email"
+ type="text"
+ required
+ class="bg-primary"
+ bind:value={email}
+ use:focus
+ autocapitalize="none"
+ />
+ </div>
+
+ <div class="text-secondary">
+ {$t("login.resetDesc")}
+ <a href="mailto:[email protected]" class="font-bold"
+ >
+ </div>
+
+ <button type="submit" class="btn" disabled={loading}>
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t("login.submit")}
+ {/if}
+ </button>
+ </form>
+ {/if}
+</div>
diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte
index 2b36dcfe..5caf48fb 100644
--- a/src/routes/(static)/login/+page.svelte
+++ b/src/routes/(static)/login/+page.svelte
@@ -8,6 +8,7 @@
import { fly } from "svelte/transition";
import { enhance } from "$app/forms";
import Pinpad from "$comp/Pinpad.svelte";
+ import PasswordInput from "$comp/PasswordInput.svelte";
import { focus, fail } from "$lib/utils";
import { password, pin, loginRedirect } from "$lib/store";
import { t } from "$lib/translations";
@@ -105,38 +106,7 @@
placeholder={$t("login.username")}
/>
- <label
- for="password"
- class="input flex items-center justify-center gap-2 w-full"
- >
- {#if revealPassword}
- <input
- class="clean"
- name="password"
- type="text"
- required
- bind:value={$password}
- autocapitalize="none"
- placeholder={$t("login.password")}
- />
- {:else}
- <input
- class="clean"
- name="password"
- type="password"
- required
- bind:value={$password}
- autocapitalize="none"
- placeholder={$t("login.password")}
- />
- {/if}
- <iconify-icon
- class="cursor-pointer ml-auto"
- onclick={() => (revealPassword = !revealPassword)}
- icon={revealPassword ? "ph:eye-bold" : "ph:eye-slash-bold"}
- width="32"
- ></iconify-icon>
- </label>
+ <PasswordInput bind:value={$password} placeholder={$t("login.password")} />
<div class="flex justify-end items-center">
<a href="/forgot" class="underline underline-offset-4 text-secondary"
@@ -164,11 +134,13 @@
<div
class="fixed bg-base-100 bg-opacity-90 inset-0 h-full w-full z-50 cursor-default"
onclick={(e) => e.stopPropagation()}
+ role="dialog"
+ aria-labelledby="title"
>
<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>
+ <h1 id="title" class="text-center text-2xl font-semibold">2FA</h1>
<Pinpad bind:v={token} {cancel} />
<div class="w-full flex">
diff --git a/src/routes/(static)/register/+page.svelte b/src/routes/(static)/register/+page.svelte
index 8a2a5015..147c361e 100644
--- a/src/routes/(static)/register/+page.svelte
+++ b/src/routes/(static)/register/+page.svelte
@@ -9,6 +9,7 @@
import Pin from "$comp/Pin.svelte";
import Spinner from "$comp/Spinner.svelte";
+ import PasswordInput from "$comp/PasswordInput.svelte";
import { focus, fail } from "$lib/utils";
import { avatar, password, loginRedirect } from "$lib/store";
@@ -27,6 +28,7 @@
let username = $state();
let { index } = $state(data);
+ let revealPassword = $state(false);
let cleared;
let clear = () => {
@@ -40,6 +42,8 @@
let refresh = async (e) => {
e.preventDefault();
+ cleared = false;
+
username = uniqueNamesGenerator({
dictionaries: [animals, NumberDictionary.generate({ min: 10, max: 99 })],
length: 2,
@@ -73,8 +77,6 @@
let email,
btn = $state();
- let revealPassword = $state();
-
let loading = $state();
async function handleSubmit(e) {
e.preventDefault();
@@ -245,45 +247,11 @@
>
</label>
- <label
- for="password"
- class="input flex items-center justify-center gap-2 w-full"
- >
- {#if revealPassword}
- <input
- class="clean"
- name="password"
- type="text"
- required
- bind:value={$password}
- autocapitalize="none"
- placeholder={$t("login.password")}
- />
- {:else}
- <input
- class="clean"
- name="password"
- type="password"
- required
- bind:value={$password}
- autocapitalize="none"
- onfocus={clear}
- placeholder={$t("login.password")}
- />
- {/if}
- <button
- type="button"
- tabindex="-1"
- onclick={() => (revealPassword = !revealPassword)}
- class="contents"
- aria-label="Toggle"
- >
- <iconify-icon
- icon={revealPassword ? "ph:eye-bold" : "ph:eye-slash-bold"}
- width="32"
- ></iconify-icon></button
- >
- </label>
+ <PasswordInput
+ bind:value={$password}
+ placeholder={$t("login.password")}
+ bind:show={revealPassword}
+ />
<button type="submit" class="btn" disabled={loading} bind:this={btn}>
{#if loading}