Skeleton: Initial support for user emails
This commit is contained in:
@ -18,6 +18,7 @@
|
|||||||
RacePickCard,
|
RacePickCard,
|
||||||
RaceResultCard,
|
RaceResultCard,
|
||||||
SeasonPickCard,
|
SeasonPickCard,
|
||||||
|
EMailIcon,
|
||||||
} from "$lib/components";
|
} from "$lib/components";
|
||||||
import { get_avatar_preview_event_handler } from "$lib/image";
|
import { get_avatar_preview_event_handler } from "$lib/image";
|
||||||
import {
|
import {
|
||||||
@ -37,11 +38,12 @@
|
|||||||
type ModalComponent,
|
type ModalComponent,
|
||||||
type ToastStore,
|
type ToastStore,
|
||||||
getToastStore,
|
getToastStore,
|
||||||
|
SlideToggle,
|
||||||
} from "@skeletonlabs/skeleton";
|
} from "@skeletonlabs/skeleton";
|
||||||
import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
|
import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
|
||||||
import { invalidate } from "$app/navigation";
|
import { invalidate } from "$app/navigation";
|
||||||
import { get_error_toast } from "$lib/toast";
|
import { get_error_toast, get_info_toast } from "$lib/toast";
|
||||||
import { pb, subscribe, unsubscribe } from "$lib/pocketbase";
|
import { pb, pbUser, subscribe, unsubscribe } from "$lib/pocketbase";
|
||||||
import { AVATAR_HEIGHT, AVATAR_WIDTH } from "$lib/config";
|
import { AVATAR_HEIGHT, AVATAR_WIDTH } from "$lib/config";
|
||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
|
|
||||||
@ -138,11 +140,33 @@
|
|||||||
// Reactive state
|
// Reactive state
|
||||||
let username_value: string = $state(data.user?.username ?? "");
|
let username_value: string = $state(data.user?.username ?? "");
|
||||||
let firstname_value: string = $state(data.user?.firstname ?? "");
|
let firstname_value: string = $state(data.user?.firstname ?? "");
|
||||||
|
let email_value: string = $state(data.user?.email ?? "");
|
||||||
let password_value: string = $state("");
|
let password_value: string = $state("");
|
||||||
let avatar_value: FileList | undefined = $state();
|
let avatar_value: FileList | undefined = $state();
|
||||||
|
|
||||||
|
let registration_mode: boolean = $state(false);
|
||||||
|
|
||||||
|
// Add "Enter" event listeners for login/register text inputs
|
||||||
|
const enter_handler = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
// Cancel the default action, if needed
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
registration_mode ? update_profile(true) : login();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Database actions
|
// Database actions
|
||||||
const login = async (): Promise<void> => {
|
const login = async (): Promise<void> => {
|
||||||
|
if (!username_value || username_value.trim() === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please enter your username!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!password_value || password_value.trim() === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please enter your password!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await pb.collection("users").authWithPassword(username_value, password_value);
|
await pb.collection("users").authWithPassword(username_value, password_value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -150,6 +174,9 @@
|
|||||||
}
|
}
|
||||||
await invalidate("data:user");
|
await invalidate("data:user");
|
||||||
drawerStore.close();
|
drawerStore.close();
|
||||||
|
username_value = data.user?.username ?? "";
|
||||||
|
firstname_value = data.user?.firstname ?? "";
|
||||||
|
email_value = data.user?.email ?? "";
|
||||||
password_value = "";
|
password_value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,6 +186,7 @@
|
|||||||
drawerStore.close();
|
drawerStore.close();
|
||||||
username_value = "";
|
username_value = "";
|
||||||
firstname_value = "";
|
firstname_value = "";
|
||||||
|
email_value = "";
|
||||||
password_value = "";
|
password_value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -201,6 +229,10 @@
|
|||||||
toastStore.trigger(get_error_toast("Please enter your first name!"));
|
toastStore.trigger(get_error_toast("Please enter your first name!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!email_value || email_value.trim() === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please enter your e-mail address!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!password_value || password_value.trim() === "") {
|
if (!password_value || password_value.trim() === "") {
|
||||||
toastStore.trigger(get_error_toast("Please enter a password!"));
|
toastStore.trigger(get_error_toast("Please enter a password!"));
|
||||||
return;
|
return;
|
||||||
@ -209,11 +241,17 @@
|
|||||||
await pb.collection("users").create({
|
await pb.collection("users").create({
|
||||||
username: username_value.trim(),
|
username: username_value.trim(),
|
||||||
firstname: firstname_value.trim(),
|
firstname: firstname_value.trim(),
|
||||||
|
email: email_value.trim(),
|
||||||
password: password_value.trim(),
|
password: password_value.trim(),
|
||||||
passwordConfirm: password_value.trim(), // lol
|
passwordConfirm: password_value.trim(), // lol
|
||||||
admin: false,
|
admin: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await pb.collection("users").requestVerification(email_value.trim());
|
||||||
|
toastStore.trigger(get_info_toast("Check your inbox!"));
|
||||||
|
|
||||||
|
pb.authStore.clear();
|
||||||
|
|
||||||
await login();
|
await login();
|
||||||
} else {
|
} else {
|
||||||
if (!data.user?.id || data.user.id === "") {
|
if (!data.user?.id || data.user.id === "") {
|
||||||
@ -221,12 +259,20 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (email_value && email_value.trim() !== data.user.email) {
|
||||||
|
await pb.collection("users").requestEmailChange(email_value.trim());
|
||||||
|
toastStore.trigger(get_info_toast("Check your inbox!"));
|
||||||
|
}
|
||||||
|
|
||||||
await pb.collection("users").update(data.user.id, {
|
await pb.collection("users").update(data.user.id, {
|
||||||
username: username_value.trim().length > 0 ? username_value.trim() : data.user.username,
|
username: username_value.trim().length > 0 ? username_value.trim() : data.user.username,
|
||||||
firstname:
|
firstname:
|
||||||
firstname_value.trim().length > 0 ? firstname_value.trim() : data.user.firstname,
|
firstname_value.trim().length > 0 ? firstname_value.trim() : data.user.firstname,
|
||||||
avatar: avatar_avif,
|
avatar: avatar_avif,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pb.authStore.clear();
|
||||||
|
toastStore.trigger(get_info_toast("Please login again (sry UwU)!"));
|
||||||
drawerStore.close();
|
drawerStore.close();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -322,7 +368,22 @@
|
|||||||
<!-- Login Drawer -->
|
<!-- Login Drawer -->
|
||||||
<!-- Login Drawer -->
|
<!-- Login Drawer -->
|
||||||
<div class="flex flex-col gap-2 p-2 pt-3">
|
<div class="flex flex-col gap-2 p-2 pt-3">
|
||||||
<h4 class="h4 select-none">Enter Username and Password</h4>
|
<div class="flex">
|
||||||
|
<h4 class="h4 select-none text-nowrap align-middle font-bold" style="line-height: 32px;">
|
||||||
|
Login or Register
|
||||||
|
</h4>
|
||||||
|
<div class="w-full"></div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<span class="align-middle" style="line-height: 32px;">Login</span>
|
||||||
|
<SlideToggle
|
||||||
|
name="registrationmode"
|
||||||
|
background="bg-tertiary-500"
|
||||||
|
active="bg-tertiary-500"
|
||||||
|
bind:checked={registration_mode}
|
||||||
|
/>
|
||||||
|
<span class="align-middle" style="line-height: 32px;">Register</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
bind:value={username_value}
|
bind:value={username_value}
|
||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
@ -330,28 +391,68 @@
|
|||||||
minlength={3}
|
minlength={3}
|
||||||
maxlength={10}
|
maxlength={10}
|
||||||
required
|
required
|
||||||
|
onkeypress={enter_handler}
|
||||||
>
|
>
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
</Input>
|
</Input>
|
||||||
<Input
|
<div
|
||||||
bind:value={firstname_value}
|
class="{registration_mode
|
||||||
placeholder="First Name (leave empty for login)"
|
? ''
|
||||||
autocomplete="off"
|
: 'mt-[-8px] h-0'} overflow-hidden transition-all duration-150 ease-out"
|
||||||
>
|
>
|
||||||
<NameIcon />
|
<Input
|
||||||
</Input>
|
bind:value={firstname_value}
|
||||||
|
placeholder="First Name"
|
||||||
|
autocomplete="off"
|
||||||
|
tabindex={registration_mode ? 0 : -1}
|
||||||
|
onkeypress={enter_handler}
|
||||||
|
>
|
||||||
|
<NameIcon />
|
||||||
|
</Input>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="{registration_mode
|
||||||
|
? ''
|
||||||
|
: 'mt-[-8px] h-0'} overflow-hidden transition-all duration-150 ease-out"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
id="login_email"
|
||||||
|
type="email"
|
||||||
|
bind:value={email_value}
|
||||||
|
placeholder="E-Mail"
|
||||||
|
autocomplete="email"
|
||||||
|
tabindex={registration_mode ? 0 : -1}
|
||||||
|
onkeypress={enter_handler}
|
||||||
|
>
|
||||||
|
<EMailIcon />
|
||||||
|
</Input>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
|
id="login_password"
|
||||||
bind:value={password_value}
|
bind:value={password_value}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
required
|
required
|
||||||
|
onkeypress={enter_handler}
|
||||||
>
|
>
|
||||||
<PasswordIcon />
|
<PasswordIcon />
|
||||||
</Input>
|
</Input>
|
||||||
<div class="flex justify-end gap-2">
|
<div
|
||||||
<Button onclick={login} color="tertiary" shadow>Login</Button>
|
class="{!registration_mode
|
||||||
<Button onclick={update_profile(true)} color="tertiary" shadow>Register</Button>
|
? ''
|
||||||
|
: 'mt-[-8px] h-0'} w-full overflow-hidden transition-all duration-150 ease-out"
|
||||||
|
>
|
||||||
|
<Button onclick={login} color="tertiary" width="w-full" shadow>Login</Button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="{registration_mode
|
||||||
|
? ''
|
||||||
|
: 'mt-[-8px] h-0'} w-full overflow-hidden transition-all duration-150 ease-out"
|
||||||
|
>
|
||||||
|
<Button onclick={update_profile(true)} color="tertiary" width="w-full" shadow>
|
||||||
|
Register
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if $drawerStore.id === "profile_drawer" && data.user}
|
{:else if $drawerStore.id === "profile_drawer" && data.user}
|
||||||
@ -359,7 +460,7 @@
|
|||||||
<!-- Profile Drawer -->
|
<!-- Profile Drawer -->
|
||||||
<!-- Profile Drawer -->
|
<!-- Profile Drawer -->
|
||||||
<div class="flex flex-col gap-2 p-2 pt-3">
|
<div class="flex flex-col gap-2 p-2 pt-3">
|
||||||
<h4 class="h4 select-none">Edit Profile</h4>
|
<h4 class="h4 select-none align-middle font-bold" style="line-height: 32px;">Edit Profile</h4>
|
||||||
<Input
|
<Input
|
||||||
bind:value={username_value}
|
bind:value={username_value}
|
||||||
maxlength={10}
|
maxlength={10}
|
||||||
@ -371,6 +472,9 @@
|
|||||||
<Input bind:value={firstname_value} placeholder="First Name" autocomplete="off">
|
<Input bind:value={firstname_value} placeholder="First Name" autocomplete="off">
|
||||||
<NameIcon />
|
<NameIcon />
|
||||||
</Input>
|
</Input>
|
||||||
|
<Input bind:value={email_value} placeholder="E-Mail" autocomplete="email">
|
||||||
|
<EMailIcon />
|
||||||
|
</Input>
|
||||||
<FileDropzone
|
<FileDropzone
|
||||||
name="avatar"
|
name="avatar"
|
||||||
bind:files={avatar_value}
|
bind:files={avatar_value}
|
||||||
@ -381,8 +485,10 @@
|
|||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</FileDropzone>
|
</FileDropzone>
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<Button onclick={update_profile()} color="secondary" shadow>Save Changes</Button>
|
<Button onclick={update_profile()} color="secondary" width="w-full" shadow>
|
||||||
<Button onclick={logout} color="primary" shadow>Logout</Button>
|
Save Changes
|
||||||
|
</Button>
|
||||||
|
<Button onclick={logout} color="primary" width="w-full" shadow>Logout</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
Reference in New Issue
Block a user