Skip to content

Commit 943e224

Browse files
Copilotstnguyen90
andauthored
Allow email update from the verify email modal (#2911)
* Initial plan * Add update email address option to verify email modal Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
1 parent 96eeac0 commit 943e224

2 files changed

Lines changed: 121 additions & 43 deletions

File tree

src/lib/components/account/sendVerificationEmailModal.svelte

Lines changed: 119 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
22
import { invalidate, goto } from '$app/navigation';
3+
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
34
import { Modal } from '$lib/components';
4-
import { Button } from '$lib/elements/forms';
5+
import { Button, InputEmail, InputPassword } from '$lib/elements/forms';
56
import { sdk } from '$lib/stores/sdk';
67
import { user } from '$lib/stores/user';
78
import { get } from 'svelte/store';
@@ -28,6 +29,17 @@
2829
let resendTimer = $state(0);
2930
let timerInterval: ReturnType<typeof setInterval> | null = null;
3031
32+
let showUpdateEmail = $state(false);
33+
let newEmail = $state('');
34+
let newPassword = $state('');
35+
let updating = $state(false);
36+
37+
$effect(() => {
38+
if (showUpdateEmail) {
39+
newEmail = email || get(user)?.email || '';
40+
}
41+
});
42+
3143
async function logout() {
3244
error = null;
3345
try {
@@ -114,6 +126,32 @@
114126
}
115127
}
116128
129+
async function updateEmail() {
130+
error = null;
131+
updating = true;
132+
try {
133+
await sdk.forConsole.account.updateEmail({
134+
email: newEmail,
135+
password: newPassword
136+
});
137+
await invalidate(Dependencies.ACCOUNT);
138+
trackEvent(Submit.AccountUpdateEmail);
139+
resetUpdateEmailForm();
140+
} catch (err) {
141+
error = err.message;
142+
trackError(err, Submit.AccountUpdateEmail);
143+
} finally {
144+
updating = false;
145+
}
146+
}
147+
148+
function resetUpdateEmailForm() {
149+
showUpdateEmail = false;
150+
newEmail = '';
151+
newPassword = '';
152+
error = null;
153+
}
154+
117155
onMount(restoreTimerState);
118156
119157
onDestroy(() => {
@@ -129,48 +167,87 @@
129167
</script>
130168

131169
<div class="email-verification-scrim">
132-
<Modal
133-
bind:show
134-
bind:error
135-
title="Verify your email address"
136-
{onSubmit}
137-
dismissible={false}
138-
autoClose={false}
139-
backdrop={false}>
140-
<Card.Base variant="secondary" padding="s">
141-
<Layout.Stack gap="xxs">
142-
<Typography.Text gap="m">
143-
To continue using Appwrite Cloud, please verify your email address. An email
144-
will be sent to <Typography.Text
145-
variant="m-600"
146-
color="neutral-secondary"
147-
style="display: inline;">{email || get(user)?.email}</Typography.Text>
148-
</Typography.Text>
149-
150-
<Link variant="default" on:click={() => logout()}>Switch account</Link>
151-
152-
{#if emailSent && resendTimer > 0}
153-
<div transition:slide={{ duration: 150 }}>
154-
<Typography.Text
170+
{#if !showUpdateEmail}
171+
<Modal
172+
bind:show
173+
bind:error
174+
title="Verify your email address"
175+
{onSubmit}
176+
dismissible={false}
177+
autoClose={false}
178+
backdrop={false}>
179+
<Card.Base variant="secondary" padding="s">
180+
<Layout.Stack gap="xxs">
181+
<Typography.Text gap="m">
182+
To continue using Appwrite Cloud, please verify your email address. An email
183+
will be sent to <Typography.Text
184+
variant="m-600"
155185
color="neutral-secondary"
156-
style="margin-block-start: var(--gap-L, 16px);">
157-
Didn't get the email? Try again in {resendTimer}s
158-
</Typography.Text>
159-
</div>
160-
{/if}
161-
</Layout.Stack>
162-
</Card.Base>
163-
164-
<svelte:fragment slot="footer">
165-
<Button
166-
submit
167-
submissionLoader
168-
forceShowLoader={creating}
169-
disabled={creating || resendTimer > 0}>
170-
{emailSent ? 'Resend email' : 'Send email'}
171-
</Button>
172-
</svelte:fragment>
173-
</Modal>
186+
style="display: inline;">{email || get(user)?.email}</Typography.Text>
187+
</Typography.Text>
188+
189+
<Typography.Text>
190+
Wrong email? <Link
191+
variant="default"
192+
on:click={() => {
193+
showUpdateEmail = true;
194+
error = null;
195+
}}>Update email address</Link> or <Link
196+
variant="default"
197+
on:click={() => logout()}>Switch account</Link>
198+
</Typography.Text>
199+
200+
{#if emailSent && resendTimer > 0}
201+
<div transition:slide={{ duration: 150 }}>
202+
<Typography.Text
203+
color="neutral-secondary"
204+
style="margin-block-start: var(--gap-L, 16px);">
205+
Didn't get the email? Try again in {resendTimer}s
206+
</Typography.Text>
207+
</div>
208+
{/if}
209+
</Layout.Stack>
210+
</Card.Base>
211+
212+
<svelte:fragment slot="footer">
213+
<Button
214+
submit
215+
submissionLoader
216+
forceShowLoader={creating}
217+
disabled={creating || resendTimer > 0}>
218+
{emailSent ? 'Resend email' : 'Send email'}
219+
</Button>
220+
</svelte:fragment>
221+
</Modal>
222+
{:else}
223+
<Modal
224+
bind:show={showUpdateEmail}
225+
bind:error
226+
title="Update email address"
227+
onSubmit={updateEmail}
228+
autoClose={false}
229+
backdrop={false}>
230+
<InputEmail
231+
id="new-email"
232+
label="Email"
233+
placeholder="Enter email"
234+
bind:value={newEmail}
235+
required
236+
helper="You'll need access to this email to verify your account" />
237+
<InputPassword
238+
id="update-password"
239+
label="Password"
240+
placeholder="Enter password"
241+
required
242+
bind:value={newPassword} />
243+
244+
<svelte:fragment slot="footer">
245+
<Button text on:click={resetUpdateEmailForm}>Cancel</Button>
246+
<Button submit submissionLoader forceShowLoader={updating} disabled={updating}
247+
>Update</Button>
248+
</svelte:fragment>
249+
</Modal>
250+
{/if}
174251
</div>
175252

176253
<style>

src/lib/elements/forms/inputEmail.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
export let readonly = false;
1414
export let autofocus = false;
1515
export let autocomplete = false;
16+
export let helper: string = undefined;
1617
export let leadingIcon: ComponentType | undefined = undefined;
1718
1819
let error: string;
@@ -49,7 +50,7 @@
4950
on:input
5051
on:invalid={handleInvalid}
5152
type="email"
52-
helper={error}
53+
helper={error || helper}
5354
state={error ? 'error' : 'default'}
5455
autofocus={autofocus || undefined}
5556
autocomplete={autocomplete ? 'on' : 'off'}

0 commit comments

Comments
 (0)