Skip to content

Commit a2f7d51

Browse files
committed
Fix Username, Password, and Email change dialogs
1 parent 7d9f986 commit a2f7d51

6 files changed

Lines changed: 126 additions & 43 deletions

File tree

src/lib/api/internal/v1/models/ChangePasswordRequest.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export interface ChangePasswordRequest {
2424
* @type {string}
2525
* @memberof ChangePasswordRequest
2626
*/
27-
oldPassword: string;
27+
currentPassword: string;
2828
/**
2929
*
3030
* @type {string}
@@ -37,7 +37,7 @@ export interface ChangePasswordRequest {
3737
* Check if a given object implements the ChangePasswordRequest interface.
3838
*/
3939
export function instanceOfChangePasswordRequest(value: object): value is ChangePasswordRequest {
40-
if (!('oldPassword' in value) || value['oldPassword'] === undefined) return false;
40+
if (!('currentPassword' in value) || value['currentPassword'] === undefined) return false;
4141
if (!('newPassword' in value) || value['newPassword'] === undefined) return false;
4242
return true;
4343
}
@@ -52,7 +52,7 @@ export function ChangePasswordRequestFromJSONTyped(json: any, ignoreDiscriminato
5252
}
5353
return {
5454

55-
'oldPassword': json['oldPassword'],
55+
'currentPassword': json['currentPassword'],
5656
'newPassword': json['newPassword'],
5757
};
5858
}
@@ -68,7 +68,7 @@ export function ChangePasswordRequestToJSONTyped(value?: ChangePasswordRequest |
6868

6969
return {
7070

71-
'oldPassword': value['oldPassword'],
71+
'currentPassword': value['currentPassword'],
7272
'newPassword': value['newPassword'],
7373
};
7474
}

src/lib/components/input/PasswordInput.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import type { AnyComponent } from '$lib/types/AnyComponent';
99
import type { ValidationResult } from '$lib/types/ValidationResult';
1010
import type { TimeoutHandle } from '$lib/types/WAPI';
11+
import type { Snippet } from 'svelte';
1112
import type { FullAutoFill } from 'svelte/elements';
1213
import PasswordStrengthMeter from './impl/PasswordStrengthMeter.svelte';
1314
@@ -133,15 +134,15 @@
133134
{validationResult}
134135
{Icon}
135136
onblur={() => (showPopup = false)}
136-
popup={showPopup ? popup : undefined}
137+
popup={showPopup ? (popup as Snippet) : undefined}
137138
>
138139
{#snippet after()}
139140
<Button
140141
type="button"
141142
class="cursor-pointer"
142143
onclick={() => (valueShown = !valueShown)}
143144
variant="ghost"
144-
disabled={validationResult === null || (validationResult && !validationResult.valid)}
145+
disabled={value.length == 0}
145146
>
146147
{#if valueShown}
147148
<EyeOff />

src/lib/stores/UserStore.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ function setSelfName(name: string) {
3636
});
3737
}
3838

39+
function setSelfEmail(email: string) {
40+
update((state) => {
41+
if (!state.self) return state;
42+
state.self.email = email;
43+
state.all = updateAllFromSelf(state.all, state.self);
44+
return state;
45+
});
46+
}
47+
3948
async function setSelf(user: ApiUserSelf) {
4049
update((state) => ({
4150
...state,
@@ -88,6 +97,7 @@ export const UserStore = {
8897
set,
8998
update,
9099
setSelfName,
100+
setSelfEmail,
91101
setSelf,
92102
refreshSelf,
93103
reset,
Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
<script lang="ts">
22
import Mail from '@lucide/svelte/icons/mail';
3+
import { accountV1Api } from '$lib/api';
34
import EmailInput from '$lib/components/input/EmailInput.svelte';
45
import { Button } from '$lib/components/ui/button';
6+
import { handleApiError } from '$lib/errorhandling/apiErrorHandling';
7+
import { UserStore } from '$lib/stores/UserStore';
58
import type { ApiUserSelf } from '$lib/types/ApiUser';
9+
import { toast } from 'svelte-sonner';
610
711
interface Props {
812
account: ApiUserSelf;
913
}
1014
1115
let { account }: Props = $props();
1216
17+
let loading = $state(false);
18+
1319
let email = $state<string>('');
20+
let emailValid = $state(false);
21+
22+
async function submitEmail(e: Event) {
23+
e.preventDefault();
24+
if (loading) return;
25+
loading = true;
26+
27+
try {
28+
await accountV1Api.authenticatedAccountChangeEmail({ email });
29+
30+
toast.success('Email changed successfully');
31+
32+
UserStore.setSelfEmail(email);
1433
15-
function submitEmail() {
16-
console.log('Submitting email');
34+
email = '';
35+
} catch (e) {
36+
await handleApiError(e);
37+
} finally {
38+
loading = false;
39+
}
1740
}
1841
</script>
1942

@@ -22,9 +45,16 @@
2245
placeholder={account.email}
2346
autocomplete="off"
2447
bind:value={email}
48+
bind:valid={emailValid}
2549
Icon={Mail}
2650
>
2751
{#snippet after()}
28-
<Button type="button" onclick={submitEmail} variant="ghost">Change</Button>
52+
<Button
53+
type="button"
54+
onclick={submitEmail}
55+
disabled={!emailValid || email === account.email || loading}
56+
>
57+
Change
58+
</Button>
2959
{/snippet}
3060
</EmailInput>
Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
<script lang="ts">
22
import KeyRound from '@lucide/svelte/icons/key-round';
3+
import { accountV1Api } from '$lib/api';
34
import PasswordInput from '$lib/components/input/PasswordInput.svelte';
45
import { Button } from '$lib/components/ui/button';
6+
import { handleApiError } from '$lib/errorhandling/apiErrorHandling';
57
import { validatePasswordMatch } from '$lib/inputvalidation/passwordValidator';
8+
import { toast } from 'svelte-sonner';
9+
10+
let loading = $state<boolean>(false);
611
712
let currentPassword = $state<string>('');
813
let currentPasswordValid = $derived(currentPassword.length > 0);
@@ -11,42 +16,63 @@
1116
let passwordValid = $state<boolean>(false);
1217
1318
let passwordConfirm = $state<string>('');
19+
let passwordConfirmValid = $derived(validatePasswordMatch(passwordConfirm, password));
20+
21+
async function submitPassword(e: SubmitEvent) {
22+
e.preventDefault();
23+
loading = true;
24+
25+
try {
26+
await accountV1Api.authenticatedAccountChangePassword({
27+
currentPassword,
28+
newPassword: password,
29+
});
30+
31+
toast.success('Password has been changed');
1432
15-
function submitPassword() {
16-
console.log('Submitting password');
33+
currentPassword = '';
34+
password = '';
35+
passwordConfirm = '';
36+
} catch (e) {
37+
await handleApiError(e);
38+
} finally {
39+
loading = false;
40+
}
1741
}
1842
1943
let canSubmitPassword = $derived(
20-
currentPasswordValid && passwordValid && password == passwordConfirm
44+
currentPasswordValid && passwordValid && (passwordConfirmValid?.valid ?? false) && !loading
2145
);
2246
</script>
2347

24-
<PasswordInput
25-
label="Current Password"
26-
placeholder="Current Password"
27-
autocomplete="off"
28-
bind:value={currentPassword}
29-
Icon={KeyRound}
30-
/>
31-
32-
<PasswordInput
33-
label="New Password"
34-
placeholder="New Password"
35-
autocomplete="new-password"
36-
bind:value={password}
37-
bind:valid={passwordValid}
38-
Icon={KeyRound}
39-
validate
40-
showStrengthMeter
41-
/>
42-
43-
<PasswordInput
44-
label="Confirm New Password"
45-
placeholder="Confirm New Password"
46-
autocomplete="new-password"
47-
bind:value={passwordConfirm}
48-
Icon={KeyRound}
49-
validate={validatePasswordMatch(passwordConfirm, password)}
50-
/>
51-
52-
<Button type="submit" disabled={!canSubmitPassword}>Change Password</Button>
48+
<form class="w-full" onsubmit={submitPassword}>
49+
<PasswordInput
50+
label="Current Password"
51+
placeholder="Current Password"
52+
autocomplete="off"
53+
bind:value={currentPassword}
54+
Icon={KeyRound}
55+
/>
56+
57+
<PasswordInput
58+
label="New Password"
59+
placeholder="New Password"
60+
autocomplete="new-password"
61+
bind:value={password}
62+
bind:valid={passwordValid}
63+
Icon={KeyRound}
64+
validate
65+
showStrengthMeter
66+
/>
67+
68+
<PasswordInput
69+
label="Confirm New Password"
70+
placeholder="Confirm New Password"
71+
autocomplete="new-password"
72+
bind:value={passwordConfirm}
73+
Icon={KeyRound}
74+
validate={passwordConfirmValid}
75+
/>
76+
77+
<Button type="submit" disabled={!canSubmitPassword}>Change Password</Button>
78+
</form>

src/routes/(authenticated)/settings/account/ChangeUsername.svelte

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@
1414
1515
let { account }: Props = $props();
1616
17+
let loading = $state(false);
18+
1719
let username = $state<string>('');
20+
let usernameValid = $state(false);
21+
22+
async function submitUsername(e: Event) {
23+
e.preventDefault();
24+
if (loading) return;
25+
loading = true;
1826
19-
async function submitUsername() {
2027
try {
2128
await accountV1Api.authenticatedAccountChangeUsername({ username });
2229
@@ -33,6 +40,8 @@
3340
3441
return true;
3542
});
43+
} finally {
44+
loading = false;
3645
}
3746
}
3847
</script>
@@ -42,9 +51,16 @@
4251
placeholder={account.name}
4352
autocomplete="off"
4453
bind:value={username}
54+
bind:valid={usernameValid}
4555
Icon={User}
4656
>
4757
{#snippet after()}
48-
<Button type="button" onclick={submitUsername} variant="ghost">Change</Button>
58+
<Button
59+
type="button"
60+
onclick={submitUsername}
61+
disabled={!usernameValid || username === account.email || loading}
62+
>
63+
Change
64+
</Button>
4965
{/snippet}
5066
</UsernameInput>

0 commit comments

Comments
 (0)