aboutsummaryrefslogtreecommitdiffstats
path: root/src
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:19 -0800
commit66740dea41f2772d46e1b13b91ef0e7b1a68bfff (patch)
tree2ff8926078222020c05252e64da6ae5e9c9cbbef /src
parent916f331e0e9a051830ce988639bb2149788e8228 (diff)
downloadcoinos-ui-66740dea41f2772d46e1b13b91ef0e7b1a68bfff.tar.gz
coinos-ui-66740dea41f2772d46e1b13b91ef0e7b1a68bfff.zip
new pinpad component
Diffstat (limited to 'src')
-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
-rw-r--r--src/lib/nostr.js2
-rw-r--r--src/lib/utils.js2
-rw-r--r--src/locales/en.json2
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte5
-rw-r--r--src/routes/(app)/[username]/(profile)/messages/+page.svelte4
-rw-r--r--src/routes/(app)/[username]/dashboard/+page.svelte2
-rw-r--r--src/routes/(app)/[username]/generate/+page.svelte6
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/+page.svelte8
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_pos.svelte2
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_security.svelte161
-rw-r--r--src/routes/(app)/disable2fa/+server.js4
-rw-r--r--src/routes/(app)/otpsecret/+server.js2
-rw-r--r--src/routes/(app)/send/[...text]/+page.svelte4
19 files changed, 275 insertions, 250 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';
diff --git a/src/lib/nostr.js b/src/lib/nostr.js
index 35d843b2..de19b477 100644
--- a/src/lib/nostr.js
+++ b/src/lib/nostr.js
@@ -3,7 +3,7 @@ import * as ecc from 'tiny-secp256k1';
import { generateMnemonic, entropyToMnemonic, mnemonicToEntropy, mnemonicToSeedSync } from 'bip39';
import { Buffer } from 'buffer';
import { bech32m } from 'bech32';
-import { bip32, failure, wait, post, stretch } from '$lib/utils';
+import { bip32, fail, wait, post, stretch } from '$lib/utils';
import { calculateId, signId } from 'nostr';
import { password as pw, passwordPrompt, pin, loginRedirect } from '$lib/store';
import { goto, invalidate } from '$app/navigation';
diff --git a/src/lib/utils.js b/src/lib/utils.js
index d0b0b1c2..3a644e1f 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -101,7 +101,7 @@ export const warning = (m) => {
}
});
};
-export const failure = (m) => {
+export const fail = (m) => {
toast.pop();
toast.push(m, {
theme: {
diff --git a/src/locales/en.json b/src/locales/en.json
index eb0039ac..b39571f6 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -241,7 +241,7 @@
"autoLock": "Auto-Lock Timer",
"autoLockDescription": "Your account will go into a secure mode, requiring you to re-enter your credentials after a specified time:",
"twofa": "2-Factor Authentication",
- "twofaDescription": "Add an extra level of security to your device by enableing 2-factor authentication.",
+ "twofaDescription": "Add an extra level of security to your device by enabling 2-factor authentication.",
"twofaSetup": "Set up 2FA",
"twofaDisable": "Disable 2FA",
"select": "Select",
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 40c73ea4..1cf525a0 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -4,8 +4,11 @@
export let data;
let { events, subject, user } = data;
+ let v;
</script>
<div class="flex justify-center lg:justify-start">
- <Balance {user} />
+ {#if user?.id === subject.id}
+ <Balance {user} />
+ {/if}
</div>
diff --git a/src/routes/(app)/[username]/(profile)/messages/+page.svelte b/src/routes/(app)/[username]/(profile)/messages/+page.svelte
index 00960534..db7b1ced 100644
--- a/src/routes/(app)/[username]/(profile)/messages/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/messages/+page.svelte
@@ -5,7 +5,7 @@
import { scale } from 'svelte/transition';
import { enhance } from '$app/forms';
import { Icon } from '$comp';
- import { back, failure, focus } from '$lib/utils';
+ import { back, fail, focus } from '$lib/utils';
import { sign, send, encrypt, decrypt } from '$lib/nostr';
import { messages, password } from '$lib/store';
import { tick, onMount } from 'svelte';
@@ -64,7 +64,7 @@
sent = true;
} catch (e) {
- failure('Failed to send message');
+ fail('Failed to send message');
delete $messages[user.pubkey][subject.pubkey][event.id];
$messages = $messages;
}
diff --git a/src/routes/(app)/[username]/dashboard/+page.svelte b/src/routes/(app)/[username]/dashboard/+page.svelte
index d136943e..499034e6 100644
--- a/src/routes/(app)/[username]/dashboard/+page.svelte
+++ b/src/routes/(app)/[username]/dashboard/+page.svelte
@@ -1,6 +1,6 @@
<script>
import { enhance } from '$app/forms';
- import { f, s, post, failure, sat, sats } from '$lib/utils';
+ import { f, s, post, fail, sat, sats } from '$lib/utils';
import { tick } from 'svelte';
import { Avatar, Icon } from '$comp';
import { rate, password, selectedRate } from '$lib/store';
diff --git a/src/routes/(app)/[username]/generate/+page.svelte b/src/routes/(app)/[username]/generate/+page.svelte
index 6336062a..bd72592f 100644
--- a/src/routes/(app)/[username]/generate/+page.svelte
+++ b/src/routes/(app)/[username]/generate/+page.svelte
@@ -1,5 +1,5 @@
<script>
- import { failure, post, wait } from '$lib/utils';
+ import { fail, post, wait } from '$lib/utils';
import { Buffer } from 'buffer';
import { onMount } from 'svelte';
import { bech32m } from 'bech32';
@@ -41,9 +41,9 @@
} catch (e) {
$pin = '';
if (e.message?.startsWith('Pin')) {
- failure('Wrong pin, try again');
+ fail('Wrong pin, try again');
} else {
- failure('Failed to generate keys');
+ fail('Failed to generate keys');
goto('/');
}
}
diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
index 1e8b31ce..5638a22b 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
@@ -5,7 +5,7 @@
import { Icon, Spinner, Pin } from '$comp';
import { t } from '$lib/translations';
- import { failure, post, success } from '$lib/utils';
+ import { fail, post, success } from '$lib/utils';
import { avatar, banner, password, pin } from '$lib/store';
import { upload } from '$lib/upload';
import { page } from '$app/stores';
@@ -34,7 +34,7 @@
$: form?.success && success('Settings saved!');
$: if (form?.message?.startsWith('Pin')) {
- failure('Wrong pin, try again');
+ fail('Wrong pin, try again');
$pin = '';
}
@@ -98,7 +98,7 @@
applyAction(result);
} catch (e) {
console.log(e);
- failure('Something went wrong');
+ fail('Something went wrong');
}
loading = false;
@@ -109,7 +109,7 @@
</script>
{#if loaded && user.haspin && $pin?.length !== 6}
- <Pin />
+ <Pin bind:value={$pin} />
{/if}
<form
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
index 94dc8c88..472f4d07 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
@@ -1,7 +1,7 @@
<script>
import { Icon, LocaleSelector, Toggle } from '$comp';
import { t } from '$lib/translations';
- import { success, failure } from '$lib/utils';
+ import { success, fail } from '$lib/utils';
import { page } from '$app/stores';
export let user, rates;
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
index cf969f1c..eb3b405e 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
@@ -1,80 +1,52 @@
<script>
import { tick } from 'svelte';
import { t } from '$lib/translations';
- import { Icon, Qr } from '$comp';
- import { Pincode, PincodeInput } from 'svelte-pincode';
- import { post, success, failure } from '$lib/utils';
+ import { Pin, Icon, Qr } from '$comp';
+ import { post, success, fail } from '$lib/utils';
import { pin as current } from '$lib/store';
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
export let user, submit;
- let settingPin, setting2fa, disabling2fa;
+ let pin, verify;
+ let settingPin, setting2fa, disabling2fa, confirming2fa;
let password, revealPassword;
let token = '';
- let pinCode = [];
- let verifyCode = [];
- let tokenInput, pinInput, verifyInput;
- let verifying = false;
let old;
let togglePassword = () => (revealPassword = !revealPassword);
- let startVerifying = () => {
- if (pinCode.join('').length < 6) return;
- verifying = true;
- };
-
- $: pin = pinCode.join('');
-
let otp;
- $: pinChange($current);
- let pinChange = async (pin) => {
- try {
- otp = await post('/otpsecret', { pin });
- } catch (e) {
- console.log(e);
- }
- };
let locked;
- $: verifyCode.join('').length > 5 && checkPin();
+ $: verifying = pin?.length > 5;
+ $: verify && checkPin(pin);
let checkPin = async () => {
old = $current;
- if (locked) return;
- locked = true;
-
- let verify = verifyCode.join('');
-
- pinCode = ['', '', '', '', '', ''];
- verifyCode = ['', '', '', '', '', ''];
- settingPin = false;
try {
if (pin.length > 5 && pin === verify) {
user.haspin = true;
$current = pin;
+ pin = '';
+ verify = '';
submit.click();
+ settingPin = false;
+ verifying = false;
} else {
- failure('Pin mismatch, try again');
+ fail('Pin mismatch, try again');
+ pin = '';
+ verify = '';
+ verifying = false;
settingPin = true;
- pinInput.focusFirstInput();
}
} catch (e) {
- failure('Problem setting PIN');
+ console.log(e);
+ fail('Problem setting PIN');
}
-
- verifying = false;
-
- await new Promise((r) => setTimeout(r, 500));
- locked = false;
};
- $: if (verifying) verifyInput && tick().then(verifyInput.focusFirstInput);
- $: if (settingPin) pinInput && tick().then(pinInput.focusFirstInput);
- $: if (setting2fa || disabling2fa) tokenInput && tick().then(tokenInput?.focusFirstInput);
-
let reset = () => {
token = '';
return true;
@@ -88,15 +60,35 @@
submit.click();
} catch (e) {
console.log(e);
- failure('Failed to disable pin');
+ fail('Failed to disable pin');
}
} else {
settingPin = true;
}
};
- let toggleEnabling = () => reset() && (setting2fa = !setting2fa);
- let toggleDisabling = () => reset() && (disabling2fa = !disabling2fa);
+ let startEnabling2fa = async () => {
+ reset();
+ try {
+ if (!otp) otp = await post('/otpsecret', { pin: $current });
+ } catch (e) {
+ console.log(e);
+ }
+
+ setting2fa = true;
+ };
+
+ let startDisabling2fa = () => (disabling2fa = true);
+ let startConfirming2fa = () => (confirming2fa = true);
+ let cancel = () => {
+ pin = null;
+ token = null;
+ verifying = false;
+ settingPin = false;
+ setting2fa = false;
+ confirming2fa = false;
+ disabling2fa = false;
+ };
$: enable2fa(token);
let enable2fa = async (twoFa) => {
@@ -105,10 +97,10 @@
await post('/enable2fa', { token });
success('2FA enabled');
user.twofa = 1;
- toggleEnabling();
+ cancel();
}
} catch (e) {
- failure('Failed to enable 2FA, try again');
+ fail('Failed to enable 2FA, try again');
}
};
@@ -118,12 +110,12 @@
if (disabling2fa && token.length === 6) {
await post('/disable2fa', { token });
success('2FA disabled');
- user.twofa = 0;
- toggleDisabling();
+ delete user.twofa;
+ disabling2fa = false;
}
} catch (e) {
console.log(e);
- failure('Failed to disable 2FA, try again');
+ fail('Failed to disable 2FA, try again');
}
};
</script>
@@ -155,31 +147,10 @@
<p class="text-secondary mb-1">
{$t('user.settings.securityPINDescription')}
</p>
- {#if settingPin}
- <div class="flex my-4">
- <div class="mx-auto">
- <div class:hidden={verifying}>
- <Pincode on:complete={startVerifying} bind:code={pinCode} bind:this={pinInput}>
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- </Pincode>
- </div>
- <div class:hidden={!verifying}>
- <Pincode bind:code={verifyCode} bind:this={verifyInput}>
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- </Pincode>
- </div>
- </div>
- </div>
+ {#if verifying}
+ <Pin bind:value={verify} {cancel} />
+ {:else if settingPin}
+ <Pin bind:value={pin} title={$t('user.settings.setPIN')} {cancel} />
{:else}
<button type="button" class="primary" on:click={togglePin}
><Icon icon="lock" style="mr-1" />
@@ -193,6 +164,7 @@
<p class="text-secondary mb-4">
{$t('user.settings.twofaDescription')}
</p>
+
{#if setting2fa}
<a href={otp.uri}>
<Qr src={otp.qr} />
@@ -203,40 +175,25 @@
<b>{otp.secret}</b>
</div>
- <div class="text-center my-4">
- {$t('user.settings.oneTimeCode')}<br />
- <Pincode bind:value={token} bind:this={tokenInput}>
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- </Pincode>
- </div>
- {:else if disabling2fa}
- <div class="text-center my-4">
- {$t('user.settings.oneTimeCode')}<br />
- <Pincode bind:value={token} bind:this={tokenInput}>
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- <PincodeInput />
- </Pincode>
- </div>
+ <button type="button" class="primary" on:click={startConfirming2fa}>
+ <Icon icon="mobile" style="mr-1" />
+ Confirm
+ </button>
{:else if user.twofa}
- <button type="button" class="primary" on:click={toggleDisabling}>
+ <button type="button" class="primary" on:click={startDisabling2fa}>
<Icon icon="mobile" style="mr-1" />
{$t('user.settings.twofaDisable')}
</button>
{:else}
- <button type="button" class="primary" on:click={toggleEnabling}>
+ <button type="button" class="primary" on:click={startEnabling2fa}>
<Icon icon="mobile" style="mr-1" />
{$t('user.settings.twofaSetup')}
</button>
{/if}
+
+ {#if confirming2fa || disabling2fa}
+ <Pin bind:value={token} title="Enter 2FA Code" {cancel} />
+ {/if}
</div>
<div>
diff --git a/src/routes/(app)/disable2fa/+server.js b/src/routes/(app)/disable2fa/+server.js
index 13fcb2b9..b2bb07ec 100644
--- a/src/routes/(app)/disable2fa/+server.js
+++ b/src/routes/(app)/disable2fa/+server.js
@@ -1,4 +1,4 @@
-import { error } from '@sveltejs/kit';
+import { error, json } from '@sveltejs/kit';
import { auth, post } from '$lib/utils';
export async function POST({ cookies, request }) {
@@ -6,7 +6,7 @@ export async function POST({ cookies, request }) {
let body = await request.json();
let res = await post('/disable2fa', body, auth(cookies));
- return new Response(JSON.stringify(res));
+ return json(res);
} catch (e) {
throw error(500, 'Problem disabling 2fa');
}
diff --git a/src/routes/(app)/otpsecret/+server.js b/src/routes/(app)/otpsecret/+server.js
index 31dbcec1..7d5c1ad9 100644
--- a/src/routes/(app)/otpsecret/+server.js
+++ b/src/routes/(app)/otpsecret/+server.js
@@ -9,7 +9,7 @@ export async function POST({ cookies, request }) {
let uri = `otpauth://totp/coinos:${username}?secret=${secret}&period=30&digits=6&algorithm=SHA1&issuer=coinos`;
return new Response(JSON.stringify({ secret, uri, qr: Qr.drawImg(uri, { size: 600 }) }));
} catch (e) {
- console.log(e);
+ if (e.message.includes('pin')) throw e;
throw new error(500, 'Problem saving secret');
}
}
diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte
index e3d8091f..46f5e56a 100644
--- a/src/routes/(app)/send/[...text]/+page.svelte
+++ b/src/routes/(app)/send/[...text]/+page.svelte
@@ -6,7 +6,7 @@
import { browser } from '$app/environment';
import { t } from '$lib/translations';
import { AppHeader, Avatar, Icon, Spinner } from '$comp';
- import { back, failure } from '$lib/utils';
+ import { back, fail } from '$lib/utils';
import { format, parseISO } from 'date-fns';
export let data;
@@ -90,7 +90,7 @@
<h1 class="px-3 md:px-0 text-xl font-semibold mt-10">{$t('user.send.contacts')}</h1>
<div>
{#each contacts as c}
- <a href={`/${c.username}`}>
+ <a href={`/${c.username}/receive`}>
<div class="border-b p-2 last:border-b-0 hover:bg-gray-100">
<div class="flex">
<div>