From 9506e5b43c29125e3f243f6a85674b5ce52c3c03 Mon Sep 17 00:00:00 2001 From: Lucid Kobold <72232219+LucidKobold@users.noreply.github.com> Date: Tue, 21 Jun 2022 16:16:17 -0500 Subject: [PATCH] Moved completed tutorial states to session and local storage. --- package.json | 2 +- src/app/store.ts | 6 +- src/components/calender/index.tsx | 6 +- src/components/tutorial/index.tsx | 39 +++++-- .../calender/{calender.ts => index.ts} | 0 src/features/tutorial/index.ts | 109 ++++++++++++++++++ src/pages/calendar/[...date].tsx | 8 +- src/pages/index.tsx | 45 +++++--- src/theme/layout/Header.tsx | 2 +- 9 files changed, 182 insertions(+), 35 deletions(-) rename src/features/calender/{calender.ts => index.ts} (100%) create mode 100644 src/features/tutorial/index.ts diff --git a/package.json b/package.json index 6671530..c3dc022 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "name": "lucid-creations-media-potty-chart", "homepage": "https://lucidcreations.media/introducing-code-name-potty-chart/", - "version": "v0.0.11-alpha", + "version": "0.0.11", "author": { "name": "Lucid Creations Media", "url": "https://lucidcreations.media", diff --git a/src/app/store.ts b/src/app/store.ts index db29048..0710a07 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,11 +1,13 @@ import { configureStore } from "@reduxjs/toolkit"; -import calenderReducer from "../features/calender/calender"; +import calenderReducer from "../features/calender"; import stickersReducer from "../features/calender/stickers"; +import tutorialReducer from "../features/tutorial"; export const store = configureStore({ reducer: { calender: calenderReducer, - stickers: stickersReducer + stickers: stickersReducer, + tutorial: tutorialReducer } }); diff --git a/src/components/calender/index.tsx b/src/components/calender/index.tsx index 6ee7457..e6d697b 100644 --- a/src/components/calender/index.tsx +++ b/src/components/calender/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react"; import { isSameDay, format } from "date-fns"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; -import { updateCurrDate, updateMonth } from "../../features/calender/calender"; +import { updateCurrDate, updateMonth } from "../../features/calender"; import CalenderNav from "./CalenderNav"; import Day from "./Day"; @@ -43,12 +43,12 @@ const Calender = ({ }, [dispatch, newDate]); useEffect(() => { - console.info("Check to update date."); + // console.info("Check to update date."); const currDateObj = new Date(currDate); if (!isSameDay(currDateObj, new Date())) { - console.info("Updated date."); + // console.info("Updated date."); dispatch(updateCurrDate()); } }, [currDate, dispatch]); diff --git a/src/components/tutorial/index.tsx b/src/components/tutorial/index.tsx index 9f538fd..67090c8 100644 --- a/src/components/tutorial/index.tsx +++ b/src/components/tutorial/index.tsx @@ -1,20 +1,39 @@ -import { Box, Button, Heading } from "@chakra-ui/react"; +import { Box, Button, Heading, HStack, VStack } from "@chakra-ui/react"; import React from "react"; interface TutorialProps { - setTutorialCookie: (bool: boolean) => void; + setTutorialComplete: () => void; + setTempTutorialComplete: () => void; } -const Tutorial = ({ setTutorialCookie }: TutorialProps): JSX.Element => { - const handleSetCookieButton = (): void => { - setTutorialCookie(true); - }; +const Tutorial = ({ + setTutorialComplete, + setTempTutorialComplete +}: TutorialProps): JSX.Element => { return ( - {"Tutorial Component"} - + + {"Tutorial Component"} + + + + + ); }; diff --git a/src/features/calender/calender.ts b/src/features/calender/index.ts similarity index 100% rename from src/features/calender/calender.ts rename to src/features/calender/index.ts diff --git a/src/features/tutorial/index.ts b/src/features/tutorial/index.ts new file mode 100644 index 0000000..08b68b7 --- /dev/null +++ b/src/features/tutorial/index.ts @@ -0,0 +1,109 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { addMonths } from "date-fns"; + +interface StorageState { + exp: string; + version: string; + completed: boolean; +} + +// * Storage Helpers * // + +const setTempStorage = (storageState: StorageState): void => { + sessionStorage.setItem("completedTutorial", JSON.stringify(storageState)); +}; + +const getTempStorage = (): StorageState | null => { + return JSON.parse(sessionStorage.getItem("completedTutorial")); +}; + +const clearTempStorage = (): void => { + sessionStorage.removeItem("completedTutorial"); +}; + +const setStorage = (storageState: StorageState): void => { + localStorage.setItem("completedTutorial", JSON.stringify(storageState)); +}; + +const getStorage = (): StorageState | null => { + return JSON.parse(localStorage.getItem("completedTutorial")); +}; + +const clearStorage = (): void => { + localStorage.removeItem("completedTutorial"); +}; + +interface TutorialSlice { + completedTutorial: boolean | null; + storageState: StorageState | null; +} + +const initialState: TutorialSlice = { + completedTutorial: null, + storageState: null +}; + +const tutorialSlice = createSlice({ + name: "Tutorial", + initialState, + reducers: { + // Set temp complete + setTempTutorialComplete(state: TutorialSlice) { + const exp: string = addMonths(new Date(), 1).toJSON(); + const version: string = process.env.NEXT_PUBLIC_APP_VERSION.split("-")[0]; + const storageState: StorageState = { + exp, + version, + completed: true + }; + + setTempStorage(storageState); + state.storageState = storageState; + state.completedTutorial = true; + }, + // Set completed (remember) + setTutorialCompleted(state: TutorialSlice) { + const exp: string = addMonths(new Date(), 1).toJSON(); + const version: string = process.env.NEXT_PUBLIC_APP_VERSION.split("-")[0]; + const storageState: StorageState = { + exp, + version, + completed: true + }; + + setStorage(storageState); + state.storageState = storageState; + state.completedTutorial = true; + }, + // Clear states and storages + clearTutorialCompleted(state: TutorialSlice) { + clearTempStorage(); + clearStorage(); + state.storageState = null; + state.completedTutorial = null; + }, + // Get and set states + getAndSetTutorial(state: TutorialSlice) { + console.log("get and set tutorial states"); + const temp = getTempStorage(); + const local = getStorage(); + + if (temp !== null || local !== null) { + state.storageState = temp !== null ? temp : local; + state.completedTutorial = temp !== null ? temp.completed : local.completed; + } + + if (temp === null && local === null) { + state.completedTutorial = false; + } + } + } +}); + +export const { + setTempTutorialComplete, + setTutorialCompleted, + clearTutorialCompleted, + getAndSetTutorial +} = tutorialSlice.actions; +export default tutorialSlice.reducer; diff --git a/src/pages/calendar/[...date].tsx b/src/pages/calendar/[...date].tsx index 7f99984..0f519e9 100644 --- a/src/pages/calendar/[...date].tsx +++ b/src/pages/calendar/[...date].tsx @@ -20,14 +20,16 @@ const DateRoute: React.FC = () => { const router = useRouter(); const { date: slug } = router.query; - const [date, setDate] = useState(null); + const [date, setDate] = useState(null); const [error, setError] = useState(false); // const dateRange = useRef(findValidDateRange()); // const validDateRange = Object.assign({}, dateRange.current); - const validateDateInput = (dateArr: number[]): UpdateCalendarProps => { + const validateDateInput = ( + dateArr: number[] + ): UpdateCalenderPropsDateLayout => { if (!(dateArr.length >= 2) && !(dateArr.length <= 3)) { return { year: 0, @@ -198,7 +200,7 @@ const DateRoute: React.FC = () => { ) : ( - + ); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 70f6319..37ec320 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,16 +1,27 @@ import React, { Fragment, useEffect, useRef, useState } from "react"; import { Box } from "@chakra-ui/react"; -import { addMonths, format } from "date-fns"; +import { format } from "date-fns"; import Calender from "../components/calender"; import Tutorial from "../components/tutorial"; import LoadingOverlay from "../components/loading/LoadingOverlay"; import { Provider } from "react-redux"; import { store } from "../app/store"; import { useAppDispatch, useAppSelector } from "../app/hooks"; -import { updateLoading } from "../features/calender/calender"; +import { updateLoading } from "../features/calender"; +import { + getAndSetTutorial, + setTempTutorialComplete, + setTutorialCompleted +} from "../features/tutorial"; const IndexPage = (): JSX.Element => { const isLoading = useAppSelector((state) => state.calender.isLoading); + const completedTutorial = useAppSelector( + (state) => state.tutorial.completedTutorial + ); + const tutorialCompletionInfo = useAppSelector( + (state) => state.tutorial.storageState + ); const dispatch = useAppDispatch(); const currDate = useRef({ @@ -19,25 +30,26 @@ const IndexPage = (): JSX.Element => { day: parseInt(format(new Date(), "d")) }); - const [completedTutorial, setCompletedTutorial] = useState( - null - ); - - const getTutorialCookie = (): boolean => { - return JSON.parse(localStorage.getItem("tutorialCompleted")) || false; + const handleTempTutorialCompleted = (): void => { + dispatch(setTempTutorialComplete()); }; - const setTutorialCookie = (bool: boolean): void => { - localStorage.setItem("tutorialCompleted", `${bool}`); - setCompletedTutorial(true); + const handleTutorialCompleted = (): void => { + dispatch(setTutorialCompleted()); }; useEffect(() => { + if (completedTutorial === null || tutorialCompletionInfo === null) { + dispatch(getAndSetTutorial()); + dispatch(updateLoading(false)); + } + if (completedTutorial !== null) { dispatch(updateLoading(false)); } - setCompletedTutorial(getTutorialCookie()); - }, [completedTutorial, dispatch]); + + console.info("use effect", completedTutorial, tutorialCompletionInfo); + }, [completedTutorial, dispatch, tutorialCompletionInfo]); return ( { minWidth="min-content" > - {completedTutorial === null ? ( + {isLoading === true ? ( @@ -57,7 +69,10 @@ const IndexPage = (): JSX.Element => { ) : completedTutorial ? ( ) : ( - + )} diff --git a/src/theme/layout/Header.tsx b/src/theme/layout/Header.tsx index 28bf10d..3165975 100644 --- a/src/theme/layout/Header.tsx +++ b/src/theme/layout/Header.tsx @@ -15,7 +15,7 @@ import appLogo from "../../../public/images/logo.svg"; const Header = (): JSX.Element => { const appName = "LCM Potty Chart"; - const appVersion = "v0.0.11-alpha"; + const appVersion = process.env.NEXT_PUBLIC_APP_VERSION || ""; // Add transparency while not at the top of the page. const [transparentNavbar, setTransparentNavbar] = useState(false);