;
+ }
+
return (
<>
diff --git a/src/api/index.ts b/src/api/index.ts
index 43737ac3..e8e9dc50 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -6,6 +6,7 @@ import partition from 'lodash/partition';
import * as Sentry from '@sentry/browser';
import { Application, Mentor, User } from '../types/models';
import { setVisitor } from '../utils/tawk';
+import Auth from '../utils/auth';
type RequestMethod = 'POST' | 'GET' | 'PUT' | 'DELETE';
type ErrorResponse = {
@@ -32,9 +33,9 @@ let currentUser: User | undefined;
export default class ApiService {
mentorsPromise: Promise | null = null
- auth: any
+ auth: Auth;
- constructor(auth: any) {
+ constructor(auth: Auth) {
this.auth = auth
}
@@ -114,6 +115,11 @@ export default class ApiService {
clearCurrentUser = () => {
currentUser = undefined;
+ ApiService.clearCurrentUserFromStorage();
+ }
+
+ // because we need to call it from authContext which doesn't have access to ApiService
+ static clearCurrentUserFromStorage = () => {
localStorage.removeItem(USER_LOCAL_KEY);
}
@@ -386,4 +392,8 @@ export default class ApiService {
return false;
}
+
+ resendVerificationEmail = async () => {
+ return this.makeApiCall(`${paths.USERS}/verify`, null, 'POST');
+ }
}
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index 1ce3fc24..3095a9a1 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -1,4 +1,4 @@
-import React, { useState, useContext } from 'react';
+import React, { useState } from 'react';
import styled from 'styled-components';
import OffCanvas from 'react-aria-offcanvas';
import Modal from '../Modal/Modal';
@@ -8,7 +8,7 @@ import Logo from '../Logo';
import Title from '../SiteTitle';
import Navigation from '../Navigation/Navigation';
import MobileNavigation from '../MobileNavigation/MobileNavigation';
-import { AuthContext } from '../../context/authContext/AuthContext';
+import { useAuth } from '../../context/authContext/AuthContext';
import { useDeviceType } from '../../hooks/useDeviceType';
function Header() {
@@ -19,7 +19,7 @@ function Header() {
});
const [isOpen, setIsOpen] = useState(false);
const { isDesktop } = useDeviceType();
- const auth = useContext(AuthContext);
+ const auth = useAuth();
const authenticated = auth.isAuthenticated();
const handleModal = ({ title, content, onClose }) => {
diff --git a/src/components/MemberArea/MemberArea.js b/src/components/MemberArea/MemberArea.js
index d3edb766..91155ee2 100644
--- a/src/components/MemberArea/MemberArea.js
+++ b/src/components/MemberArea/MemberArea.js
@@ -18,6 +18,7 @@ function MemberArea({ onOpenModal }) {
const [isMemberMenuOpen, setIsMemberMenuOpen] = useState(false);
const { currentUser, isMentor, isAdmin, isAuthenticated, logout } = useUser();
const api = useApi();
+ const user = useUser();
const auth = useAuth();
const openBecomeMentor = useCallback(
() => onOpenModal('Edit Your Profile', ),
@@ -51,7 +52,8 @@ function MemberArea({ onOpenModal }) {
- currentUser && setIsMemberMenuOpen(!isMemberMenuOpen)
+ (currentUser || user.isNotYetVerified) &&
+ setIsMemberMenuOpen(!isMemberMenuOpen)
}
>
{currentUser ? (
@@ -70,10 +72,12 @@ function MemberArea({ onOpenModal }) {
Open pending applications
)}
-
- Manage Account
-
- {!isMentor && (
+ {!user.isNotYetVerified && (
+
+ Manage Account
+
+ )}
+ {!isMentor && !user.isNotYetVerified && (
Become a mentor
diff --git a/src/components/Modal/Modal.js b/src/components/Modal/Modal.js
index 918c095a..976436e8 100644
--- a/src/components/Modal/Modal.js
+++ b/src/components/Modal/Modal.js
@@ -3,12 +3,12 @@ import classNames from 'classnames';
export default class Modal extends Component {
state = {
- isActive: false,
+ isActive: this.props.isActive ?? false,
};
handleOpen = (children) => {
this.setState({
- isActive: true,
+ isActive: !!children,
children,
});
};
diff --git a/src/components/layouts/App/App.js b/src/components/layouts/App/App.js
index bcaa4744..32c62dbd 100644
--- a/src/components/layouts/App/App.js
+++ b/src/components/layouts/App/App.js
@@ -1,4 +1,4 @@
-import { useEffect, useState } from 'react';
+import { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components/macro';
import { toast, ToastContainer } from 'react-toastify';
@@ -11,6 +11,8 @@ import { ActionsHandler } from './ActionsHandler';
import { desktop, mobile } from '../../../Me/styles/shared/devices';
import { Sidebar } from '../../Sidebar/Sidebar';
import { useMentors } from '../../../context/mentorsContext/MentorsContext';
+import { useUser } from '../../../context/userContext/UserContext';
+import { VerificationModal } from './VerificationModal';
const App = (props) => {
const { children } = props;
@@ -22,6 +24,23 @@ const App = (props) => {
onClose: null,
});
const { mentors } = useMentors();
+ const { emailVerifiedInfo } = useUser();
+ const closeModal = useCallback(() => setModal({}), []);
+
+ const showVerifyEmailModal = useCallback(() => {
+ setModal({
+ title: 'Verify your email',
+ content: (
+ {
+ toast.success('We just sent you the verification email');
+ closeModal();
+ }}
+ />
+ ),
+ onClose: closeModal,
+ });
+ }, [closeModal]);
useEffect(() => {
if (process.env.REACT_APP_MAINTENANCE_MESSAGE) {
@@ -38,6 +57,12 @@ const App = (props) => {
}
}, []);
+ useEffect(() => {
+ if (emailVerifiedInfo?.isVerified === false) {
+ showVerifyEmailModal(emailVerifiedInfo.email);
+ }
+ }, [emailVerifiedInfo, showVerifyEmailModal]);
+
useEffect(
() => setWindowTitle({ tag, country, name, language }),
[tag, country, name, language]
diff --git a/src/components/layouts/App/VerificationModal.tsx b/src/components/layouts/App/VerificationModal.tsx
new file mode 100644
index 00000000..7a480202
--- /dev/null
+++ b/src/components/layouts/App/VerificationModal.tsx
@@ -0,0 +1,65 @@
+import { useState } from 'react';
+import styled from 'styled-components/macro';
+import { useApi } from '../../../context/apiContext/ApiContext';
+import { useUser } from '../../../context/userContext/UserContext';
+import Button from '../../../Me/components/Button';
+import { maskEmail } from '../../../utils/maskSansitiveString';
+
+type VerificationModalProps = {
+ onSuccess: () => void;
+ email: string;
+};
+
+const ModalText = styled.p`
+ text-align: center;
+ font-size: 16px;
+ line-height: 1.5;
+`;
+
+export const VerificationModal = ({ onSuccess }: VerificationModalProps) => {
+ const [loading, setLoading] = useState(false);
+ const { emailVerifiedInfo } = useUser();
+ const api = useApi();
+
+ if (emailVerifiedInfo.isVerified === true) {
+ // eslint-disable-next-line no-console
+ console.warn('email is verified');
+ return;
+ }
+
+ const send = async () => {
+ setLoading(true);
+ try {
+ const result = await api.resendVerificationEmail();
+ if (result.success) {
+ onSuccess();
+ }
+ } catch {}
+ setLoading(false);
+ };
+
+ return (
+ <>
+
+ Psst, we believe that you are who you say you are.
+
+ Just to make sure, we need you to verify your email.
+
Recognize {maskEmail(emailVerifiedInfo.email)}?
+ {emailVerifiedInfo.isRegisteredRecently ? (
+ <>
+ This is the address we sent a verification email to.
+
+ Can't find it? Hit the button
+ >
+ ) : (
+ <>Hit the button to send a verification email right to your inbox>
+ )}
+
+
+
+
+ >
+ );
+};
diff --git a/src/context/apiContext/ApiContext.tsx b/src/context/apiContext/ApiContext.tsx
index fe26657c..a99f4731 100644
--- a/src/context/apiContext/ApiContext.tsx
+++ b/src/context/apiContext/ApiContext.tsx
@@ -5,13 +5,16 @@ import ApiService from '../../api';
export const ApiContext = createContext(null);
export const ApiProvider: FC = (props: any) => {
- const { children } = props
- const auth = useContext(AuthContext)
- const api = useMemo(() => new ApiService(auth), [auth]) ;
- return {children}
+ const { children } = props;
+ const auth = useContext(AuthContext);
+ const api = useMemo(() => new ApiService(auth), [auth]);
+ return {children};
};
export function useApi(): ApiService {
const api = useContext(ApiContext);
+ if (!api) {
+ throw new Error(`"useApi" has to be called inside ApiProvider`);
+ }
return api;
}
diff --git a/src/context/authContext/AuthContext.tsx b/src/context/authContext/AuthContext.tsx
index e09785ec..32f52bca 100644
--- a/src/context/authContext/AuthContext.tsx
+++ b/src/context/authContext/AuthContext.tsx
@@ -1,4 +1,6 @@
-import { createContext, useContext, FC, useEffect, useState } from 'react';
+import { NextRouter, useRouter } from 'next/router';
+import { createContext, useContext, FC, useEffect, useState, useCallback, useRef } from 'react';
+import { toast } from 'react-toastify';
import { isSsr } from '../../helpers/ssr';
import Auth from '../../utils/auth';
@@ -7,13 +9,46 @@ const auth = new Auth();
export const AuthProvider: FC = (props: any) => {
const { children } = props;
- const [isLoading, setIsLoading] = useState(!isSsr() /* ssr doesn't need loader */);
+ const router = useRouter();
+ const [isLoading, setIsLoading] = useState(
+ !isSsr() /* ssr doesn't need loader */
+ );
+
+ const handleVerificationRedirect = useCallback(() => {
+ const {is, success, message} = justVerifiedEmail(router);
+ if (is) {
+ const text = `Verification result: ${message}`;
+ if (success) {
+ toast.success(text);
+ } else {
+ toast.error(text);
+ }
+ router.push('/');
+ }
+ return is;
+ }, [router])
useEffect(() => {
- auth.renewSession().finally(() => {
- setIsLoading(false);
- });
- }, []);
+ const isJustVerified = handleVerificationRedirect();
+ if (isJustVerified) {
+ auth.forgetUser();
+ return;
+ }
+ auth
+ .renewSession()
+ .catch((e) => {
+ toast.error(
+ <>
+
Something went wrong, please login again
+
If the problem persists, please contact us.
+
Error: {typeof e === 'string' ? e : JSON.stringify(e)}