aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-02-06 16:40:14 -0800
committerAdam Soltys <[email protected]>2023-02-06 16:40:14 -0800
commit362674fa4055fdd95c71fd7167891cf569a6cc2e (patch)
treef0fba3315e9bd585f819ca946f3dfe3d4b98ddf3 /src/components
parent7f864350d156ade70675128be9838f9183d1d7aa (diff)
downloadcoinos-ui-362674fa4055fdd95c71fd7167891cf569a6cc2e.tar.gz
coinos-ui-362674fa4055fdd95c71fd7167891cf569a6cc2e.zip
new pinpad component
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Feed.svelte4
-rw-r--r--src/components/Password.svelte6
-rw-r--r--src/components/Pin.svelte26
-rw-r--r--src/components/Pinpad.svelte81
-rw-r--r--src/components/RegisterOrLogin.svelte203
-rw-r--r--src/components/index.js1
6 files changed, 193 insertions, 128 deletions
diff --git a/src/components/Feed.svelte b/src/components/Feed.svelte
index ab757fa1..7de087a7 100644
--- a/src/components/Feed.svelte
+++ b/src/components/Feed.svelte
@@ -4,7 +4,7 @@
import { Event } from '$comp';
import { browser } from '$app/environment';
import VirtualScroll from 'svelte-virtual-scroll-list';
- import { failure } from '$lib/utils';
+ import { fail } from '$lib/utils';
import { sign, send } from '$lib/nostr';
export let events;
@@ -34,7 +34,7 @@
events = events;
} catch (e) {
console.log(e, e.stack);
- failure('Problem posting event');
+ fail('Problem posting event');
}
};
diff --git a/src/components/Password.svelte b/src/components/Password.svelte
index fd39441c..a84c0b16 100644
--- a/src/components/Password.svelte
+++ b/src/components/Password.svelte
@@ -1,6 +1,6 @@
<script>
import { Icon } from '$comp';
- import { focus, failure, post } from '$lib/utils';
+ import { focus, fail, post } from '$lib/utils';
import { password as pw, passwordPrompt } from '$lib/store';
export let user;
@@ -17,7 +17,7 @@
$passwordPrompt = false;
$pw = password;
} catch (e) {
- failure('Invalid password, try again');
+ fail('Invalid password, try again');
}
};
</script>
@@ -51,7 +51,7 @@
<button
type="button"
on:click={() => (revealPassword = !revealPassword)}
- class="absolute right-5 top-5"
+ class="absolute right-5 top-6"
>
<Icon icon={revealPassword ? 'eye' : 'eye-off'} />
</button>
diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte
index 7225ac37..0db831ff 100644
--- a/src/components/Pin.svelte
+++ b/src/components/Pin.svelte
@@ -1,24 +1,28 @@
<script>
+ import { t } from '$lib/translations';
import { focus } from '$lib/utils';
- import { pin } from '$lib/store';
+ import { Pinpad } from '$comp';
- let pinCode = '';
- let pinInput;
+ export let value = '';
+ export let title = $t('user.settings.verifyPIN');
+ export let cancel = () => (pin = '');
- let cancel = () => ($pin = '000000');
+ let pin = '';
- $: pinCode.length > 5 && ($pin = pinCode);
+ $: update(value);
+ let update = (v) => (pin = v) || console.log('updated', v, pin);
+
+ $: pin.toString().length > 5 && (value = pin.toString());
</script>
-<div class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20">
- <div class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5">
- <h1 class="text-center text-2xl font-semibold">Please enter your PIN</h1>
- <input bind:value={pinCode} use:focus />
+<div class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
+ <div class="relative top-40 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5">
+ <h1 class="text-center text-2xl font-semibold">{title}</h1>
+ <Pinpad bind:v={pin} {cancel} />
<div class="w-full flex">
<button
- type="submit"
class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto"
- on:click={cancel}
+ on:click|preventDefault={cancel}
>
<div class="my-auto">Cancel</div>
</button>
diff --git a/src/components/Pinpad.svelte b/src/components/Pinpad.svelte
new file mode 100644
index 00000000..bf3778f7
--- /dev/null
+++ b/src/components/Pinpad.svelte
@@ -0,0 +1,81 @@
+<script>
+ import { Left, Icon } from '$comp';
+ import { pin, selectedRate } from '$lib/store';
+ import { f, s, focus, post, warning, sat, sats } from '$lib/utils';
+ import { t } from '$lib/translations';
+
+ export let v;
+ export let cancel;
+
+ let arrow = '<';
+ let loading = false;
+
+ const numPad = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', '0', '<'];
+
+ const handleInput = (n) => {
+ v = (v || '').toString();
+ if (n === 'C') return (v = '');
+ if (n === '<') return (v = v.length > 1 ? Math.floor(v / 10) : '');
+ if (v.length > 5) n = '';
+ if (parseInt(n) >= 0) v += n;
+ };
+
+ let validate = (e) => {
+ let k = e.key || e.code;
+ if (k === 'Escape') cancel();
+ if (e.key.length <= 1 && !(e.metaKey || e.ctrlKey || e.altKey) && !(k >= '0' && k <= '9')) {
+ if (e.preventDefault) e.preventDefault();
+ else e.returnValue = false;
+ }
+ };
+
+ let hide = true;
+</script>
+
+<div class="flex justify-center items-center">
+ <div class="space-y-5">
+ <div class="text-5xl md:text-6xl font-semibold relative w-[300px]">
+ {#if hide}
+ <input
+ on:keydown={validate}
+ use:focus
+ bind:value={v}
+ type="password"
+ class="outline-none border-0 text-center py-0"
+ pattern="[0-9]+"
+ />
+ {:else}
+ <input
+ on:keydown={validate}
+ bind:value={v}
+ class="outline-none border-0 text-center py-0"
+ type="text"
+ pattern="[0-9]+"
+ />
+ {/if}
+ <button type="button" on:click={() => (hide = !hide)} class="absolute -right-5 top-5">
+ <Icon icon={hide ? 'eye-off' : 'eye'} style="w-8" />
+ </button>
+ </div>
+
+ <div class="grid grid-cols-3 gap-2 w-[300px] mx-auto grayscale">
+ {#each numPad as value}
+ {#if value === arrow}
+ <button
+ type="button"
+ class="bg-primary rounded-xl py-4 px-8 font-semibold active:bg-black active:text-white flex justify-center items-center hover:opacity-80"
+ on:click={() => handleInput(value)}
+ >
+ <Left />
+ </button>
+ {:else}
+ <button
+ type="button"
+ class="bg-primary rounded-xl py-4 px-8 font-semibold active:bg-black active:text-white hover:opacity-80"
+ on:click={() => handleInput(value)}>{value}</button
+ >
+ {/if}
+ {/each}
+ </div>
+ </div>
+</div>
diff --git a/src/components/RegisterOrLogin.svelte b/src/components/RegisterOrLogin.svelte
index 7ac2fbb3..6c099178 100644
--- a/src/components/RegisterOrLogin.svelte
+++ b/src/components/RegisterOrLogin.svelte
@@ -1,28 +1,32 @@
<script>
import { applyAction, deserialize } from '$app/forms';
import { tick } from 'svelte';
- import { Pincode, PincodeInput } from 'svelte-pincode';
import { fly } from 'svelte/transition';
import { enhance } from '$app/forms';
- import { Icon, Spinner } from '$comp';
- import { focus } from '$lib/utils';
+ import { Pin, Icon, Spinner } from '$comp';
+ import { focus, fail } from '$lib/utils';
import { password, loginRedirect } from '$lib/store';
import { t } from '$lib/translations';
import { page } from '$app/stores';
import { generate } from '$lib/nostr';
import { invalidateAll } from '$app/navigation';
- let token, tokenInput, formElement;
+ export let form;
+ export let pageID;
+
+ let token, formElement;
let code = [];
let redirect;
$password = undefined;
$: need2fa = form?.message === '2fa';
- $: if (need2fa) tokenInput && tick().then(tokenInput.focusFirstInput);
-
- export let form;
- export let pageID;
+ let cancel = () => (need2fa = false);
+ $: if (form?.message === '2fa' && form.token === token) {
+ token = '';
+ console.log('TOKEN', token);
+ fail('Invalid token, try again');
+ }
let username, email, btn;
@@ -66,6 +70,10 @@
}
</script>
+{#if need2fa}
+ <Pin bind:value={token} title="Enter 2FA Code" {cancel} />
+{/if}
+
<div class="pt-10">
<div class="w-[243px] mx-auto mb-10">
<a href="/">
@@ -75,126 +83,97 @@
<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]">
- {#if need2fa}
- <h1 class="text-2xl font-bold text-center">Enter 2FA Code</h1>
+ <h1 class="text-2xl font-bold text-center">{$t('login.' + pageID)}</h1>
- {#if form?.token}
- <div class="text-red-600 text-center" in:fly>Invalid token, try again</div>
- {/if}
-
- <form class="space-y-5" {action} method="POST" use:enhance>
- <input type="hidden" name="username" value={username} />
- <input type="hidden" name="password" value={$password} />
- <input type="hidden" name="token" value={token} />
-
- <div>
- <Pincode bind:code bind:value={token} bind:this={tokenInput}>
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- </Pincode>
- </div>
- <button
- bind:this={btn}
- type="submit"
- class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
- >{$t('login.' + pageID)}
- </button>
- </form>
- {:else}
- <h1 class="text-2xl font-bold text-center">{$t('login.' + pageID)}</h1>
+ {#if form?.error}
+ <div class="text-red-600 text-center" in:fly>
+ {form.error}
+ </div>
+ {/if}
- {#if form?.error}
- <div class="text-red-600 text-center" in:fly>
- {form.error}
- </div>
- {/if}
+ <form class="space-y-5" on:submit|preventDefault={handleSubmit} method="POST">
+ <input type="hidden" name="loginRedirect" value={$loginRedirect} />
+ <input type="hidden" name="token" value={token} />
- <form class="space-y-5" on:submit|preventDefault={handleSubmit} method="POST">
- <input type="hidden" name="loginRedirect" value={$loginRedirect} />
+ <div>
+ <label for="username" class="font-semibold">{$t('login.username')}</label>
+ <input
+ name="username"
+ type="text"
+ required
+ class="bg-primary"
+ bind:value={username}
+ use:focus
+ autocapitalize="none"
+ />
+ </div>
- <div>
- <label for="username" class="font-semibold">{$t('login.username')}</label>
+ <div class="relative">
+ <label for="password" class="block font-semibold">{$t('login.password')}</label>
+ {#if revealPassword}
<input
- name="username"
+ name="password"
type="text"
required
class="bg-primary"
- bind:value={username}
- use:focus
+ bind:value={$password}
autocapitalize="none"
/>
- </div>
-
- <div class="relative">
- <label for="password" class="block font-semibold">{$t('login.password')}</label>
- {#if revealPassword}
- <input
- name="password"
- type="text"
- required
- class="bg-primary"
- bind:value={$password}
- autocapitalize="none"
- />
- {:else}
- <input
- name="password"
- type="password"
- required
- class="bg-primary"
- bind:value={$password}
- autocapitalize="none"
- />
- {/if}
- <button
- type="button"
- on:click={() => (revealPassword = !revealPassword)}
- class="absolute right-5 top-10"
- >
- <Icon icon={revealPassword ? 'eye' : 'eye-off'} />
- </button>
- </div>
-
- {#if pageID === 'register'}
- <p class="text-secondary text-sm">
- {$t('login.passwordRecommendation')}
- </p>
{:else}
- <!-- TODO -->
- <!-- <div class="flex justify-end items-center"> -->
- <!-- <a href="/forgot" class="underline underline-offset-4 text-black text-sm" -->
- <!-- >{$t('login.forgotUserOrPassword')}</a -->
- <!-- > -->
- <!-- </div> -->
+ <input
+ name="password"
+ type="password"
+ required
+ class="bg-primary"
+ bind:value={$password}
+ autocapitalize="none"
+ />
{/if}
-
<button
- type="submit"
- class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
- disabled={loading}
+ type="button"
+ on:click={() => (revealPassword = !revealPassword)}
+ class="absolute right-5 top-10"
>
- {#if loading}
- <Spinner />
- {:else}
- {$t('login.' + pageID)}
- {/if}
+ <Icon icon={revealPassword ? 'eye' : 'eye-off'} />
</button>
- </form>
+ </div>
- <p class="text-secondary text-center font-medium">
- {pageID === 'register' ? $t('login.haveAccount') : $t('login.noAccount')}
- <a
- href={pageID === 'register' ? '/login' : '/register'}
- class="block md:inline text-black underline underline-offset-4 hover:opacity-80"
- >
- {pageID === 'register' ? $t('login.signIn') : $t('login.register')}
- </a>
- </p>
- {/if}
+ {#if pageID === 'register'}
+ <p class="text-secondary text-sm">
+ {$t('login.passwordRecommendation')}
+ </p>
+ {:else}
+ <!-- TODO -->
+ <!-- <div class="flex justify-end items-center"> -->
+ <!-- <a href="/forgot" class="underline underline-offset-4 text-black text-sm" -->
+ <!-- >{$t('login.forgotUserOrPassword')}</a -->
+ <!-- > -->
+ <!-- </div> -->
+ {/if}
+
+ <button
+ type="submit"
+ class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
+ disabled={loading}
+ bind:this={btn}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t('login.' + pageID)}
+ {/if}
+ </button>
+ </form>
+
+ <p class="text-secondary text-center font-medium">
+ {pageID === 'register' ? $t('login.haveAccount') : $t('login.noAccount')}
+ <a
+ href={pageID === 'register' ? '/login' : '/register'}
+ class="block md:inline text-black underline underline-offset-4 hover:opacity-80"
+ >
+ {pageID === 'register' ? $t('login.signIn') : $t('login.register')}
+ </a>
+ </p>
</div>
</div>
</div>
diff --git a/src/components/index.js b/src/components/index.js
index 18a821ff..ab54e09f 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -1,3 +1,4 @@
+export { default as Pinpad } from './Pinpad.svelte';
export { default as Balance } from './Balance.svelte';
export { default as Lnurlp } from './Lnurlp.svelte';
export { default as Lnurlw } from './Lnurlw.svelte';