aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2022-09-28 20:21:58 -0700
committerAdam Soltys <[email protected]>2022-09-28 20:21:58 -0700
commit9e07792fcdd0ba0b7c07a20fe83db54597388e26 (patch)
treea79d54ea6b864abc1953a3aafcc3ff630c1b06e5
parent732f77dcabc45a3df578a1a03c457b9c94866619 (diff)
downloadcoinos-ui-9e07792fcdd0ba0b7c07a20fe83db54597388e26.tar.gz
coinos-ui-9e07792fcdd0ba0b7c07a20fe83db54597388e26.zip
withdraw page and other fixups
-rw-r--r--package.json3
-rw-r--r--pnpm-lock.yaml119
-rw-r--r--src/components/LandingHeader.svelte8
-rw-r--r--src/components/RegisterOrLogin.svelte19
-rw-r--r--src/components/Spinner.svelte13
-rw-r--r--src/components/index.js1
-rw-r--r--src/lib/parse.js271
-rw-r--r--src/lib/utils.js5
-rw-r--r--src/locales/en.json6
-rw-r--r--src/locales/es.json3
-rw-r--r--src/locales/fr.json3
-rw-r--r--src/routes/[username]/dashboard/+page.svelte140
-rw-r--r--src/routes/scan/+page.svelte24
-rw-r--r--src/routes/send/+page.svelte62
-rw-r--r--static/icons/paste.svg10
-rw-r--r--static/icons/scan.svg3
16 files changed, 559 insertions, 131 deletions
diff --git a/package.json b/package.json
index a1fdfdde..9c8dadf9 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
},
"type": "module",
"dependencies": {
- "@lottiefiles/svelte-lottie-player": "^0.2.0"
+ "@lottiefiles/svelte-lottie-player": "^0.2.0",
+ "qr-scanner": "^1.4.1"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 172fa101..249d6f05 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,6 +12,7 @@ specifiers:
prettier: ^2.7.1
prettier-plugin-svelte: ^2.7.0
qr-code-styling: 1.6.0-rc.1
+ qr-scanner: ^1.4.1
svelte: ^3.50.1
svelte-outclick: ^3.1.0
svelte-pincode: ^2.1.1
@@ -22,9 +23,10 @@ specifiers:
dependencies:
'@lottiefiles/svelte-lottie-player': 0.2.0
+ qr-scanner: 1.4.1
devDependencies:
- '@sveltejs/adapter-node': 1.0.0-next.92
+ '@sveltejs/adapter-node': 1.0.0-next.96
'@zerodevx/svelte-toast': 0.7.2
autoprefixer: [email protected]
@@ -84,6 +86,58 @@ packages:
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
dev: true
+ /@rollup/plugin-commonjs/[email protected]:
+ resolution: {integrity: sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ rollup: ^2.68.0
+ dependencies:
+ '@rollup/pluginutils': [email protected]
+ commondir: 1.0.1
+ estree-walker: 2.0.2
+ glob: 7.2.3
+ is-reference: 1.2.1
+ magic-string: 0.25.9
+ resolve: 1.22.1
+ rollup: 2.78.1
+ dev: true
+
+ /@rollup/plugin-json/[email protected]:
+ resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
+ peerDependencies:
+ rollup: ^1.20.0 || ^2.0.0
+ dependencies:
+ '@rollup/pluginutils': [email protected]
+ rollup: 2.78.1
+ dev: true
+
+ /@rollup/plugin-node-resolve/[email protected]:
+ resolution: {integrity: sha512-5G2niJroNCz/1zqwXtk0t9+twOSDlG00k1Wfd7bkbbXmwg8H8dvgHdIWAun53Ps/rckfvOC7scDBjuGFg5OaWw==}
+ engines: {node: '>= 10.0.0'}
+ peerDependencies:
+ rollup: ^2.78.0
+ dependencies:
+ '@rollup/pluginutils': [email protected]
+ '@types/resolve': 1.17.1
+ deepmerge: 4.2.2
+ is-builtin-module: 3.2.0
+ is-module: 1.0.0
+ resolve: 1.22.1
+ rollup: 2.78.1
+ dev: true
+
+ /@rollup/pluginutils/[email protected]:
+ resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
+ engines: {node: '>= 8.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0
+ dependencies:
+ '@types/estree': 0.0.39
+ estree-walker: 1.0.1
+ picomatch: 2.3.1
+ rollup: 2.78.1
+ dev: true
+
/@rollup/pluginutils/4.2.1:
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
@@ -92,10 +146,13 @@ packages:
picomatch: 2.3.1
dev: true
- /@sveltejs/adapter-node/1.0.0-next.92:
- resolution: {integrity: sha512-HhSZzXN9BlkkoEPMYfHk6BbkYnO/zoLqBLM+oafDZszABvhNXmAMgO7GFFcWxBnZkO7gicLDHYzsOtxxambLfA==}
+ /@sveltejs/adapter-node/1.0.0-next.96:
+ resolution: {integrity: sha512-tIHaRolUYy2PiHl4RUWaOsYxEjK5lN9501qzCzFbYr/uoLnZcnPGSXNJICwX0AX9AUkV6cvkZey6bLbUQcwH0Q==}
dependencies:
- esbuild: 0.15.7
+ '@rollup/plugin-commonjs': [email protected]
+ '@rollup/plugin-json': [email protected]
+ '@rollup/plugin-node-resolve': [email protected]
+ rollup: 2.78.1
dev: true
@@ -163,14 +220,32 @@ packages:
resolution: {integrity: sha512-HheveklTjp3hxpYQhoHfyA6B4bQaUeSV5MQf2usIv/58UF2jY/YqhCAWj9bDBjufbuZc5pSz4BXvdX3WVT+viA==}
dev: true
+ /@types/estree/0.0.39:
+ resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
+ dev: true
+
+ /@types/estree/1.0.0:
+ resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
+ dev: true
+
/@types/node/18.7.18:
resolution: {integrity: sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==}
dev: true
+ /@types/offscreencanvas/2019.7.0:
+ resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==}
+ dev: false
+
/@types/pug/2.0.6:
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
dev: true
+ /@types/resolve/1.17.1:
+ resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
+ dependencies:
+ '@types/node': 18.7.18
+ dev: true
+
/@types/sass/1.43.1:
resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==}
dependencies:
@@ -266,6 +341,11 @@ packages:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: true
+ /builtin-modules/3.3.0:
+ resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
+ engines: {node: '>=6'}
+ dev: true
+
/camelcase-css/2.0.1:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
@@ -294,6 +374,10 @@ packages:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
+ /commondir/1.0.1:
+ resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
+ dev: true
+
/concat-map/0.0.1:
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
dev: true
@@ -584,6 +668,10 @@ packages:
engines: {node: '>=6'}
dev: true
+ /estree-walker/1.0.1:
+ resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
+ dev: true
+
/estree-walker/2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
@@ -709,6 +797,13 @@ packages:
binary-extensions: 2.2.0
dev: true
+ /is-builtin-module/3.2.0:
+ resolution: {integrity: sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==}
+ engines: {node: '>=6'}
+ dependencies:
+ builtin-modules: 3.3.0
+ dev: true
+
/is-core-module/2.10.0:
resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
dependencies:
@@ -727,11 +822,21 @@ packages:
is-extglob: 2.1.1
dev: true
+ /is-module/1.0.0:
+ resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
+ dev: true
+
/is-number/7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
dev: true
+ /is-reference/1.2.1:
+ resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
+ dependencies:
+ '@types/estree': 1.0.0
+ dev: true
+
/kleur/4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
@@ -978,6 +1083,12 @@ packages:
qrcode-generator: 1.4.4
dev: true
+ /qr-scanner/1.4.1:
+ resolution: {integrity: sha512-xiR90NONHTfTwaFgW/ihlqjGMIZg6ExHDOvGQRba1TvV+WVw7GoDArIOt21e+RO+9WiO4AJJq+mwc5f4BnGH3w==}
+ dependencies:
+ '@types/offscreencanvas': 2019.7.0
+ dev: false
+
/qrcode-generator/1.4.4:
resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==}
dev: true
diff --git a/src/components/LandingHeader.svelte b/src/components/LandingHeader.svelte
index c979e2c8..adeac98e 100644
--- a/src/components/LandingHeader.svelte
+++ b/src/components/LandingHeader.svelte
@@ -67,11 +67,11 @@
</button>
<div
- class="block md:hidden absolute top-0 {showMobileMenu
+ class="container w-full px-10 md:hidden absolute top-0 {showMobileMenu
? 'right-0'
: 'right-[-100%]'} transition-all ease-in-out duration-300 h-[100vh] w-full bg-white"
>
- <div class="space-y-8 mt-24 ml-10 font-bold text-xl">
+ <div class="space-y-8 mt-24 font-bold text-xl">
<button on:click={() => mobileMenuButtonClick(howItWorks)} class="block"
>{$t('howItWorks.header')}</button
>
@@ -82,12 +82,12 @@
>
{#if !$user}
<button
- class="border rounded-full px-6 py-2 font-bold block"
+ class="border rounded-full px-6 py-2 font-bold block w-full"
on:click={() => goto('/register')}
>{$t('nav.startInSeconds')}
</button>
<button
- class="bg-black text-white border rounded-full px-6 py-2 font-bold block"
+ class="bg-black text-white border rounded-full px-6 py-2 font-bold block w-full"
on:click={() => goto('/login')}
>
{$t('nav.signIn')}
diff --git a/src/components/RegisterOrLogin.svelte b/src/components/RegisterOrLogin.svelte
index 88fe62e6..fa805a1d 100644
--- a/src/components/RegisterOrLogin.svelte
+++ b/src/components/RegisterOrLogin.svelte
@@ -1,5 +1,5 @@
<script>
- import { browser } from "$app/environment";
+ import { browser } from '$app/environment';
import { page } from '$app/stores';
import { tick } from 'svelte';
import { Icon } from '$comp';
@@ -23,12 +23,12 @@
signIn: '/login'
}[pageID];
- // TODO: move to server side
+ // TODO: move to server side
$: if (browser && $user) goto(`/${$user.username}/receive`);
- let focus = (el) => setTimeout(() => el.focus());
+ let focus = (el) => setTimeout(() => el.focus());
- console.log("REDIRECT STORE", $redirect)
+ console.log('REDIRECT STORE', $redirect);
</script>
<div class="pt-10">
@@ -56,13 +56,21 @@
class="bg-primary"
bind:value={username}
use:focus
+ 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} />
+ <input
+ name="password"
+ type="text"
+ required
+ class="bg-primary"
+ bind:value={password}
+ autocapitalize="none"
+ />
{:else}
<input
name="password"
@@ -70,6 +78,7 @@
required
class="bg-primary"
bind:value={password}
+ autocapitalize="none"
/>
{/if}
<button
diff --git a/src/components/Spinner.svelte b/src/components/Spinner.svelte
new file mode 100644
index 00000000..eccf98ce
--- /dev/null
+++ b/src/components/Spinner.svelte
@@ -0,0 +1,13 @@
+<svg
+ class="mx-auto h-6 w-6 animate-spin text-white"
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+>
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
+ <path
+ class="opacity-75"
+ fill="currentColor"
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
+ />
+</svg>
diff --git a/src/components/index.js b/src/components/index.js
index 7d01e338..ba0f6516 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -1,3 +1,4 @@
+export { default as Spinner } from './Spinner.svelte';
export { default as Check } from './Check.svelte';
export { default as Heart } from './Heart.svelte';
export { default as Quote } from './Quote.svelte';
diff --git a/src/lib/parse.js b/src/lib/parse.js
new file mode 100644
index 00000000..2a827956
--- /dev/null
+++ b/src/lib/parse.js
@@ -0,0 +1,271 @@
+export default (text) => {
+ if (!text) return;
+
+ if (text.includes('@') && text.includes('.')) {
+ let [name, domain] = text.split('@');
+ try {
+ clearTimeout(debounce);
+ await new Promise((r) => (debounce = setTimeout(r, 1500)));
+ ({ data: text } = await Vue.axios.get(
+ `/encode?domain=${domain}&name=${name}`
+ ));
+ } catch (e) {}
+ }
+
+ try {
+ if (text.toLowerCase().slice(0, 10) === 'lightning:')
+ text = text.slice(10);
+ let payreq = text.toLowerCase();
+ payment.payreq = payreq;
+ payment.payobj = bolt11.decode(payment.payreq);
+ let {
+ network: { bech32 },
+ } = payment.payobj;
+
+ if (!networks.includes('lightning'))
+ return commit('error', 'Lightning not supported in this account');
+
+ if (bech32 !== process.env.VUE_APP_NETWORK_BECH32) {
+ dispatch('clearPayment');
+ commit(
+ 'error',
+ `Wrong network prefix '${bech32}' instead of '${process.env.VUE_APP_NETWORK_BECH32}'`
+ );
+ throw new Error('Wrong network');
+ }
+
+ if (user.account.ticker !== 'BTC')
+ await dispatch(
+ 'shiftAccount',
+ user.accounts.find((a) => a.asset === BTC).id
+ );
+
+ let { tags, satoshis, millisatoshis } = payment.payobj;
+ let description = tags.find((t) => t.tagName === 'description');
+ if (description) payment.memo = description.data;
+ payment.amount = millisatoshis
+ ? Math.round(millisatoshis / 1000)
+ : satoshis;
+ payment.fiatAmount = ((payment.amount * getters.rate) / SATS).toFixed(
+ 2
+ );
+ payment.network = 'lightning';
+
+ await Vue.axios.post('/lightning/query', { payreq });
+ if (!getters.payment.recipient)
+ go({ name: 'send', params: { keep: true } });
+
+ return;
+ } catch (e) {
+ if (e.response) commit('error', e.response.data);
+ }
+ //
+ // let url;
+ // try {
+ // if (nodes.includes('bitcoin')) {
+ // url = bip21.decode(text);
+ // if (!networks.includes('bitcoin'))
+ // return commit('error', 'Bitcoin not supported in this account');
+ // payment.network = 'bitcoin';
+ // url.options.asset = BTC;
+ // }
+ // } catch (e) {
+ // if (nodes.includes('liquid')) {
+ // try {
+ // url = bip21.decode(text, 'liquidnetwork');
+ // if (!networks.includes('liquid'))
+ // return commit('error', 'Liquid not supported in this account');
+ // payment.network = 'liquid';
+ // } catch (e) {
+ // /**/
+ // }
+ // }
+ // }
+ //
+ // if (url) {
+ // let { amount, asset, assetid, message } = url.options;
+ // if (assetid) asset = assetid;
+ // if (!asset) asset = BTC;
+ // let account = user.accounts.find((a) => a.asset === asset);
+ // if (account) {
+ // if (account.asset !== user.account.asset)
+ // await dispatch('shiftAccount', account.id);
+ // } else return commit('error', 'Unrecognized asset');
+ //
+ // payment.address = url.address;
+ //
+ // if (message) payment.memo = message;
+ //
+ // if (amount) {
+ // payment.amount = parseInt((amount * SATS).toFixed(0));
+ // payment.fiatAmount = ((payment.amount * getters.rate) / SATS).toFixed(
+ // 2
+ // );
+ // }
+ //
+ // await dispatch('isInternal');
+ // await dispatch('estimateFee');
+ // go({ name: 'send', params: { keep: true } });
+ // return;
+ // }
+ //
+ // if (nodes.includes('bitcoin') && validate(text)) {
+ // if (!networks.includes('bitcoin'))
+ // return commit('error', 'Bitcoin not supported in this account');
+ // payment.address = text;
+ // payment.network = 'bitcoin';
+ // await dispatch('isInternal');
+ // go({ name: 'send', params: { keep: true } });
+ // return;
+ // }
+ //
+ // // Liquid
+ // if (nodes.includes('liquid') && isLiquid(text)) {
+ // if (!networks.includes('liquid'))
+ // return commit('error', 'Liquid not supported in this account');
+ // payment.address = text;
+ // payment.network = 'liquid';
+ // await dispatch('isInternal');
+ // go({ name: 'send', params: { keep: true } });
+ // return;
+ // }
+ //
+ // if (text.startsWith('6P')) {
+ // commit('text', text);
+ // return go('/decrypt');
+ // }
+ //
+ // try {
+ // let ecpair = ECPair.fromWIF(text, {
+ // ...this._vm.$network,
+ // confidentialPrefix: 1,
+ // assetHash: '',
+ // });
+ // commit('ecpair', ecpair);
+ // go('/sweep');
+ // } catch (e) {
+ // /* */
+ // }
+ //
+ // if (text.toLowerCase().startsWith('lnurl:')) {
+ // try {
+ // const { data: lnurl } = await Vue.axios.get(`/url?code=${text}`);
+ // await dispatch('handleScan', lnurl);
+ // return;
+ // } catch (e) {
+ // commit('error', e.response ? e.response.data : e.message);
+ // }
+ // }
+ //
+ // if (text.toLowerCase().includes('lnurl')) {
+ // commit('loading', true);
+ // let txt = text.toLowerCase().match(/lnurl[a-z0-9]+/);
+ //
+ // try {
+ // const { data: params } = await Vue.axios.get(`/decode?text=${txt}`);
+ // if (params.status === 'ERROR') {
+ // let { reason } = params;
+ // try {
+ // ({ reason } = JSON.parse(reason.replace(/.*{/, '{')));
+ // } catch {
+ // /**/
+ // }
+ //
+ // commit('loading', false);
+ // return commit('error', reason);
+ // }
+ //
+ // let { seed } = getters;
+ //
+ // switch (params.tag) {
+ // case 'channelRequest':
+ // await dispatch('openChannel', params);
+ // break;
+ // case 'login':
+ // if (!seed) ({ seed } = await dispatch('passwordPrompt'));
+ // try {
+ // const key = linkingKey(params.domain, seed);
+ // const signedMessage = secp256k1.ecdsaSign(
+ // hexToUint8Array(params.k1),
+ // key.privateKey
+ // );
+ // const signedMessageDER = secp256k1.signatureExport(
+ // signedMessage.signature
+ // );
+ // const linkingKeyPub = secp256k1.publicKeyCreate(
+ // key.privateKey,
+ // true
+ // );
+ // const sig = bytesToHexString(signedMessageDER);
+ //
+ // const response = await Vue.axios.post('/login', {
+ // params,
+ // sig,
+ // key: bytesToHexString(linkingKeyPub),
+ // });
+ //
+ // if (response.data.status === 'OK') {
+ // commit('snack', 'Login success');
+ // } else {
+ // commit('error', response.status);
+ // }
+ //
+ // go('/home');
+ // } catch (e) {
+ // commit('error', e.response ? e.response.data : e.message);
+ // }
+ // break;
+ //
+ // case 'withdrawRequest':
+ // commit('lnurl', params);
+ // go('/withdraw');
+ // break;
+ // case 'payRequest':
+ // commit('lnurl', params);
+ // go('/pay');
+ // break;
+ // }
+ // } catch (e) {
+ // commit('error', e.response ? e.response.data : e.message);
+ // }
+ //
+ // commit('loading', false);
+ // }
+ //
+ // if (isUuid(text)) {
+ // try {
+ // const { data: payment } = await Vue.axios.post('/checkRedeemCode', {
+ // redeemcode: text,
+ // });
+ //
+ // if (payment) {
+ // window.location.href = `${window.location.protocol}//${window.location.host}/redeem/${text}`;
+ // return;
+ // }
+ // } catch (e) {
+ // commit('error', e.response ? e.response.data : e.message);
+ // }
+ //
+ // try {
+ // const { data: invoice } = await Vue.axios.get(
+ // `/invoice?uuid=${text}`
+ // );
+ // await dispatch('handleScan', invoice.text);
+ // return;
+ // } catch (e) {
+ // commit('error', e.response ? e.response.data : e.message);
+ // }
+ // }
+ //
+ // if (
+ // text.startsWith(
+ // `${window.location.protocol}//${window.location.host}/redeem`
+ // )
+ // ) {
+ // window.location.href = text;
+ // }
+ //
+ // commit('stopScanning', true);
+ //
+ // return 'no';
+};
diff --git a/src/lib/utils.js b/src/lib/utils.js
index df621c4b..61e2ddac 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -1,6 +1,7 @@
import cookie from 'cookie';
import { browser } from '$app/environment';
import { toast } from '@zerodevx/svelte-toast';
+import { goto } from '$app/navigation';
export function scroll(section) {
section.scrollIntoView({ behavior: 'smooth' });
@@ -144,3 +145,7 @@ export const toggleFullscreen = (el) =>
: el.requestFullscreen().catch(console.log);
export const sats = 100000000;
+
+export const goBack = () => {
+ browser && history.go(-1);
+};
diff --git a/src/locales/en.json b/src/locales/en.json
index 7c07f0e9..0a99b947 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -162,7 +162,9 @@
"remove": "Remove",
"scanToPayBTC": "Scan to pay with bitcoin",
"dashboard": {
- "ACCOUNT_BALANCE": "ACCOUNT BALANCE",
+ "go": "Go",
+ "dashboard": "Dashboard",
+ "accountBalance": "Account Balance",
"bitcoinPrice": "bitcoin Price:",
"conversionFeeWarning": "You will be charged a 1% withdrawal fee on the amount.",
"dataFromBinance": "Data from <span class=\"text-black\">Binance</span>.",
@@ -170,7 +172,7 @@
"optionalNote": "Note (Optional)",
"send": "Send",
"receive": "Receive a Payment",
- "withdraw": "Withdraw"
+ "withdraw": "Withdraw Funds"
},
"receive": {
"lessThan1BTCWarning": "Please enter an amount less than 1 bitcoin.",
diff --git a/src/locales/es.json b/src/locales/es.json
index f378c555..41452f4a 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -159,7 +159,8 @@
"remove": "Eliminar",
"scanToPayBTC": "Escanea para pagar en bitcoin",
"dashboard": {
- "ACCOUNT_BALANCE": "ACCOUNT BALANCE",
+ "dashboard": "Tablero",
+ "accountBalance": "Saldo",
"bitcoinPrice": "Precio de bitcoin:",
"conversionFeeWarning": "Cobraremos una tarifa de retiro del 1% sobre el monto",
"dataFromBinance": "Datos de <span class=\"text-black\">Binance</span>.",
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 221679ab..263bc74c 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -131,7 +131,8 @@
"remove": "Enlève",
"scanToPayBTC": "Scanner pour payer avec bitcoin",
"dashboard": {
- "ACCOUNT_BALANCE": "SOLDE",
+ "dashboard": "Tableau de bord",
+ "accountBalance": "Solde",
"bitcoinPrice": "Prix de bitcoin:",
"conversionFeeWarning": "Il y a un prix de 1% pour convertir entre bitcoin, Liquid et Lightning.",
"dataFromBinance": "Données de <span class=\"text-black\">Binance</span>.",
diff --git a/src/routes/[username]/dashboard/+page.svelte b/src/routes/[username]/dashboard/+page.svelte
index a580f349..55fe69ee 100644
--- a/src/routes/[username]/dashboard/+page.svelte
+++ b/src/routes/[username]/dashboard/+page.svelte
@@ -13,48 +13,29 @@
$: animateRate($rate);
let animateRate = (newRate) => {
- if (!accountBalanceFiat) return;
+ if (!accountBalanceFiat) return;
let t = 0;
- let newBalance = (user.account.balance * newRate / sats);
- let oldBalance = parseFloat(accountBalanceFiat);
- let diff = oldBalance - newBalance;
+ let newBalance = (user.account.balance * newRate) / sats;
+ let oldBalance = parseFloat(accountBalanceFiat);
+ let diff = oldBalance - newBalance;
clearInterval(interval);
interval = setInterval(() => {
let delta = diff * ease(t / 100);
- accountBalanceFiat = (oldBalance - delta).toFixed(2)
+ accountBalanceFiat = (oldBalance - delta).toFixed(2);
if (t > 80) clearInterval(interval);
t++;
}, 10);
};
$: user = data.user;
- $: accountBalanceFiat = accountBalanceFiat || user && $rate && (user.account.balance * $rate / sats).toFixed(2);
+ $: accountBalanceFiat =
+ accountBalanceFiat || (user && $rate && ((user.account.balance * $rate) / sats).toFixed(2));
let payreq = '',
payreqField,
- withdrawing,
loading;
- let toggle = async () => {
- payreq = '';
- withdrawing = !withdrawing;
- await tick();
- if (withdrawing) payreqField.focus();
- };
-
- let withdraw = async () => {
- loading = true;
- try {
- await post('/withdraw', { payreq });
- withdrawing = false;
- loading = false;
- } catch (e) {
- failure(e.message);
- loading = false;
- }
- };
-
$: btcPrice = f($selectedRate, user.currency);
$: accountBalanceBtc = user.account.balance / sats;
@@ -75,96 +56,29 @@
<div class="px-3 md:px-0 flex justify-center items-center mt-10 lg:mt-0 mb-20">
<div>
<h1 class="text-3xl md:text-4xl font-semibold mb-8">
- {withdrawing ? 'Withdraw' : 'Dashboard'}
+ {$t('user.dashboard.dashboard')}
</h1>
- {#if !withdrawing}
- <!-- <span class="text-secondary mx-auto text-lg font-bold" -->
- <!-- >{$t('user.dashboard.bitcoinPrice')} -->
- <!-- <span class="text-black" -->
- <!-- >{$selectedRate ? btcPrice : $t('user.dashboard.fetchingRate')}</span -->
- <!-- > -->
- <!-- </span> -->
- <!-- -->
- <h3 class="text-secondary font-bold border-b pb-1 mt-10 mb-6 w-full md:w-[500px]">
- {$t('user.dashboard.ACCOUNT_BALANCE')}
- </h3>
-
- <span class="text-3xl font-bold block mb-1">{accountBalanceSats} SAT</span>
+ <h3 class="text-secondary font-bold border-b pb-1 mt-10 mb-6 w-full md:w-[500px] uppercase">
+ {$t('user.dashboard.accountBalance')}
+ </h3>
- <span class="text-secondary text-xl block"
- >{$selectedRate ? f(accountBalanceFiat, user.currency) : $t('user.dashboard.fetchingRate')}</span
- >
-
- <div class="md:flex md:space-x-5">
- <button
- class="rounded-full border py-2 px-5 font-bold mt-4 block md:inline hover:opacity-80"
- on:click={() => goto(`/${user.username}/receive`)}>{$t('user.dashboard.receive')}</button
- >
- <button
- class="rounded-full border py-2 px-5 font-bold mt-5 md:mt-4 hover:opacity-80"
- on:click={toggle}>{$t('user.dashboard.withdraw')}</button
- >
- </div>
- {:else}
- <div class="space-y-3 w-[250px] md:w-[500px] md:pr-20">
- <button on:click={() => (withdrawing = false)}>
- <Icon icon="arrow-left" style="w-8 hover:opacity-80" />
- </button>
-
- <div>
- <!-- found missing translation -->
- <label for="invoice" class="font-bold mb-1 block">Lightning Invoice</label>
- <input
- bind:this={payreqField}
- type="text"
- name="payreq"
- required
- bind:value={payreq}
- class="block rounded-2xl p-3 w-full bg-primary"
- />
- </div>
+ <span class="text-3xl font-bold block mb-1">{accountBalanceSats} SAT</span>
- <!-- TODO -->
- <!--
- <div>
- <label for="note" class="font-bold mb-1 block">{$t('user.dashboard.optionalNote')}</label>
- <textarea name="note" rows={2} class="block border rounded-2xl p-3 w-full bg-primary" />
- </div>
--->
+ <span class="text-secondary text-xl block"
+ >{$selectedRate
+ ? f(accountBalanceFiat, user.currency)
+ : $t('user.dashboard.fetchingRate')}</span
+ >
- <button
- disabled={!payreq}
- class="{!payreq
- ? 'opacity-50'
- : 'opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold w-full mt-2 bg-black text-white"
- on:click={withdraw}
- >
- {#if loading}
- <svg
- class="mx-auto h-6 w-6 animate-spin text-white"
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- >
- <circle
- class="opacity-25"
- cx="12"
- cy="12"
- r="10"
- stroke="currentColor"
- stroke-width="4"
- />
- <path
- class="opacity-75"
- fill="currentColor"
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
- />
- </svg>
- {:else}
- {$t('user.dashboard.send')}
- {/if}
- </button>
- </div>
- {/if}
+ <div class="grid sm:grid-cols-2 gap-2 mt-8">
+ <button
+ class="rounded-full border py-2 px-5 font-bold hover:opacity-80 text-xl w-full mb-2"
+ on:click={() => goto(`/${user.username}/receive`)}>{$t('user.dashboard.receive')}</button
+ >
+ <button
+ class="rounded-full border py-2 px-5 font-bold hover:opacity-80 text-xl w-full mb-2"
+ on:click={() => goto('/send')}>{$t('user.dashboard.withdraw')}</button
+ >
+ </div>
</div>
</div>
diff --git a/src/routes/scan/+page.svelte b/src/routes/scan/+page.svelte
new file mode 100644
index 00000000..5c8c09f6
--- /dev/null
+++ b/src/routes/scan/+page.svelte
@@ -0,0 +1,24 @@
+<script>
+ import { Icon } from "$comp";
+ import QrScanner from 'qr-scanner';
+ import { onMount } from 'svelte';
+ import { goBack } from "$lib/utils";
+
+ let scanner, vid;
+ onMount(() => {
+ scanner = new QrScanner(vid, console.log);
+ scanner.start();
+ });
+
+</script>
+
+ <button
+ class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80"
+ on:click={goBack}
+ >
+ <Icon icon="arrow-left" style="w-10" />
+ </button>
+
+ <div class="flex w-full">
+<video bind:this={vid} class="w-full max-w-[600px] mx-auto" />
+</div>
diff --git a/src/routes/send/+page.svelte b/src/routes/send/+page.svelte
new file mode 100644
index 00000000..777f9766
--- /dev/null
+++ b/src/routes/send/+page.svelte
@@ -0,0 +1,62 @@
+<script>
+ import { t } from '$lib/translations';
+ import { AppHeader, Icon, Spinner } from '$comp';
+
+ let payreq = '',
+ loading,
+ payreqField;
+
+ let withdraw = async () => {
+ loading = true;
+ try {
+ await post('/withdraw', { payreq });
+ loading = false;
+ } catch (e) {
+ failure(e.message);
+ loading = false;
+ }
+ };
+</script>
+
+<AppHeader avatarPosition="left-[calc(50vw-64px)] lg:left-[calc(15vw-64px)]" />
+
+<div class="container px-4 mt-20 max-w-xl mx-auto">
+ <div>
+ <!-- found missing translation -->
+ <label for="invoice" class="font-bold mb-1 block"
+ >Destination <small class="text-secondary">Address, Invoice, User, Email or Phone</small
+ ></label
+ >
+
+ <div class="relative">
+ <input
+ bind:this={payreqField}
+ type="text"
+ name="payreq"
+ required
+ bind:value={payreq}
+ class="block rounded-2xl p-3 w-full bg-primary"
+ />
+
+ <a href="/scan">
+ <div class="flex absolute h-[48px] right-0 top-0 px-2 mt-[1px]">
+ <Icon icon="scan" style="mr-2 w-6 my-auto" />
+ </div>
+ </a>
+ </div>
+ </div>
+
+ <button
+ disabled={!payreq}
+ class="{!payreq
+ ? 'opacity-50'
+ : 'opacity-100 hover:opacity-80'} rounded-2xl border py-3 font-bold w-full mt-2 bg-black text-white"
+ on:click={withdraw}
+ >
+ {#if loading}
+ <Spinner />
+ {:else}
+ {$t('user.dashboard.go')}
+ {/if}
+ </button>
+</div>
diff --git a/static/icons/paste.svg b/static/icons/paste.svg
new file mode 100644
index 00000000..eb1412d4
--- /dev/null
+++ b/static/icons/paste.svg
@@ -0,0 +1,10 @@
+<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
+</style>
+<polyline class="st0" points="23,12 23,18 29,18 23,12 15,12 15,30 29,30 29,18 "/>
+<path class="st0" d="M20,6h-8V4c0-1.1,0.9-2,2-2h4c1.1,0,2,0.9,2,2V6z"/>
+<path class="st0" d="M15,28h-5c-2.2,0-4-1.8-4-4V8c0-2.2,1.8-4,4-4h2"/>
+<path class="st0" d="M20,4h2c2.2,0,4,1.8,4,4v7"/>
+</svg>
diff --git a/static/icons/scan.svg b/static/icons/scan.svg
new file mode 100644
index 00000000..c58313bd
--- /dev/null
+++ b/static/icons/scan.svg
@@ -0,0 +1,3 @@
+<svg width="15px" height="15px" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 1C1.67157 1 1 1.67157 1 2.5V5H0V2.5C0 1.11929 1.11929 0 2.5 0H5V1H2.5ZM12.5 1H10V0H12.5C13.8807 0 15 1.11929 15 2.5V5H14V2.5C14 1.67157 13.3284 1 12.5 1ZM13 8H2V7H13V8ZM0 12.5V10H1V12.5C1 13.3284 1.67157 14 2.5 14H5V15H2.5C1.11929 15 0 13.8807 0 12.5ZM14 12.5V10H15V12.5C15 13.8807 13.8807 15 12.5 15H10V14H12.5C13.3284 14 14 13.3284 14 12.5Z" fill="black"/>
+</svg>