diff --git a/frontend/src/components/LandingNavbar.js b/frontend/src/components/LandingNavbar.js index 211b8dcf..45a11b39 100644 --- a/frontend/src/components/LandingNavbar.js +++ b/frontend/src/components/LandingNavbar.js @@ -40,7 +40,6 @@ const LandingNavbar = () => { py={1} flexBasis="200px" flexGrow={1} - flexShirnk={1} id="Logo Container" > diff --git a/frontend/src/components/Modal/index.js b/frontend/src/components/Modal/index.js index 7f4f4905..29794e25 100644 --- a/frontend/src/components/Modal/index.js +++ b/frontend/src/components/Modal/index.js @@ -6,7 +6,7 @@ import CustomIcon from "../CustomIcon"; import styles from "./styles"; const Modal = ({ children, onClose }) => ( - + e.stopPropagation()} css={styles.flex}> Value - {Object.keys(whales).map((whaleType) => { + {Object.keys(whales).map((whaleType, idx) => { return ( - <> + {whales[whaleType].statistic} - + ); })} diff --git a/frontend/src/core/hooks/useLogin.js b/frontend/src/core/hooks/useLogin.js index b410cc9a..df2a92e3 100644 --- a/frontend/src/core/hooks/useLogin.js +++ b/frontend/src/core/hooks/useLogin.js @@ -1,5 +1,7 @@ +import { useContext } from "react"; import { useMutation } from "react-query"; import { useToast, useUser, useInviteAccept } from "."; +import UIContext from "../providers/UIProvider/context"; import { AuthService } from "../services"; const LOGIN_TYPES = { @@ -7,6 +9,7 @@ const LOGIN_TYPES = { TOKEN: true, }; const useLogin = (loginType) => { + const { setLoggingIn } = useContext(UIContext); const { getUser } = useUser(); const toast = useToast(); const { inviteAccept } = useInviteAccept(); @@ -16,6 +19,9 @@ const useLogin = (loginType) => { error, data, } = useMutation(AuthService.login, { + onMutate: () => { + setLoggingIn(true); + }, onSuccess: (data) => { // Default value for loginType is LOGIN_TYPES.MANUAL if (!loginType) { @@ -37,6 +43,9 @@ const useLogin = (loginType) => { onError: (error) => { toast(error, "error"); }, + onSettled: () => { + setLoggingIn(false); + }, }); return { diff --git a/frontend/src/core/hooks/useLogout.js b/frontend/src/core/hooks/useLogout.js index c8be9a86..c8cd3e91 100644 --- a/frontend/src/core/hooks/useLogout.js +++ b/frontend/src/core/hooks/useLogout.js @@ -1,4 +1,4 @@ -import { useCallback, useContext } from "react"; +import { useContext } from "react"; import { useMutation, useQueryClient } from "react-query"; import { useUser, useRouter } from "."; import UIContext from "../providers/UIProvider/context"; @@ -7,21 +7,19 @@ import { AuthService } from "../services"; const useLogout = () => { const { setLoggingOut } = useContext(UIContext); const router = useRouter(); - const { mutate: revoke } = useMutation(AuthService.revoke, { + const cache = useQueryClient(); + const { mutate: logout } = useMutation(AuthService.revoke, { + onMutate: () => { + setLoggingOut(true); + }, onSuccess: () => { + router.push("/"); + setUser(null); localStorage.removeItem("MOONSTREAM_ACCESS_TOKEN"); cache.clear(); - setUser(null); - router.push("/"); }, }); const { setUser } = useUser(); - const cache = useQueryClient(); - - const logout = useCallback(() => { - setLoggingOut(true); - revoke(); - }, [revoke, setLoggingOut]); return { logout, diff --git a/frontend/src/core/hooks/useSignUp.js b/frontend/src/core/hooks/useSignUp.js index 6ae77f7e..c4dc12b5 100644 --- a/frontend/src/core/hooks/useSignUp.js +++ b/frontend/src/core/hooks/useSignUp.js @@ -21,6 +21,9 @@ const useSignUp = (source) => { data, isSuccess, } = useMutation(AuthService.register(), { + onMutate: () => { + ui.setLoggingIn(true); + }, onSuccess: (response) => { localStorage.setItem("MOONSTREAM_ACCESS_TOKEN", response.data.id); const invite_code = window.sessionStorage.getItem("invite_code"); @@ -35,13 +38,15 @@ const useSignUp = (source) => { }); } getUser(); - ui.setisOnboardingComplete(false); - ui.setOnboardingState({ welcome: 0, subscriptions: 0, stream: 0 }); + ui.setOnboardingComplete(false); router.push("/welcome", undefined, { shallow: false }); }, onError: (error) => { toast(error, "error"); }, + onSettled: () => { + ui.setLoggingIn(false); + }, }); return { diff --git a/frontend/src/core/providers/UIProvider/index.js b/frontend/src/core/providers/UIProvider/index.js index d4d73a58..2d1f91f3 100644 --- a/frontend/src/core/providers/UIProvider/index.js +++ b/frontend/src/core/providers/UIProvider/index.js @@ -1,4 +1,10 @@ -import React, { useState, useContext, useEffect, useCallback } from "react"; +import React, { + useState, + useContext, + useEffect, + useCallback, + useLayoutEffect, +} from "react"; import { useBreakpointValue } from "@chakra-ui/react"; import { useStorage, useQuery, useRouter } from "../../hooks"; import UIContext from "./context"; @@ -47,21 +53,25 @@ const UIProvider = ({ children }) => { // ******* APP state ******** const [isLoggedIn, setLoggedIn] = useState(user && user.username); const [isLoggingOut, setLoggingOut] = useState(false); + const [isLoggingIn, setLoggingIn] = useState(false); const [isAppReady, setAppReady] = useState(false); - const [isAppView, setAppView] = useState( - router.nextRouter.asPath.includes("/stream") || - router.nextRouter.asPath.includes("/account") || - router.nextRouter.asPath.includes("/subscriptions") || - router.nextRouter.asPath.includes("/analytics") || - router.nextRouter.asPath.includes("/welcome") - ); + const [isAppView, setAppView] = useState(false); - useEffect(() => { - if (isAppView && isAppReady && !user?.username && !isLoggingOut) { - // toggleModal("login"); + useLayoutEffect(() => { + if ( + isAppView && + isInit && + !user?.username && + !isLoggingOut && + !isLoggingIn && + !modal + ) { + toggleModal("login"); + } else if (user || isLoggingOut) { + toggleModal(false); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isAppView, isAppReady, user, isLoggingOut]); + }, [isAppView, isAppReady, user, isLoggingOut, modal]); useEffect(() => { if (isLoggingOut && !isAppView && user) { @@ -77,15 +87,16 @@ const UIProvider = ({ children }) => { } }, [user]); - useEffect(() => { - setAppView( - router.nextRouter.asPath.includes("/stream") || - router.nextRouter.asPath.includes("/account") || - router.nextRouter.asPath.includes("/subscriptions") || - router.nextRouter.asPath.includes("/analytics") || - router.nextRouter.asPath.includes("/welcome") - ); - }, [router.nextRouter.asPath, user]); + useLayoutEffect(() => { + if ( + isLoggingOut && + router.nextRouter.pathname === "/" && + !user && + !localStorage.getItem("MOONSTREAM_ACCESS_TOKEN") + ) { + setLoggingOut(false); + } + }, [isLoggingOut, router.nextRouter.pathname, user]); // *********** Sidebar states ********************** @@ -166,6 +177,8 @@ const UIProvider = ({ children }) => { const [onboardingState, setOnboardingState] = useState(false); const [onboardingStep, setOnboardingStep] = useState(); const [onboardingStateInit, setOnboardingStateInit] = useState(false); + const [onboardingRedirectCheckPassed, setOnboardingRedirectCheckPassed] = + useState(false); const setOnboardingComplete = useCallback( (newState) => { @@ -178,7 +191,7 @@ const UIProvider = ({ children }) => { //If onboarding state not exists - fetch it from backend //If it exists but init is not set - set init true //If it exists and is init -> post update to backend - if (!onboardingState && user) { + if (!onboardingState && user && !isLoggingOut) { const currentOnboardingState = async () => PreferencesService.getOnboardingState().then((response) => { return response.data; @@ -189,7 +202,7 @@ const UIProvider = ({ children }) => { }); } else if (user && onboardingState && !onboardingStateInit) { setOnboardingStateInit(true); - } else if (onboardingStateInit) { + } else if (user && onboardingStateInit) { PreferencesService.setOnboardingState(onboardingState); } // eslint-disable-next-line @@ -211,16 +224,16 @@ const UIProvider = ({ children }) => { useEffect(() => { //redirect to welcome page if yet not completed onboarding - if ( - isLoggedIn && - isAppReady && - onboardingState && - !onboardingState?.is_complete - ) { - router.replace("/welcome"); + if (isLoggedIn && onboardingState && !onboardingState?.is_complete) { + router.replace("/welcome", undefined, { shallow: true }); + } + if (isLoggedIn) { + setOnboardingRedirectCheckPassed(true); + } else { + setOnboardingRedirectCheckPassed(false); } // eslint-disable-next-line - }, [isLoggedIn, onboardingState?.is_complete, isAppReady]); + }, [isLoggedIn, onboardingState?.is_complete]); useEffect(() => { //This will set up onboarding complete once user finishes each view at least once @@ -242,7 +255,7 @@ const UIProvider = ({ children }) => { isAppReady && user && Number.isInteger(onboardingStep) && - onboardingState + onboardingState?.steps ) { setOnboardingState({ ...onboardingState, @@ -259,12 +272,26 @@ const UIProvider = ({ children }) => { // ******************************************************** useEffect(() => { - if (isInit && router.nextRouter.isReady && onboardingState) { + if ( + isInit && + router.nextRouter.isReady && + onboardingState && + !isLoggingOut && + !isLoggingIn && + onboardingRedirectCheckPassed + ) { setAppReady(true); } else { setAppReady(false); } - }, [isInit, router, onboardingState]); + }, [ + isInit, + router, + onboardingState, + isLoggingOut, + isLoggingIn, + onboardingRedirectCheckPassed, + ]); return ( { setOnboardingState, onboardingState, isLoggingOut, + isLoggingIn, + setLoggingIn, }} > {children} diff --git a/frontend/src/layouts/AppLayout.js b/frontend/src/layouts/AppLayout.js index c4bf88ba..f6d46b64 100644 --- a/frontend/src/layouts/AppLayout.js +++ b/frontend/src/layouts/AppLayout.js @@ -1,11 +1,19 @@ -import { Flex } from "@chakra-ui/react"; +import { Flex, Spinner, Box } from "@chakra-ui/react"; import { getLayout as getSiteLayout } from "./RootLayout"; -import React, { useContext } from "react"; +import React, { useContext, useEffect } from "react"; import UIContext from "../core/providers/UIProvider/context"; const AppLayout = ({ children }) => { const ui = useContext(UIContext); + useEffect(() => { + ui.setAppView(true); + return () => { + ui.setAppView(false); + }; + // eslint-disable-next-line + }, []); + return ( { w="100%" overflow="hidden" > + {(!ui.isAppReady || !ui.isLoggedIn) && ( + + )} + {(!ui.isAppReady || !ui.isLoggedIn) && ( + + )} + {ui.isAppReady && ui.isLoggedIn && children} ); diff --git a/frontend/src/layouts/RootLayout.js b/frontend/src/layouts/RootLayout.js index d45f4aa9..e1806376 100644 --- a/frontend/src/layouts/RootLayout.js +++ b/frontend/src/layouts/RootLayout.js @@ -1,32 +1,14 @@ import { CloseIcon } from "@chakra-ui/icons"; -import { - Flex, - Spinner, - Center, - Text, - Link, - IconButton, -} from "@chakra-ui/react"; -import React, { Suspense, useContext, useState, useEffect } from "react"; +import { Flex, Center, Text, Link, IconButton } from "@chakra-ui/react"; +import React, { Suspense, useContext, useState } from "react"; +import UIContext from "../core/providers/UIProvider/context"; const Sidebar = React.lazy(() => import("../components/Sidebar")); const Navbar = React.lazy(() => import("../components/Navbar")); -import UIContext from "../core/providers/UIProvider/context"; const RootLayout = (props) => { const ui = useContext(UIContext); - const [showSpinner, setSpinner] = useState(true); const [showBanner, setShowBanner] = useState(true); - useEffect(() => { - if (ui.isAppView && ui.isAppReady) { - setSpinner(false); - } else if (!ui.isAppView) { - setSpinner(false); - } else { - setSpinner(true); - } - }, [ui, setSpinner]); - return ( { )} - {!showSpinner && props.children} - {showSpinner && } + {props.children} );