diff --git a/src/lib/components/account/sendVerificationEmailModal.svelte b/src/lib/components/account/sendVerificationEmailModal.svelte index 1ad53ed1bb..100ff63090 100644 --- a/src/lib/components/account/sendVerificationEmailModal.svelte +++ b/src/lib/components/account/sendVerificationEmailModal.svelte @@ -9,10 +9,9 @@ import Link from '$lib/elements/link.svelte'; import { Card, Layout, Typography } from '@appwrite.io/pink-svelte'; import { Dependencies } from '$lib/constants'; - import { onMount, onDestroy } from 'svelte'; + import { onDestroy } from 'svelte'; import { resolve } from '$app/paths'; - import { browser } from '$app/environment'; - import { slide } from 'svelte/transition'; + import ResendCooldown from '$lib/components/resendCooldown.svelte'; let { show = $bindable(false), @@ -25,8 +24,6 @@ let error = $state(null); let creating = $state(false); let emailSent = $state(false); - let resendTimer = $state(0); - let timerInterval: ReturnType | null = null; async function logout() { error = null; @@ -41,72 +38,15 @@ const cleanUrl = $derived(page.url.origin + page.url.pathname); - // manage resend timer in localStorage - const EMAIL_SENT_KEY = 'email_verification_sent'; - const TIMER_END_KEY = 'email_verification_timer_end'; - - function startResendTimer() { - resendTimer = 60; - emailSent = true; - const timerEndTime = Date.now() + 60 * 1000; - - if (browser) { - localStorage.setItem(EMAIL_SENT_KEY, 'true'); - localStorage.setItem(TIMER_END_KEY, timerEndTime.toString()); - } - - startTimerCountdown(timerEndTime); - } - - function restoreTimerState() { - if (!browser) return; - const savedTimerEnd = localStorage.getItem(TIMER_END_KEY); - const savedEmailSent = localStorage.getItem(EMAIL_SENT_KEY); - - if (savedTimerEnd && savedEmailSent) { - const timerEndTime = parseInt(savedTimerEnd); - const now = Date.now(); - const remainingTime = Math.max(0, Math.ceil((timerEndTime - now) / 1000)); - - if (remainingTime > 0) { - resendTimer = remainingTime; - emailSent = true; - startTimerCountdown(timerEndTime); - } else { - // timer has expired, clean up - localStorage.removeItem(TIMER_END_KEY); - localStorage.removeItem(EMAIL_SENT_KEY); - - resendTimer = 0; - emailSent = false; - } - } - } - - function startTimerCountdown(timerEndTime: number) { - timerInterval = setInterval(() => { - const now = Date.now(); - const remainingTime = Math.max(0, Math.ceil((timerEndTime - now) / 1000)); - resendTimer = remainingTime; - if (remainingTime <= 0) { - clearInterval(timerInterval); - timerInterval = null; - if (browser) { - localStorage.removeItem(TIMER_END_KEY); - localStorage.removeItem(EMAIL_SENT_KEY); - } - } - }, 1000); - } + // Timer UI handled by ResendCooldown component async function onSubmit() { - if (creating || resendTimer > 0) return; + if (creating) return; error = null; creating = true; try { await sdk.forConsole.account.createVerification({ url: cleanUrl }); emailSent = true; - startResendTimer(); } catch (err) { error = err.message; } finally { @@ -114,18 +54,7 @@ } } - onMount(restoreTimerState); - - onDestroy(() => { - if (timerInterval) { - clearInterval(timerInterval); - } - - if (browser) { - localStorage.removeItem(TIMER_END_KEY); - localStorage.removeItem(EMAIL_SENT_KEY); - } - }); + onDestroy(() => {});
@@ -147,27 +76,21 @@ logout()}>Switch account - - {#if emailSent && resendTimer > 0} -
- - Didn't get the email? Try again in {resendTimer}s - -
- {/if} - + {#if emailSent} + + {:else} + + {/if}
diff --git a/src/lib/components/resendCooldown.svelte b/src/lib/components/resendCooldown.svelte new file mode 100644 index 0000000000..f390f06648 --- /dev/null +++ b/src/lib/components/resendCooldown.svelte @@ -0,0 +1,83 @@ + + +{#if remaining > 0} + Try again in {remaining}s +{:else} + Resend code +{/if} diff --git a/src/routes/(public)/(guest)/login/+page.svelte b/src/routes/(public)/(guest)/login/+page.svelte index e9f8f25b4d..1efc26447e 100644 --- a/src/routes/(public)/(guest)/login/+page.svelte +++ b/src/routes/(public)/(guest)/login/+page.svelte @@ -15,9 +15,33 @@ import { Layout } from '@appwrite.io/pink-svelte'; let mail: string, pass: string, disabled: boolean; + let showPasswordLogin: boolean = false; export let data; + $: showPasswordLogin = pass && pass.length > 0; + + async function sendSignInCode() { + try { + disabled = true; + // use createEmailToken for sign in with code + const sessionToken = await sdk.forConsole.account.createEmailToken({ + userId: 'unique', + email: mail + }); + + await goto( + `${base}/login/email-otp?email=${encodeURIComponent(mail)}&userId=${sessionToken.userId}` + ); + } catch (error) { + disabled = false; + addNotification({ + type: 'error', + message: error.message + }); + } + } + async function login() { try { disabled = true; @@ -52,7 +76,6 @@ return; } - // no specific redirect, so redirect will happen through invalidating the account await invalidate(Dependencies.ACCOUNT); } catch (error) { disabled = false; @@ -92,22 +115,27 @@ Sign in -
+ - + + {#if showPasswordLogin} + + {:else} + + {/if} + {#if isCloud} or + +
+ + + Didn't get it? + + + +
+
diff --git a/src/routes/(public)/(guest)/login/email-otp/+page.ts b/src/routes/(public)/(guest)/login/email-otp/+page.ts new file mode 100644 index 0000000000..974a7a0039 --- /dev/null +++ b/src/routes/(public)/(guest)/login/email-otp/+page.ts @@ -0,0 +1,39 @@ +import { base } from '$app/paths'; +import type { Campaign } from '$lib/stores/campaigns'; +import { sdk } from '$lib/stores/sdk'; +import { redirect } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async ({ url }) => { + if (!url.searchParams.has('email')) { + redirect(303, `${base}/login`); + } + + if (url.searchParams.has('code')) { + const code = url.searchParams.get('code'); + let campaign: Campaign; + try { + const couponData = await sdk.forConsole.billing.getCoupon(code); + if (couponData.campaign) { + campaign = await sdk.forConsole.billing.getCampaign(couponData.campaign); + return { + couponData, + campaign + }; + } else redirect(303, `${base}/login`); + } catch (e) { + redirect(303, `${base}/login`); + } + } + if (url.searchParams.has('campaign')) { + const campaignId = url.searchParams.get('campaign'); + let campaign: Campaign; + try { + campaign = await sdk.forConsole.billing.getCampaign(campaignId); + return { campaign }; + } catch (e) { + redirect(303, `${base}/login`); + } + } + return; +};