Skip to content

Commit 7c2274b

Browse files
committed
fix: Add interstitial verifying + persist email
https://harperdb.atlassian.net/browse/STUDIO-512
1 parent 8fc9e38 commit 7c2274b

File tree

6 files changed

+109
-30
lines changed

6 files changed

+109
-30
lines changed

src/features/auth/ForgotPassword.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FormItem } from '@/components/ui/form/FormItem';
55
import { FormLabel } from '@/components/ui/form/FormLabel';
66
import { FormMessage } from '@/components/ui/form/FormMessage';
77
import { zodRequireEmail } from '@/lib/zod/email';
8-
import { Link, useNavigate } from '@tanstack/react-router';
8+
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
99
import { useForgotPasswordMutation } from '@/features/auth/hooks/useForgotPassword';
1010
import { useEffect } from 'react';
1111
import { useForm } from 'react-hook-form';
@@ -21,12 +21,14 @@ const ForgotPasswordSchema = z.object({
2121

2222
export function ForgotPassword() {
2323
const navigate = useNavigate();
24+
const { me: formPersistenceEmail } = useSearch({ strict: false });
2425
const methods = useForm({
2526
resolver: zodResolver(ForgotPasswordSchema),
2627
defaultValues: {
27-
email: '',
28+
email: formPersistenceEmail || '',
2829
},
2930
});
31+
const email = methods.watch('email');
3032
const { setFocus, control, handleSubmit } = methods;
3133

3234
useEffect(() => {
@@ -80,15 +82,13 @@ export function ForgotPassword() {
8082
</form>
8183
</Form>
8284
<div className="flex px-4 mt-4 underline place-content-between">
83-
<Link className="text-sm hover:text-blue-300" to="/sign-in">
85+
<Link className="text-sm hover:text-blue-300" to="/sign-in" search={{ me: email }}>
8486
Sign in to your account
8587
</Link>
86-
<Link className="text-sm hover:text-blue-300" to="/sign-up">
88+
<Link className="text-sm hover:text-blue-300" to="/sign-up" search={{ me: email }}>
8789
Sign up for free
8890
</Link>
8991
</div>
9092
</div>
9193
);
9294
}
93-
94-

src/features/auth/SignIn.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,20 @@ const SignInSchema = z.object({
2828

2929
export function SignIn() {
3030
const navigate = useNavigate();
31+
const { me: formPersistenceEmail } = useSearch({ strict: false });
3132
const router = useRouter();
3233
const queryClient = useQueryClient();
3334
const { redirect } = useSearch({ strict: false });
3435

3536
const methods = useForm({
3637
resolver: zodResolver(SignInSchema),
3738
defaultValues: {
38-
email: '',
39+
email: formPersistenceEmail || '',
3940
password: '',
4041
},
4142
});
43+
44+
const email = methods.watch('email');
4245
const { handleSubmit, control, setFocus } = methods;
4346

4447
useEffect(() => {
@@ -111,15 +114,13 @@ export function SignIn() {
111114
</form>
112115
</Form>
113116
<div className="flex px-4 mt-4 underline place-content-between">
114-
<Link className="text-sm hover:text-blue-300" to="/sign-up">
117+
<Link className="text-sm hover:text-blue-300" to="/sign-up" search={{ me: email }}>
115118
Sign up for free
116119
</Link>
117-
<Link className="text-sm hover:text-blue-300" to="/forgot-password">
120+
<Link className="text-sm hover:text-blue-300" to="/forgot-password" search={{ me: email }}>
118121
Forgot password?
119122
</Link>
120123
</div>
121124
</div>
122125
);
123126
}
124-
125-

src/features/auth/SignUp.tsx

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { zodResolver } from '@hookform/resolvers/zod';
1515
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
1616
import { useEffect } from 'react';
1717
import { useForm } from 'react-hook-form';
18-
import { toast } from 'sonner';
1918
import { z } from 'zod';
2019

2120
const SignInSchema = z.object({
@@ -42,17 +41,19 @@ const SignInSchema = z.object({
4241

4342
export function SignUp() {
4443
const navigate = useNavigate();
45-
const { email } = useSearch({ strict: false });
44+
const { email: searchEmail, me: formPersistenceEmail } = useSearch({ strict: false });
4645
const methods = useForm({
4746
resolver: zodResolver(SignInSchema),
4847
defaultValues: {
4948
firstname: '',
5049
lastname: '',
51-
email: email || '',
50+
email: formPersistenceEmail || searchEmail || '',
5251
password: '',
5352
confirmPassword: '',
5453
},
5554
});
55+
56+
const email = methods.watch('email');
5657
const { setFocus, control, handleSubmit } = methods;
5758

5859
useEffect(() => {
@@ -66,15 +67,6 @@ export function SignUp() {
6667
const { confirmPassword, ...userData } = formData;
6768
submitSignUpData(userData, {
6869
onSuccess: () => {
69-
toast.success('Success', {
70-
duration: 60_000,
71-
description: 'Your account has been created! Please check your email to finish activating your' +
72-
' account.',
73-
action: {
74-
label: 'Dismiss',
75-
onClick: () => toast.dismiss(),
76-
},
77-
});
7870
const company = parseCompanyFromEmail(userData.email);
7971
reoClient?.identify?.({
8072
username: userData.email,
@@ -83,7 +75,7 @@ export function SignUp() {
8375
lastname: userData.lastname,
8476
...(company ? { company } : {}),
8577
});
86-
navigate({ to: '/sign-in' });
78+
void navigate({ to: '/verifying?email=' + encodeURIComponent(userData.email) });
8779
},
8880
});
8981
};
@@ -138,8 +130,8 @@ export function SignUp() {
138130
<FormControl>
139131
<Input
140132
type="email"
141-
readOnly={!!email}
142-
disabled={!!email}
133+
readOnly={!!searchEmail}
134+
disabled={!!searchEmail}
143135
className="bg-purple-400 border-purple-400 dark:bg-black dark:border-black"
144136
autoComplete="email"
145137
autoCapitalize="none"
@@ -197,12 +189,10 @@ export function SignUp() {
197189
</form>
198190
</Form>
199191
<div className="flex px-4 mt-4 underline place-content-between">
200-
<Link className="m-auto text-sm hover:text-blue-300" to="/sign-in">
192+
<Link className="m-auto text-sm hover:text-blue-300" to="/sign-in" search={{ me: email }}>
201193
Already have an account? Sign in instead.
202194
</Link>
203195
</div>
204196
</div>
205197
);
206198
}
207-
208-

src/features/auth/Verifying.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useForgotPasswordMutation } from '@/features/auth/hooks/useForgotPassword';
2+
import { Link, useSearch } from '@tanstack/react-router';
3+
import { MouseEvent, useCallback } from 'react';
4+
import { toast } from 'sonner';
5+
6+
export function Verifying() {
7+
const { email }: { email?: string; } = useSearch({ strict: false });
8+
9+
const { mutate: submitForgotPasswordData, isPending } = useForgotPasswordMutation();
10+
11+
const resendCode = useCallback((e: MouseEvent) => {
12+
e.preventDefault();
13+
if (email) {
14+
submitForgotPasswordData({ email }, {
15+
onSuccess: (message) => {
16+
toast.success('Code Sent', {
17+
description: `${message}`,
18+
action: {
19+
label: 'Dismiss',
20+
onClick: () => toast.dismiss(),
21+
},
22+
});
23+
},
24+
});
25+
}
26+
return false;
27+
}, []);
28+
29+
return (
30+
<div className="text-white w-lg flex flex-col gap-4">
31+
<h1 className="text-3xl font-light">
32+
<span
33+
aria-hidden="true"
34+
className="text-2xl animate-flower-dance mr-4"
35+
title="Loading"
36+
>🌼</span>
37+
Check your email!
38+
</h1>
39+
<p>Your account has been created! You should receive an email with a link to <strong>[Verify My Email]</strong>!
40+
</p>
41+
<p>From: <strong>[email protected]</strong></p>
42+
<p>To: <strong>{email || 'your email'}</strong></p>
43+
<p>Can you click it? Then we can carry on to more fun things!</p>
44+
45+
<div className="underline flex gap-4">
46+
<Link className="text-sm hover:text-blue-300" to="/sign-in" search={{ me: email }}>
47+
I did it, let me sign in!
48+
</Link>
49+
<Link className="text-sm opacity-50 hover:text-blue-300" to={undefined} onClick={resendCode} disabled={isPending}>
50+
Send me another code, please.
51+
</Link>
52+
</div>
53+
</div>
54+
);
55+
}

src/features/auth/routes.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { RestPassword as ResetPassword } from '@/features/auth/ResetPassword';
66
import { SignIn } from '@/features/auth/SignIn';
77
import { SignUp } from '@/features/auth/SignUp';
88
import { VerifyEmail } from '@/features/auth/VerifyEmail';
9+
import { Verifying } from '@/features/auth/Verifying';
910
import { OverallAppSignIn } from '@/lib/authStore';
1011
import { getDefaultSignedInCloudRouteForUser } from '@/lib/urls/getDefaultSignedInCloudRouteForUser';
1112
import { rootRoute } from '@/router/rootRoute';
@@ -60,14 +61,27 @@ const verifyEmailRoute = createRoute({
6061
component: VerifyEmail,
6162
});
6263

64+
const verifyingEmailRoute = createRoute({
65+
getParentRoute: () => authLayout,
66+
path: 'verifying',
67+
component: Verifying,
68+
});
69+
6370
const resetPasswordRoute = createRoute({
6471
getParentRoute: () => authLayout,
6572
path: 'reset-password',
6673
component: ResetPassword,
6774
});
6875

6976
export const authRouteTree =
70-
authLayout.addChildren([signInRoute, signUpRoute, forgotPasswordRoute, verifyEmailRoute, resetPasswordRoute]);
77+
authLayout.addChildren([
78+
signInRoute,
79+
signUpRoute,
80+
forgotPasswordRoute,
81+
verifyEmailRoute,
82+
verifyingEmailRoute,
83+
resetPasswordRoute,
84+
]);
7185

7286
export const localAuthRoutes = [
7387
localSignInRoute,

src/index.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,22 @@
229229
@apply h-full bg-black font-ubuntu text-white;
230230
}
231231
}
232+
233+
234+
/* Fun dancing flower loading animation */
235+
@keyframes flower-dance {
236+
0% { transform: rotate(0deg) translateY(0) scale(1); }
237+
20% { transform: rotate(-12deg) translateY(-3px) scale(1.06); }
238+
40% { transform: rotate(6deg) translateY(0) scale(1); }
239+
60% { transform: rotate(12deg) translateY(-3px) scale(1.06); }
240+
80% { transform: rotate(-6deg) translateY(0) scale(1); }
241+
100% { transform: rotate(0deg) translateY(0) scale(1); }
242+
}
243+
244+
@layer utilities {
245+
.animate-flower-dance {
246+
animation: flower-dance 1.15s ease-in-out infinite;
247+
transform-origin: bottom center;
248+
display: inline-block;
249+
}
250+
}

0 commit comments

Comments
 (0)