aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-11-15 23:02:29 -0800
committerAdam Soltys <[email protected]>2023-11-15 23:02:29 -0800
commitb466b3d39c3b9ec792ef924c7f4750fb70b6a016 (patch)
tree9f0071056f7f53129e1eb81a1bca268300d41a05 /src
parent6d8eb8a609ebb6c1781c463a837bf9e19a6d3d4b (diff)
downloadcoinos-ui-b466b3d39c3b9ec792ef924c7f4750fb70b6a016.tar.gz
coinos-ui-b466b3d39c3b9ec792ef924c7f4750fb70b6a016.zip
prefill register page
Diffstat (limited to 'src')
-rw-r--r--src/components/AppHeader.svelte193
-rw-r--r--src/components/Avatar.svelte2
-rw-r--r--src/lib/punks.js66
-rw-r--r--src/lib/upload.js3
-rw-r--r--src/locales/en.json2
-rw-r--r--src/routes/(app)/+layout.svelte2
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.svelte36
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte8
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/+page.svelte28
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_account.svelte6
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_pos.svelte4
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/_security.svelte2
-rw-r--r--src/routes/(app)/register/+page.server.js29
-rw-r--r--src/routes/(app)/register/+page.svelte245
-rw-r--r--src/routes/+layout.svelte3
-rw-r--r--src/routes/+page.server.js1
-rw-r--r--src/routes/+page.svelte103
17 files changed, 550 insertions, 183 deletions
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte
index a37a834e..123606e1 100644
--- a/src/components/AppHeader.svelte
+++ b/src/components/AppHeader.svelte
@@ -3,7 +3,7 @@
import { banner, newPayment, colorTheme } from '$lib/store';
import { goto } from '$app/navigation';
import { Avatar, Icon } from '$comp';
- import { t } from '$lib/translations';
+ import { loading, t } from '$lib/translations';
import { page } from '$app/stores';
export let data;
@@ -28,112 +28,115 @@
$banner?.id && $banner.id === subject.id
? `url(${$banner.src})`
: subject?.banner
- ? `url(/api/public/${subject.id}-banner.webp)`
+ ? `url(/api/public/${subject.banner}.webp)`
: undefined;
$: $page && (showMenu = false);
</script>
-<header
- class="bg-gradient-to-r {$colorTheme} h-[175px] w-full relative bg-cover mb-20"
- style:background-image={bg}
->
- <nav class="flex justify-end items-center space-x-4 p-5">
- {#if user}
- <a href={`/${user.username}`}>
- <button
- class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/${user.username}`
- ? 'opacity-100'
- : 'opacity-70 hover:opacity-80'}"
- ><Icon icon="home" style="mx-auto w-6 md:w-8" />
- </button>
- </a>
- <a href={`/${user.username}/receive`}>
- <button
- class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/${user.username}/receive`
- ? 'opacity-100'
- : 'opacity-70 hover:opacity-80'}"
- ><Icon icon="numpad" style="mx-auto w-6 md:w-8" />
- </button>
- </a>
- <a href={`/${user.username}/payments`}>
- <button
- class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/${user.username}/payments`
- ? 'opacity-100'
- : 'opacity-70 hover:opacity-80'} relative"
- ><Icon icon="clock" style="mx-auto w-6 md:w-8" />
- {#if $newPayment}
- <span class="absolute top-0 right-0">
- <span class="flex h-3 w-3">
- <span
- class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#F7931A] opacity-75"
- />
- <span class="relative inline-flex rounded-full h-3 w-3 bg-[#F7931A]" />
+{#if !$loading}
+ <header
+ class="bg-gradient-to-r {$colorTheme} h-[175px] w-full relative bg-cover mb-20"
+ style:background-image={bg}
+ >
+ <nav class="flex justify-end items-center space-x-4 p-5">
+ {#if user}
+ <a href={`/${user.username}`}>
+ <button
+ class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
+ .url.pathname === `/${user.username}`
+ ? 'opacity-100'
+ : 'opacity-70 hover:opacity-80'}"
+ ><Icon icon="home" style="mx-auto w-6 md:w-8" />
+ </button>
+ </a>
+ <a href={`/${user.username}/receive`}>
+ <button
+ class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
+ .url.pathname === `/${user.username}/receive`
+ ? 'opacity-100'
+ : 'opacity-70 hover:opacity-80'}"
+ ><Icon icon="numpad" style="mx-auto w-6 md:w-8" />
+ </button>
+ </a>
+ <a href={`/${user.username}/payments`}>
+ <button
+ class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
+ .url.pathname === `/${user.username}/payments`
+ ? 'opacity-100'
+ : 'opacity-70 hover:opacity-80'} relative"
+ ><Icon icon="clock" style="mx-auto w-6 md:w-8" />
+ {#if $newPayment}
+ <span class="absolute top-0 right-0">
+ <span class="flex h-3 w-3">
+ <span
+ class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#F7931A] opacity-75"
+ />
+ <span class="relative inline-flex rounded-full h-3 w-3 bg-[#F7931A]" />
+ </span>
</span>
- </span>
- {/if}
- </button>
- </a>
- <a href={`/send`}>
- <button
- class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/send`
- ? 'opacity-100'
- : 'opacity-70 hover:opacity-80'}"
- ><Icon icon="send" style="mx-auto w-6 md:w-8" />
- </button>
- </a>
- <div class="relative">
- <OutClick on:outclick={() => (showMenu = false)}>
+ {/if}
+ </button>
+ </a>
+ <a href={`/send`}>
<button
class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
- .url.pathname === `/${user.username}` ||
- $page.url.pathname === `/${user.username}/settings` ||
- $page.url.pathname === `/support`
+ .url.pathname === `/send`
? 'opacity-100'
- : `opacity-70 hover:opacity-80 ${showMenu ? 'opacity-80' : ''}`}"
- on:click={() => (showMenu = !showMenu)}
- ><Icon icon="menu" style="mx-auto w-6 md:w-8" />
+ : 'opacity-70 hover:opacity-80'}"
+ ><Icon icon="send" style="mx-auto w-6 md:w-8" />
</button>
+ </a>
+ <div class="relative">
+ <OutClick on:outclick={() => (showMenu = false)}>
+ <button
+ class="bg-white p-2 rounded-full w-12 h-12 md:w-16 md:h-16 drop-shadow-xl border border-black/10 {$page
+ .url.pathname === `/${user.username}` ||
+ $page.url.pathname === `/${user.username}/settings` ||
+ $page.url.pathname === `/support`
+ ? 'opacity-100'
+ : `opacity-70 hover:opacity-80 ${showMenu ? 'opacity-80' : ''}`}"
+ on:click={() => (showMenu = !showMenu)}
+ ><Icon icon="menu" style="mx-auto w-6 md:w-8" />
+ </button>
- <div
- class="{showMenu
- ? 'block'
- : 'hidden'} absolute top-14 right-0 bg-white rounded-3xl p-8 shadow-xl z"
+ <div
+ class="{showMenu
+ ? 'block'
+ : 'hidden'} absolute top-14 right-0 bg-white rounded-3xl p-8 shadow-xl z"
+ >
+ <ul class="space-y-5 w-48">
+ {#each menuButtons as { href, icon, key }}
+ <li>
+ <a {href} data-sveltekit-prefetch="off">
+ <button
+ class="flex justify-center items-center font-semibold hover:opacity-80"
+ ><Icon {icon} style="mr-2 w-6 md:w-8" /> {$t(key)}
+ </button>
+ </a>
+ </li>
+ {/each}
+ </ul>
+ <hr class="my-4" />
+ <a href="/"><img src="/images/logo.svg" alt="Coinos" class="w-32" /></a>
+ </div>
+ </OutClick>
+ </div>
+ {:else}
+ <a href={`/login?redirect=${$page.url.pathname}`}>
+ <button class="bg-white px-5 py-2 rounded-xl font-semibold text-sm"
+ >{$t('nav.signIn')}</button
>
- <ul class="space-y-5 w-48">
- {#each menuButtons as { href, icon, key }}
- <li>
- <a {href} data-sveltekit-prefetch="off">
- <button class="flex justify-center items-center font-semibold hover:opacity-80"
- ><Icon {icon} style="mr-2 w-6 md:w-8" /> {$t(key)}
- </button>
- </a>
- </li>
- {/each}
- </ul>
- <hr class="my-4" />
- <a href="/"><img src="/images/logo.svg" alt="Coinos" class="w-32" /></a>
- </div>
- </OutClick>
- </div>
- {:else}
- <a href={`/login?redirect=${$page.url.pathname}`}>
- <button class="bg-white px-5 py-2 rounded-xl font-semibold text-sm"
- >{$t('nav.signIn')}</button
- >
- </a>
- {/if}
- </nav>
- <div
- class="absolute md:w-[64px] md:mx-auto lg:left-[138px] left-[calc(50vw-64px)] -bottom-[64px] z-30"
- >
- <Avatar user={subject} />
- </div>
-</header>
+ </a>
+ {/if}
+ </nav>
+ <div
+ class="absolute md:w-[64px] md:mx-auto lg:left-[164px] left-[calc(50vw-64px)] -bottom-[64px] z-30"
+ >
+ <Avatar user={subject} />
+ </div>
+ </header>
+{/if}
<style>
.z {
diff --git a/src/components/Avatar.svelte b/src/components/Avatar.svelte
index ac2dfdda..d39dd8ba 100644
--- a/src/components/Avatar.svelte
+++ b/src/components/Avatar.svelte
@@ -13,7 +13,7 @@
$: src =
($avatar?.id && $avatar.id === user.id && $avatar.src) ||
'/api/public/' +
- (user.profile ? user.id + '-profile.webp' : 'punks/' + punk(user.pubkey || user.id || 'aa'));
+ (user.profile + '.webp' || 'punks/' + punk(user.pubkey || user.id || 'aa'));
</script>
<a href={link} class:pointer-events-none={disabled}>
diff --git a/src/lib/punks.js b/src/lib/punks.js
new file mode 100644
index 00000000..4d9f2e60
--- /dev/null
+++ b/src/lib/punks.js
@@ -0,0 +1,66 @@
+export default [
+ '5211890e4128d8d730802cc75ee105867e8878581d5e72ffc98a0d60027a5398',
+ 'd1ca262272613d7386fecce7f1ad268188940b59ea83fdd1f8d7ab85771e3f43',
+ '62a55d4d6e4b6f992a1c1840f041585c957affcdbf875202cf9e1db9def05aeb',
+ 'd3018e45c66dd485697f738ad191af98267e6255d562496dd06b1a662e0c7a88',
+ '7354f3829dd05c838aa16d66b00316f7994797927a4d3b594c8af04a2b4ae86a',
+ '9018be885177c675baf9b36978428d841ca30a9185e11de1a31238a08e3ec1af',
+ '11701e76233395ebdba8d1d50334d1a0cc3a158f3452260c9a7b9a067b78d753',
+ '0ec2e8d07e053d686140857493ece653cde52764a60aa1d25651d81811d44a3d',
+ 'c1dececcbd7b623bfaa726392512e6ea110db93a0649dd01bf9e559318514f7c',
+ '01aa9348244c4b65de3df758d49c98076e17a109aef04bc211abc77cf42dc51d',
+ 'a80951fc593035abb2dda045a2076d29a30f5cc470e0a20db39c3ec588ed61d8',
+ 'ba8dd5a7070aee7102d4f8bc83ac497091e2e473a0f448a1c513569167dd0791',
+ '3f8f861e663accee51e68b987705ce647a7446b7f118abc94c632e158acbb44f',
+ '777c5715de7423597fea6c03c496ea46414f0508cd96e119261f002965beb805',
+ 'e19eb1eeabdeffa04ccd792eb09776690cf2121887b26a7c5d20d66e3e8fd0fb',
+ 'db5fe28b8ddb9ae5e4f5e0e757ee60bc938bffbdfdd38de807939e212d3c5066',
+ '914813583b901044970b9c7d6dcdfde4344339325f5988d0dd681d377f1243fb',
+ '8c18c036b257d7b9c5a970a786efa04c320f3cf5afa73fa310a6603744d1ee3a',
+ '2aaba839e8546a8cb6d04a81ff5be83ac4e700ca4491607a16ebd60dc4995750',
+ 'b742b7509a97c2b346ea762f0510e8475b30dc988417f0f146ae00a800c6dd56',
+ '836d72150b6f3033b6220d15ac9d49457d81041a700d12898bd3b8e737b4d265',
+ '8ba4736edc68da8e5b21c23af4de1f9131f47778c2a5b2a970a799223a9380b3',
+ '4f07e1da3685a858970a9f39300564971e97257f5d37b55a6d9882014aecc421',
+ '698a2ca7a09e88fa6594801da037f4dbce6ae841d1d33736aebcf8f6fe104e01',
+ 'd5b5b2167602f7277cdac76cfda8bdad0befcb8ff12df495dfabf40c91d1120c',
+ '50c7c2e8e8c7f4aabffb195187a1ac1105f5712859f02d491d794ba2d8498e08',
+ '6e1f1626a9308abd0b7732fecbc7d49366ced46dead63ef3603fb0f4ec7aa53d',
+ '85cb7cf0245613d08b3027f879f58a6db645bf0db107242beb856b340ee49ee8',
+ 'd6dbb18d5df5e92c8c74ee59aa86d19dff25a5d0f9d030e84285a686f86804d7',
+ 'eccc90741370ba4acc70bc65f4ccc38fddb1327d977c366156018974bc52fe4a',
+ '78ae0118856a1f17c13f292628fcf3dccc150c7278578caaceb9f35ca8bc9f55',
+ '43ce93c9835e983403a01329918d184832c5516e1b0ea4e0878b4c4def042777',
+ '5f88f7f579ce50d7bcf60d6e9556f77acf361ba54caf3eb61dc4f2ca9c94c6a2',
+ '43bab95a047112dca6705506f916a97252363a20da4a4c0a5ab7b676e11083fc',
+ '08ce4840184f63a779bcd1f91bbcb56a2e8cba75c3ba2dbd290a559a57e4238e',
+ '4595bb092682af7177799464364c047fbfa7ede767336734949bce5d085fce15',
+ 'c84724a398bda8d20436fb0663d059c196628fa71192bb720fa5adf6137bed9b',
+ '65309f82cd9f9926a578d09e1ad13909f68a06d02b853760731a4225f7aecef4',
+ '433519d13f2b64fe82a90cd054797d4779507fea93c1054bd51c35ba846a0f7f',
+ '4d8ad7a761d068b7ef9b058fb4ba924b276e7a45a3ad47a79b767e08e821e038',
+ '2cc94821f56d21918ba4f2770d97f280e06fd99f4199167e2f4ddc780f404c8a',
+ '7849ac0f31056265efb0110038f86e61effe17b7db836377d88433b73312e65a',
+ '2155224ef35cfb4bbdb06d982b42bb9b93d97b2bc691c22c804e1759a0a04882',
+ '9d9a9a40c490f05d7dbafde8bf60d68aab7dcb79a7976eef62d1448a4549816b',
+ '5f6549ad9dd15dd57c204b1a9b60c68c376e77d7d114cfa13219e6a1a71f901f',
+ 'f46310b2e60727af071d09e759814f52f933688a0ff0995c7da373b381e70005',
+ 'f307620a90a36045fa550316d61be4755c1f6c7f3fc81c95fc4d590ac568b2c4',
+ '6f2618272bb0164247e9b0f40b19f020c1c9253510dc658a3cc078d079ef01bf',
+ '6dbec748ea87e959390b0df429c005fccffe9e672864f555c14c7a407b940870',
+ 'a7a6187e54e83acdb893d456c8b4ad6877e4738ea9e0d47c9b2070bf2c98a35f',
+ '3759946d9e875ac2fc5c51ed850686193bb1e0716219e42efd52f3965993b275',
+ 'f6017bec50563dbbb24b979214ae225f96b01dea5b090b174038ff2842a1c6b1',
+ 'fd502d5007bfc62a91eb7a34c1ae23b9de4a20cf5e1bbd86dadc421d7083099c',
+ 'e3d1c40538a8aed282a430647e3ff16ff94b64b8ed0a7cf427e8b0e2780a9b2a',
+ '27caa38c14a85ad64b4c4469a34e61a3965a1cbc4cad9a85707da374301c830d',
+ '080574eb9e0613926e0e5d9b667c132e12965a1b1d47218a0b290a2d55c55d2f',
+ '14d254333c6240fbdacbbbe261f7ae15be2e83bffcf084f4f59f459bae0a4571',
+ '82390020b2bca461204cc49b20ee6fec1b93dad476726f377785d04040a4d6c3',
+ '7af3029a826e6dfba23ad502901a3632ef4ee047bd45d618b1f106717b0af7ea',
+ '6bc2990699be7f610c9f6a49bc7a19b034040727bc3de63fee89a1573fe00673',
+ '96ea95735b61c48c6f465db8ca0246a62539f34c3a977be6338770d92461da89',
+ '4f1b39906714266a7a1936bfa8e08c5d891285dd2b188b4bcd4afb0107671004',
+ '6524936709e0719f9e28bf1caac91f86f323d1a79f10ab65a03fa9ea2e6b3270',
+ '5d1d3769cd141603ce907df5dca159e389ca4d75338eddbe1b7db1c0766cbfd6'
+];
diff --git a/src/lib/upload.js b/src/lib/upload.js
index d8b27061..92f5290f 100644
--- a/src/lib/upload.js
+++ b/src/lib/upload.js
@@ -1,5 +1,4 @@
export const upload = async (file, type, progress, token) => {
- console.log('TYPE', type);
let url = `/api/upload/${type}`;
let formData = new FormData();
formData.append('file', file);
@@ -14,7 +13,7 @@ export const upload = async (file, type, progress, token) => {
});
ajax.upload.addEventListener('progress', progress, false);
ajax.open('POST', url);
- ajax.setRequestHeader('Authorization', `Bearer ${token}`);
+ // ajax.setRequestHeader('Authorization', `Bearer ${token}`);
ajax.send(formData);
});
};
diff --git a/src/locales/en.json b/src/locales/en.json
index 2f629675..36cb3518 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -121,7 +121,7 @@
"username": "Username",
"password": "Password",
"email": "Email",
- "passwordRecommendation": "We recommend a long, random password generated with a password manager.",
+ "passwordRecommendation": "Change to a longer random password if you intend to keep significant funds in the account.",
"forgotUserOrPassword": "Forgot Username or Password?",
"haveAccount": "Already have an account?",
"noAccount": "Don't have an account?",
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
index bcff6ef6..012dd957 100644
--- a/src/routes/(app)/+layout.svelte
+++ b/src/routes/(app)/+layout.svelte
@@ -98,7 +98,7 @@
{/if}
{#if subject?.profile}
- <meta name="og:image" content={`/api/public/${subject.uuid}-profile.webp`} />
+ <meta name="og:image" content={`/api/public/${subject.profile}.webp`} />
{:else}
<meta property="og:image" content="/icons/logo.svg" />
{/if}
diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte
index 1b99c4b4..bd2bf0d3 100644
--- a/src/routes/(app)/[username]/(profile)/+layout.svelte
+++ b/src/routes/(app)/[username]/(profile)/+layout.svelte
@@ -61,58 +61,66 @@
<div class="container mx-auto w-full px-4 mb-4 flex flex-wrap lg:flex-nowrap space-y-5">
<div class="hidden lg:block lg:w-[300px]" />
<div class="lg:w-[300px] lg:absolute space-y-5 left-20 mx-auto max-w-md">
- <div
- class="flex text-3xl font-bold text-center mx-auto justify-center gap-2"
- on:click={toggleDetails}
- >
+ <div class="flex text-3xl font-bold text-center mx-auto justify-center gap-2">
<div class="my-auto">{display || username}</div>
- <Icon icon="stats" style="w-12 rounded cursor-pointer" />
+ <button on:click={toggleDetails}>
+ <Icon icon="stats" style="w-12" />
+ </button>
</div>
{#if showDetails}
<div>
- <div class="font-bold">{$t('user.profilePage')}</div>
+ <div class="font-bold">{$t('user.profilePage')}</div>
<div class="flex gap-2">
<div class="break-all grow text-secondary text-lg">
{profile}
</div>
<div class="flex mb-auto gap-1">
- <button class="my-auto opacity-60 hover:opacity-100" on:click={() => copy(profile)}
+ <button class="my-auto" on:click={() => copy(profile)}
><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
>
- <a href={`/qr/${encodeURIComponent(profile)}`} class="my-auto opacity-60 hover:opacity-100">
+ <a
+ href={`/qr/${encodeURIComponent(profile)}`}
+ class="my-auto"
+ >
<Icon icon="qr" style="invert max-w-max min-w-[32px]" />
</a>
</div>
</div>
</div>
<div>
- <div class="font-bold">{$t('user.lightningAddress')}</div>
+ <div class="font-bold">{$t('user.lightningAddress')}</div>
<div class="flex gap-2">
<div class="break-all grow text-secondary text-lg">
{lnaddr}
</div>
<div class="flex mb-auto gap-1">
- <button class="my-auto opacity-60 hover:opacity-100" on:click={() => copy(lnaddr)}
+ <button class="my-auto" on:click={() => copy(lnaddr)}
><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
>
- <a href={`/qr/${encodeURIComponent(lnaddr)}`} class="my-auto opacity-60 hover:opacity-100">
+ <a
+ href={`/qr/${encodeURIComponent(lnaddr)}`}
+ class="my-auto"
+ >
<Icon icon="qr" style="invert max-w-max min-w-[32px]" />
</a>
</div>
</div>
</div>
<div>
- <div class="font-bold">{$t('user.nostrPubkey')}</div>
+ <div class="font-bold">{$t('user.nostrPubkey')}</div>
<div class="flex gap-2">
<div class="break-all grow text-secondary text-lg">
{npub}
</div>
<div class="flex my-auto gap-1">
- <button class="my-auto opacity-60 hover:opacity-100" on:click={() => copy(npub)}
+ <button class="my-auto" on:click={() => copy(npub)}
><Icon icon="copy" style="max-w-max min-w-[32px]" /></button
>
- <a href={`/qr/${encodeURIComponent(npub)}`} class="my-auto opacity-60 hover:opacity-100">
+ <a
+ href={`/qr/${encodeURIComponent(npub)}`}
+ class="my-auto"
+ >
<Icon icon="qr" style="invert max-w-max min-w-[32px]" />
</a>
</div>
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index e3150ff2..42cf9527 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -96,20 +96,20 @@
<div class="flex flex-wrap gap-4 justify-center w-full">
<a href={`/${user.username}/receive`} class="w-full md:w-60">
<button
- class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full"
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full bg-black text-white"
>
<div class="mx-auto flex">
- <Icon icon="numpad" style="my-auto h-6 mr-2" />
+ <Icon icon="numpad" style="my-auto h-6 mr-2 invert" />
<div class="my-auto mt-1">{$t('user.dashboard.receive')}</div>
</div>
</button>
</a>
<a href={`/send`} class="w-full md:w-60">
<button
- class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full"
+ class="rounded-full border py-3 px-6 font-bold hover:opacity-80 flex w-full bg-black text-white"
>
<div class="mx-auto flex">
- <Icon icon="send" style="my-auto h-6 mr-2" />
+ <Icon icon="send" style="my-auto h-6 mr-2 invert" />
<div class="my-auto mt-1">{$t('user.dashboard.send')}</div>
</div>
</button>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
index 762e836e..087c9450 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
@@ -68,13 +68,31 @@
}
if ($avatar) {
- await upload($avatar.file, $avatar.type, $avatar.progress, token);
- await fetch(`/api/public/${id}-profile.webp`, { cache: 'reload', mode: 'no-cors' });
+ try {
+ let { hash } = JSON.parse(
+ await upload($avatar.file, $avatar.type, $avatar.progress, token)
+ );
+
+ data.set('profile', hash);
+
+ await fetch(`/api/public/${hash}.webp`, { cache: 'reload', mode: 'no-cors' });
+ } catch (e) {
+ console.log('problem uploading avatar', e);
+ }
}
if ($banner) {
- await upload($banner.file, $banner.type, $banner.progress, token);
- await fetch(`/api/public/${id}-banner.webp`, { cache: 'reload', mode: 'no-cors' });
+ try {
+ let { hash } = JSON.parse(
+ await upload($banner.file, $banner.type, $banner.progress, token)
+ );
+
+ data.set('banner', hash);
+
+ await fetch(`/api/public/${hash}.webp`, { cache: 'reload', mode: 'no-cors' });
+ } catch (e) {
+ console.log('problem uploading banner', e);
+ }
}
let event = {
@@ -84,7 +102,7 @@
content: JSON.stringify({
name: user.username,
about: user.address,
- picture: `${PUBLIC_COINOS_URL}/public/${user.id}-profile.webp`
+ picture: `${PUBLIC_COINOS_URL}/public/${user.profile}.webp`
}),
tags: []
};
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
index 26c327c0..15d286c8 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_account.svelte
@@ -5,7 +5,7 @@
import { t } from '$lib/translations';
import { page } from '$app/stores';
- export let user;
+ export let user, rates, submit;
let { id } = user;
let selectedTheme = 1;
@@ -95,7 +95,7 @@
on:keydown={selectAvatar}
>
<img
- src={$avatar?.src || `/api/public/${user.id}-profile.webp`}
+ src={$avatar?.src || `/api/public/${user.profile}.webp`}
class="absolute w-full h-full object-cover object-center visible overflow-hidden"
alt={user.username}
/>
@@ -138,7 +138,7 @@
{#if $banner || user.banner}
<img
- src={$banner ? $banner.src : `/api/public/${user.id}-banner.webp`}
+ src={$banner ? $banner.src : `/api/public/${user.banner}.webp`}
class="w-full object-cover object-center visible overflow-hidden h-48 mb-4 hover:opacity-80"
on:click={selectBanner}
on:keydown={selectBanner}
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
index 057d8c20..a96528a0 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_pos.svelte
@@ -4,7 +4,7 @@
import { success, fail } from '$lib/utils';
import { page } from '$app/stores';
- export let user, rates;
+ export let user, rates, submit;
let fiats = Object.keys(rates);
</script>
@@ -25,7 +25,7 @@
<div>
<div class="flex justify-between items-center">
<span class="font-bold">{$t('user.settings.tipPrompt')}</span>
- <Toggle id="prompt" checked={user.prompt} bind:value={user.prompt} />
+ <Toggle id="prompt" bind:value={user.prompt} />
</div>
<p class="text-secondary mt-1 w-9/12">
{$t('user.settings.tipPromptDescription')}
diff --git a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
index 85f461ad..170df744 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/_security.svelte
@@ -8,7 +8,7 @@
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
- export let user, submit;
+ export let user, rates, submit;
let confirming2fa,
disabling2fa,
diff --git a/src/routes/(app)/register/+page.server.js b/src/routes/(app)/register/+page.server.js
index c90d4ee3..f2b49e3f 100644
--- a/src/routes/(app)/register/+page.server.js
+++ b/src/routes/(app)/register/+page.server.js
@@ -1,9 +1,32 @@
+import {
+ NumberDictionary,
+ uniqueNamesGenerator,
+ colors,
+ adjectives,
+ animals
+} from 'unique-names-generator';
import { fail, redirect } from '@sveltejs/kit';
import { fd, post, login } from '$lib/utils';
export const load = async ({ parent }) => {
let { user } = await parent();
if (user) throw redirect(307, `/${user.username}`);
+
+ let username = uniqueNamesGenerator({
+ dictionaries: [animals, NumberDictionary.generate({ min: 10, max: 99 })],
+ length: 2,
+ separator: ''
+ });
+
+ let password = uniqueNamesGenerator({
+ dictionaries: [colors, NumberDictionary.generate({ min: 100, max: 999 })],
+ length: 2,
+ separator: ''
+ });
+
+ let index = Math.floor(Math.random() * 64) + 1;
+
+ return { index, username, password };
};
export const actions = {
@@ -11,8 +34,8 @@ export const actions = {
let ip = request.headers.get('cf-connecting-ip');
let form = await fd(request);
- let { username, password, cipher, salt, pubkey, loginRedirect } = form;
- let user = { username, password, cipher, salt, pubkey };
+ let { profile, username, password, cipher, salt, pubkey, loginRedirect } = form;
+ let user = { profile, username, password, cipher, salt, pubkey };
let error;
if (loginRedirect === 'undefined') loginRedirect = undefined;
@@ -20,7 +43,7 @@ export const actions = {
try {
await post('/register', { user }, { 'cf-connecting-ip': ip });
} catch (e) {
- console.log(e)
+ console.log(e);
if (e.message.includes('taken')) error = e.message;
}
diff --git a/src/routes/(app)/register/+page.svelte b/src/routes/(app)/register/+page.svelte
index fd9e4417..37ff67c8 100644
--- a/src/routes/(app)/register/+page.svelte
+++ b/src/routes/(app)/register/+page.svelte
@@ -1,7 +1,33 @@
<script>
+ import punks from '$lib/punks';
+ import { upload } from '$lib/upload';
import { afterNavigate, invalidateAll } from '$app/navigation';
- import { RegisterOrLogin } from '$comp';
+ import { applyAction, deserialize } from '$app/forms';
+ import { tick } from 'svelte';
+ import { fly } from 'svelte/transition';
+ import { enhance } from '$app/forms';
+ import { Pin, Icon, Spinner } from '$comp';
+ import { focus, fail } from '$lib/utils';
+ import { avatar, password, loginRedirect } from '$lib/store';
+ import { t } from '$lib/translations';
+ import { page } from '$app/stores';
+ import { generate } from '$lib/nostr';
+
export let form;
+ export let data;
+
+ let cleared;
+ let clear = () => {
+ if (!cleared) {
+ cleared = true;
+ username = '';
+ $password = '';
+ revealPassword = false;
+ }
+ };
+
+ let { index, username } = data;
+ $password = data.password;
afterNavigate(async () => {
try {
@@ -10,6 +36,221 @@
console.log(e);
}
});
+
+ let token, formElement;
+ let code = [];
+ let redirect;
+
+ $: need2fa = form?.message === '2fa';
+ let cancel = () => (need2fa = false);
+ $: if (form?.message === '2fa' && form.token === token) token = '';
+
+ let email, btn;
+
+ $: update(form);
+ let update = (form) => form && ({ username, password: $password } = form);
+
+ $: token && token?.length === 6 && tick().then(() => btn.click());
+
+ let revealPassword = true;
+
+ let loading;
+ async function handleSubmit(e) {
+ loading = true;
+
+ let data = new FormData(this);
+ let user = Object.fromEntries(data);
+ await generate(user);
+
+ for (let k in user) {
+ data.set(k, user[k]);
+ }
+
+ data.set('profile', punks[index]);
+ if ($avatar) {
+ try {
+ let { hash } = JSON.parse(
+ await upload($avatar.file, $avatar.type, $avatar.progress, token)
+ );
+
+ data.set('profile', hash);
+ await fetch(`/api/public/${hash}.webp`, { cache: 'reload', mode: 'no-cors' });
+ } catch (e) {
+ console.log('problem uploading avatar', e);
+ }
+ }
+
+ const response = await fetch('/register', {
+ method: 'POST',
+ body: data
+ });
+
+ const result = deserialize(await response.text());
+
+ if (result.type === 'success') {
+ await invalidateAll();
+ }
+
+ applyAction(result);
+ loading = false;
+ }
+
+ let avatarInput;
+ $: src = `/api/public/${punks[index]}.webp`;
+ let decr = () => (index = index <= 1 ? 64 : index - 1);
+ let incr = () => (index = index >= 64 ? 1 : index + 1);
+ let selectAvatar = () => avatarInput.click();
+
+ let progress;
+ let handleFile = async ({ target }) => {
+ let type = 'profile';
+ let file = target.files[0];
+ if (!file) return;
+
+ if (file.size > 10000000) form.error = 'File too large';
+ $avatar = { file, type, progress };
+
+ var reader = new FileReader();
+ reader.onload = async (e) => {
+ src = e.target.result;
+ };
+
+ reader.readAsDataURL(file);
+ };
</script>
-<RegisterOrLogin pageID="register" bind:form />
+{#if need2fa}
+ <Pin bind:value={token} title="Enter 2FA Code" {cancel} notify={false} />
+{/if}
+
+<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.register')}</h1>
+
+ <input
+ type="file"
+ class="hidden"
+ bind:this={avatarInput}
+ on:change={(e) => handleFile(e, 'profile')}
+ />
+
+ <div class="relative">
+ <button class="absolute w-8 h-12 left-8 bg-white rounded top-12" on:click={decr}>
+ <Icon icon="chevron-left" style="w-8 " />
+ </button>
+ <div class="relative w-36 mx-auto" on:click={selectAvatar}>
+ <button
+ class="w-36 h-36 rounded-full border-4 border-white overflow-hidden flex mx-auto relative"
+ >
+ <img
+ {src}
+ class="w-full h-full object-cover object-center overflow-hidden"
+ alt={username}
+ />
+ </button>
+ <button
+ class="absolute bg-white rounded-full p-2 mx-auto right-0 bottom-0 z-10 bg-opacity-80"
+ >
+ <Icon icon="upload" style="w-8" />
+ </button>
+ </div>
+ <button class="absolute w-8 h-12 right-8 bg-white rounded top-12" on:click={incr}>
+ <Icon icon="chevron-left" style="w-8 rotate-180" />
+ </button>
+ </div>
+
+ {#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 || $page.url.searchParams.get('redirect')}
+ />
+ <input type="hidden" name="token" value={token} />
+
+ <div>
+ <label for="username" class="font-semibold">{$t('login.username')}</label>
+ <input
+ name="username"
+ type="text"
+ required
+ class="bg-primary"
+ bind:value={username}
+ on:focus={clear}
+ 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"
+ on:focus={clear}
+ />
+ {/if}
+ <button
+ type="button"
+ on:click={() => (revealPassword = !revealPassword)}
+ class="absolute right-5 top-10"
+ >
+ <Icon icon={revealPassword ? 'eye' : 'eye-off'} />
+ </button>
+ </div>
+
+ <p class="text-secondary text-sm">
+ {$t('login.passwordRecommendation')}
+ </p>
+
+ <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.register')}
+ {/if}
+ </button>
+ </form>
+
+ <p class="text-secondary text-center font-medium">
+ {$t('login.haveAccount')}
+ <a
+ href={'/login'}
+ class="block md:inline text-black underline underline-offset-4 hover:opacity-80"
+ >
+ {$t('login.signIn')}
+ </a>
+ </p>
+ </div>
+ </div>
+</div>
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 7e9cb755..2cd0c473 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,5 +1,6 @@
<script>
import '../app.css';
+ import { loading, t } from '$lib/translations';
export let data;
let { host, pathname } = data;
@@ -26,7 +27,9 @@
<meta name="twitter:creator" content="@coinoswallet" />
</svelte:head>
+{#if !$loading}
<slot />
+{/if}
<style global>
:root {
diff --git a/src/routes/+page.server.js b/src/routes/+page.server.js
index 7936e88b..9e4b62e2 100644
--- a/src/routes/+page.server.js
+++ b/src/routes/+page.server.js
@@ -8,5 +8,6 @@ export let load = async () => {
data = await get('/locations');
} catch (e) {}
+ data.faqs = ['cost', 'compatibility', 'safety', 'pos', 'bitcoin'];
return data;
};
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 0aaf887d..9d2ba213 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -12,11 +12,11 @@
import { t } from '$lib/translations';
import { close } from '$lib/socket';
- import { onMount } from 'svelte';
+ import { onDestroy, onMount } from 'svelte';
export let data;
- let { locations, user } = data;
- let update = (data) => ({ locations, user } = data);
+ let { faqs, locations, user } = data;
+ let update = (data) => ({ faqs, locations, user } = data);
$: update(data);
let howItWorks;
@@ -24,7 +24,6 @@
let about;
let log = console.log;
- let faqs = {};
const howItWorksSteps = [
{ image: 'hand', stepID: 'step1' },
@@ -32,9 +31,9 @@
{ image: 'smile', stepID: 'step3' }
];
- onMount(async () => {
- faqs = (await import('../locales/en.json')).default.faq;
+ let loaded;
+ onMount(async () => {
close();
try {
@@ -42,62 +41,68 @@
} catch (e) {
console.log(e);
}
+
+ loaded = true;
});
+
+ onDestroy(() => (loaded = false));
</script>
<svelte:head>
<script src="https://stamen-maps.a.ssl.fastly.net/js/tile.stamen.js"></script>
</svelte:head>
-<LandingHeader {howItWorks} {faq} {about} {user} />
-
-<main class="space-y-40 py-20 md:py-32 lg:py-36 xl:py-40 px-5 md:px-0">
- <LandingHero {user} />
- <LandingInfoCard
- image="lightning-qr"
- title={$t('landing.info1.title')}
- description={$t('landing.info1.description')}
- />
+{#if loaded}
+ <LandingHeader {howItWorks} {faq} {about} {user} />
- <LandingInfoCard image="phone-checkout" title={$t('landing.info2.title')} order="reverse">
- {$t('landing.info2.description1')}
- <br /><br />
- {$t('landing.info2.description2')}
- <br /><br />
- {@html $t('landing.info2.description3')}
- </LandingInfoCard>
-
- <div>
+ <main class="space-y-40 py-20 md:py-32 lg:py-36 xl:py-40 px-5 md:px-0">
+ <LandingHero {user} />
<LandingInfoCard
- image="customize"
- title={$t('landing.info3.title')}
- description={$t('landing.info3.description')}
+ image="lightning-qr"
+ title={$t('landing.info1.title')}
+ description={$t('landing.info1.description')}
/>
- <div bind:this={howItWorks} />
- <div id="about" />
- </div>
- <div>
- <h3 class="text-5xl font-medium mb-10 text-center">{$t('howItWorks.header')}</h3>
- <div class="grid lg:grid-cols-3 space-y-10 lg:space-y-0 text-center">
- {#each howItWorksSteps as step}
- <HowItWorksCard image={step.image} stepID={step.stepID} />
- {/each}
+ <LandingInfoCard image="phone-checkout" title={$t('landing.info2.title')} order="reverse">
+ {$t('landing.info2.description1')}
+ <br /><br />
+ {$t('landing.info2.description2')}
+ <br /><br />
+ {@html $t('landing.info2.description3')}
+ </LandingInfoCard>
+
+ <div>
+ <LandingInfoCard
+ image="customize"
+ title={$t('landing.info3.title')}
+ description={$t('landing.info3.description')}
+ />
+ <div bind:this={howItWorks} />
+ <div id="about" />
+ </div>
+
+ <div>
+ <h3 class="text-5xl font-medium mb-10 text-center">{$t('howItWorks.header')}</h3>
+ <div class="grid lg:grid-cols-3 space-y-10 lg:space-y-0 text-center">
+ {#each howItWorksSteps as step}
+ <HowItWorksCard image={step.image} stepID={step.stepID} />
+ {/each}
+ </div>
+ <div bind:this={faq} />
</div>
- <div bind:this={faq} />
- </div>
- <div>
- <div class="space-y-10">
- <h3 class="text-5xl font-medium text-center pt-40 -mt-40" id="faq">{$t('faq.header')}</h3>
- {#each Object.keys(faqs).filter((f) => f !== 'header') as faqID}
- <FaqCard questionID={faqID} />
- {/each}
+ <div>
+ <div class="space-y-10">
+ <h3 class="text-5xl font-medium text-center pt-40 -mt-40" id="faq">{$t('faq.header')}</h3>
+ {#each faqs as f}
+ <FaqCard questionID={f} />
+ {/each}
+ </div>
+ <div bind:this={about} />
</div>
- <div bind:this={about} />
- </div>
- <About {locations} />
-</main>
+ <About {locations} />
+ </main>
-<Footer />
+ <Footer />
+{/if}