diff --git a/backend/package.json b/backend/package.json index c7f9775e..0d284867 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "tar": "^7.4.3" }, "dependencies": { - "@pezkuwi/api": "^16.5.18", + "@pezkuwi/api": "^16.5.22", "@pezkuwi/util": "^14.0.13", "@pezkuwi/util-crypto": "^14.0.13", "cors": "^2.8.5", diff --git a/mobile/package.json b/mobile/package.json index e3e64a46..bc59751b 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -38,9 +38,9 @@ }, "dependencies": { "@babel/runtime": "^7.28.4", - "@pezkuwi/api": "^16.5.18", + "@pezkuwi/api": "^16.5.22", "@pezkuwi/keyring": "^14.0.13", - "@pezkuwi/types": "^16.5.18", + "@pezkuwi/types": "^16.5.22", "@pezkuwi/util": "^14.0.13", "@pezkuwi/util-crypto": "^14.0.13", "@react-native-async-storage/async-storage": "^2.2.0", @@ -80,17 +80,17 @@ }, "overrides": { "react-test-renderer": "19.1.0", - "@pezkuwi/api-augment": "^16.5.18", - "@pezkuwi/api-base": "^16.5.18", - "@pezkuwi/api-derive": "^16.5.18", - "@pezkuwi/rpc-augment": "^16.5.18", - "@pezkuwi/rpc-core": "^16.5.18", - "@pezkuwi/rpc-provider": "^16.5.18", - "@pezkuwi/types": "^16.5.18", - "@pezkuwi/types-augment": "^16.5.18", - "@pezkuwi/types-codec": "^16.5.18", - "@pezkuwi/types-create": "^16.5.18", - "@pezkuwi/types-known": "^16.5.18", + "@pezkuwi/api-augment": "^16.5.22", + "@pezkuwi/api-base": "^16.5.22", + "@pezkuwi/api-derive": "^16.5.22", + "@pezkuwi/rpc-augment": "^16.5.22", + "@pezkuwi/rpc-core": "^16.5.22", + "@pezkuwi/rpc-provider": "^16.5.22", + "@pezkuwi/types": "^16.5.22", + "@pezkuwi/types-augment": "^16.5.22", + "@pezkuwi/types-codec": "^16.5.22", + "@pezkuwi/types-create": "^16.5.22", + "@pezkuwi/types-known": "^16.5.22", "@pezkuwi/networks": "^16.5.9", "@pezkuwi/keyring": "^14.0.13", "@pezkuwi/util": "^14.0.13", diff --git a/web/package.json b/web/package.json index ac6b31e2..0f53550c 100644 --- a/web/package.json +++ b/web/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.0", - "@pezkuwi/api": "^16.5.18", + "@pezkuwi/api": "^16.5.22", "@pezkuwi/extension-dapp": "^0.62.20", "@pezkuwi/keyring": "^14.0.13", "@pezkuwi/util": "^14.0.13", @@ -92,18 +92,18 @@ "zod": "^3.23.8" }, "overrides": { - "@pezkuwi/api": "^16.5.18", - "@pezkuwi/api-augment": "^16.5.18", - "@pezkuwi/api-base": "^16.5.18", - "@pezkuwi/api-derive": "^16.5.18", - "@pezkuwi/rpc-augment": "^16.5.18", - "@pezkuwi/rpc-core": "^16.5.18", - "@pezkuwi/rpc-provider": "^16.5.18", - "@pezkuwi/types": "^16.5.18", - "@pezkuwi/types-augment": "^16.5.18", - "@pezkuwi/types-codec": "^16.5.18", - "@pezkuwi/types-create": "^16.5.18", - "@pezkuwi/types-known": "^16.5.18", + "@pezkuwi/api": "^16.5.22", + "@pezkuwi/api-augment": "^16.5.22", + "@pezkuwi/api-base": "^16.5.22", + "@pezkuwi/api-derive": "^16.5.22", + "@pezkuwi/rpc-augment": "^16.5.22", + "@pezkuwi/rpc-core": "^16.5.22", + "@pezkuwi/rpc-provider": "^16.5.22", + "@pezkuwi/types": "^16.5.22", + "@pezkuwi/types-augment": "^16.5.22", + "@pezkuwi/types-codec": "^16.5.22", + "@pezkuwi/types-create": "^16.5.22", + "@pezkuwi/types-known": "^16.5.22", "@pezkuwi/types-support": "^16.5.18", "@pezkuwi/keyring": "$@pezkuwi/keyring", "@pezkuwi/util": "$@pezkuwi/util", diff --git a/web/src/pages/EmailVerification.tsx b/web/src/pages/EmailVerification.tsx index d9b547cd..d1a17f97 100644 --- a/web/src/pages/EmailVerification.tsx +++ b/web/src/pages/EmailVerification.tsx @@ -1,46 +1,155 @@ import { useEffect, useState } from 'react'; -import { useSearchParams, useNavigate } from 'react-router-dom'; +import { useSearchParams, useNavigate, useLocation } from 'react-router-dom'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { supabase } from '@/lib/supabase'; -import { CheckCircle, XCircle, Loader2, ArrowLeft } from 'lucide-react'; +import { CheckCircle, XCircle, Loader2, ArrowLeft, Mail, RefreshCw } from 'lucide-react'; export default function EmailVerification() { const [searchParams] = useSearchParams(); + const location = useLocation(); const navigate = useNavigate(); - const [verifying, setVerifying] = useState(true); + const [verifying, setVerifying] = useState(false); const [verified, setVerified] = useState(false); const [error, setError] = useState(''); + const [resending, setResending] = useState(false); + const [resent, setResent] = useState(false); + + // Get email from navigation state (after sign up) + const email = location.state?.email; + const token = searchParams.get('token'); + const type = searchParams.get('type'); useEffect(() => { - const token = searchParams.get('token'); - if (token) { + // Handle Supabase email confirmation callback + if (type === 'signup' || type === 'email_change') { + // Supabase handles this automatically via the URL hash + // Check if we have an active session + supabase.auth.getSession().then(({ data: { session } }) => { + if (session) { + setVerified(true); + } + }); + } else if (token) { verifyEmail(token); - } else { - setError('No verification token provided'); - setVerifying(false); } - }, [searchParams]); + }, [token, type]); - const verifyEmail = async (token: string) => { + const verifyEmail = async (verifyToken: string) => { + setVerifying(true); try { const { error } = await supabase.functions.invoke('email-verification', { - body: { action: 'verify', token } + body: { action: 'verify', token: verifyToken } }); if (error) throw error; - + setVerified(true); - } catch (err: Error) { - setError(err.message || 'Failed to verify email'); + } catch (err: unknown) { + const errorMessage = err instanceof Error ? err.message : 'Failed to verify email'; + setError(errorMessage); } finally { setVerifying(false); } }; + const handleResendEmail = async () => { + if (!email) return; + + setResending(true); + setError(''); + try { + const { error } = await supabase.auth.resend({ + type: 'signup', + email: email, + }); + + if (error) throw error; + + setResent(true); + setTimeout(() => setResent(false), 5000); + } catch (err: unknown) { + const errorMessage = err instanceof Error ? err.message : 'Failed to resend email'; + setError(errorMessage); + } finally { + setResending(false); + } + }; + + // Show "check your email" screen after sign up + if (email && !token && !type) { + return ( +
+ + + +
+ +
+ Check Your Email + + We sent a verification link to + +
+ +

{email}

+ +
+

Please check your email and click the verification link to activate your account.

+

If you don't see the email, check your spam folder.

+
+ + {error && ( +

{error}

+ )} + + {resent && ( +

Verification email sent!

+ )} + +
+ + + +
+
+
+
+ ); + } + + // Show verification status screen (when user clicks email link) return ( -
- +
+ - Email Verification - + Email Verification + {verifying ? 'Verifying your email...' : 'Email verification status'} {verifying && (
- -

Please wait while we verify your email...

+ +

Please wait while we verify your email...

)} {!verifying && verified && (
-

Email Verified Successfully!

-

+

Email Verified Successfully!

+

Your email has been verified. You can now access all features.

-
)} - {!verifying && !verified && ( + {!verifying && !verified && error && (
-

Verification Failed

-

{error}

+

Verification Failed

+

{error}

-
)} + + {!verifying && !verified && !error && !token && !type && ( +
+ +

No Verification Token

+

+ Please click the verification link in your email. +

+ +
+ )}
); -} \ No newline at end of file +} diff --git a/web/src/pages/Login.tsx b/web/src/pages/Login.tsx index 315807b1..58436462 100644 --- a/web/src/pages/Login.tsx +++ b/web/src/pages/Login.tsx @@ -79,16 +79,17 @@ const Login: React.FC = () => { } const { error } = await signUp( - signupData.email, - signupData.password, - signupData.name, + signupData.email, + signupData.password, + signupData.name, signupData.referralCode ); - + if (error) { setError(error.message); } else { - navigate('/'); + // Redirect to email verification page + navigate('/email-verification', { state: { email: signupData.email } }); } } catch { setError('Signup failed. Please try again.');