aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/(app)
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/routes/(app)
parent6d8eb8a609ebb6c1781c463a837bf9e19a6d3d4b (diff)
downloadcoinos-ui-b466b3d39c3b9ec792ef924c7f4750fb70b6a016.tar.gz
coinos-ui-b466b3d39c3b9ec792ef924c7f4750fb70b6a016.zip
prefill register page
Diffstat (limited to 'src/routes/(app)')
-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
9 files changed, 325 insertions, 35 deletions
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>