Skip to content

Commit

Permalink
Email verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Assios committed Aug 26, 2023
1 parent 7fcbb40 commit 817d75e
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 95 deletions.
6 changes: 5 additions & 1 deletion app/components/AuthProvider/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Achievement {
export interface AppUser {
uid: string;
email: string;
emailVerified: boolean;
username: string;
stats: PlayerStats;
lastUpdatedUsername: any;
Expand Down Expand Up @@ -60,7 +61,10 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({
if (firebaseUser) {
const appUser = await getUserFromFirestore(firebaseUser.uid);
if (appUser) {
setUser(appUser);
setUser({
...appUser,
emailVerified: firebaseUser.emailVerified,
});
setIsAuthenticated(true);
} else {
setUser(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";

const EmailVerificationReminder = () => {
return (
<div className="bg-yellow-500 text-black p-4 flex items-center justify-center flex-col">
{" "}
{/* Changed to 'flex-col' */}
<div className="flex items-center mb-2">
{" "}
{/* Enclosed svg and text in a div */}
<svg
xmlns="http://www.w3.org/2000/svg"
className="stroke-current shrink-0 h-6 w-6 mr-2"
fill="none"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<span>Please check your inbox and verify your email.</span>
</div>
<span className="text-xs">Once verified, please refresh this page.</span>
</div>
);
};

export default EmailVerificationReminder;
191 changes: 99 additions & 92 deletions app/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NavLink } from "@remix-run/react";
import { AuthContext } from "../AuthProvider/AuthProvider";
import { logOut } from "../../firebase/authUtils";
import { idToColor } from "~/utils/utils";
import EmailVerificationReminder from "../EmailVerificationReminder/EmailVerificationReminder";

export const Navbar = ({ setShowModal, setShowTutorial, showNavbarStats }) => {
const authContext = useContext(AuthContext);
Expand Down Expand Up @@ -82,110 +83,116 @@ export const Navbar = ({ setShowModal, setShowTutorial, showNavbarStats }) => {
) : null;

return (
<div className="navbar bg-primary text-primary-content">
<div className="navbar-start">
<button className="btn btn-ghost btn-circle" onClick={setShowTutorial}>
<div className="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 md:w-6 md:h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<NavLink
prefetch="intent"
to="games"
className="btn btn-ghost btn-circle"
>
<div className="indicator">
<>
{user && !user.emailVerified && <EmailVerificationReminder />}
<div className="navbar bg-primary text-primary-content">
<div className="navbar-start">
<button
className="btn btn-ghost btn-circle"
onClick={setShowTutorial}
>
<div className="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 md:w-6 md:h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<NavLink
prefetch="intent"
to="games"
className="btn btn-ghost btn-circle"
>
<div className="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 md:w-6 md:h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
</svg>
</div>
</NavLink>
</div>
<div className="navbar-center">
<NavLink
prefetch="intent"
to="/"
className="btn btn-ghost normal-case text-md md:text-lg lg:text-2xl"
>
Chessguessr
</NavLink>
</div>
<div className="navbar-end">
<label className="swap swap-rotate btn btn-ghost btn-circle">
<input
type="checkbox"
data-toggle-theme="corporate,lichess-dark"
data-act-class="ACTIVECLASS"
/>

<svg
className="swap-off fill-current w-6 h-6 md:w-8 md:h-8"
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 md:w-6 md:h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
</div>
</NavLink>
</div>
<div className="navbar-center">
<NavLink
prefetch="intent"
to="/"
className="btn btn-ghost normal-case text-md md:text-lg lg:text-2xl"
>
Chessguessr
</NavLink>
</div>
<div className="navbar-end">
<label className="swap swap-rotate btn btn-ghost btn-circle">
<input
type="checkbox"
data-toggle-theme="corporate,lichess-dark"
data-act-class="ACTIVECLASS"
/>

<svg
className="swap-off fill-current w-6 h-6 md:w-8 md:h-8"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>

<svg
className="swap-on fill-current w-8 h-8"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>

{showNavbarStats && (
<button className="btn btn-ghost btn-circle" onClick={setShowModal}>
<svg
className="swap-on fill-current w-8 h-8"
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 md:w-6 md:h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</button>
)}
</label>

{user ? (
userProfileDropdown
) : (
<NavLink prefetch="intent" to="/login" className="btn btn-ghost">
Login
</NavLink>
)}
{showNavbarStats && (
<button className="btn btn-ghost btn-circle" onClick={setShowModal}>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5 md:w-6 md:h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
</button>
)}

{user ? (
userProfileDropdown
) : (
<NavLink prefetch="intent" to="/login" className="btn btn-ghost">
Login
</NavLink>
)}
</div>
</div>
</div>
</>
);
};
5 changes: 5 additions & 0 deletions app/firebase/authUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,8 @@ export const logOut = async () => {
export const observeAuth = (callback: (user: User | null) => void) => {
return onAuthStateChanged(auth, callback);
};

export const isValidUsername = (username) => {
const usernameRegex = /^[a-zA-Z0-9]{1,16}$/;
return usernameRegex.test(username);
};
1 change: 1 addition & 0 deletions app/firebase/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export async function getUserFromFirestore(
return {
uid,
...data,
emailVerified: data.emailVerified || false,
};
} else {
return null;
Expand Down
8 changes: 6 additions & 2 deletions app/routes/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from "react";
import {
signUpWithEmailPasswordAndUsername,
observeAuth,
isValidUsername,
} from "../firebase/authUtils";

export default function SignUp() {
Expand Down Expand Up @@ -31,8 +32,10 @@ export default function SignUp() {
return;
}

if (!username) {
setMessage("Username is required.");
if (!username || !isValidUsername(username)) {
setMessage(
"Username should only contain letters and numbers and be at most 16 characters long."
);
return;
}

Expand Down Expand Up @@ -73,6 +76,7 @@ export default function SignUp() {
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
maxLength={16}
/>
</div>

Expand Down

1 comment on commit 817d75e

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for chessguessr ready!

✅ Preview
https://chessguessr-fm31am2tl-assios.vercel.app

Built with commit 817d75e.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.