Skip to content

Save the code and add checkpoint so the users can start from where they left off #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions app/components/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { OutputReducerAction } from "@/lib/reducers";
import { validateCode } from "@/lib/client-functions";
import FiChevronRight from "@/app/styles/icons/HiChevronRightGreen";
import { useRouter } from "next/navigation";
import { useEditorStore } from "@/lib/stores";
import { useUserSolutionStore, useEditorStore } from "@/lib/stores";
import { sendGAEvent } from "@next/third-parties/google";

export default function CodeEditor({
Expand All @@ -38,6 +38,7 @@ export default function CodeEditor({
const [monaco, setMonaco] = useState<any>(null);
const router = useRouter();
const editorStore = useEditorStore();
const userSolutionStore = useUserSolutionStore();

useEffect(() => {
if (monaco) {
Expand Down Expand Up @@ -76,6 +77,31 @@ export default function CodeEditor({
document.removeEventListener("keydown", handleKeyDown);
};
}, [codeString]);

useEffect(() => {
const savedCode = userSolutionStore.getSavedUserSolutionByLesson(
chapterIndex,
stepIndex,
);
if (savedCode && savedCode !== codeString) {
setCodeString(savedCode);
}
}, [chapterIndex, stepIndex]);

useEffect(() => {
userSolutionStore.saveUserSolutionForLesson(
chapterIndex,
stepIndex,
codeString,
);
}, [codeString, chapterIndex, stepIndex]);

useEffect(() => {
if (Object.keys(userSolutionStore.userSolutionsByLesson).length == 0) {
setCodeString(JSON.stringify(codeFile.code, null, 2));
}
}, [userSolutionStore]);

return (
<>
<div className={ctx(styles.codeEditor, GeistMono.className)}>
Expand All @@ -85,7 +111,7 @@ export default function CodeEditor({
theme={colorMode === "light" ? "light" : "my-theme"}
value={codeString}
height={"100%"}
onChange={(codeString) => setCodeString(codeString ? codeString : "")}
onChange={(codeString) => setCodeString(codeString ?? "")}
options={{ minimap: { enabled: false }, fontSize: 14 }}
onMount={(editor, monaco) => {
setMonaco(monaco);
Expand Down
12 changes: 12 additions & 0 deletions app/components/ContinueBtn/ContinueBtn.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.continueBtn {
display: flex;
gap: 5px;
}

.rightIcon {
display: flex;
justify-content: center;
align-items: center;
width: 10px;
height: 10px;
}
39 changes: 39 additions & 0 deletions app/components/ContinueBtn/ContinueBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import { useRouter } from "next/navigation";
import { getCheckPoint } from "@/lib/progressSaving";
import styles from "./ContinueBtn.module.css";
import RightArrow from "@/app/styles/icons/RightArrow";
import { Button } from "@chakra-ui/react";

export default function ContinueBtn() {
const router = useRouter();
const checkpoint = getCheckPoint();

const handleClick = () => {
const checkpoint = getCheckPoint();
if (checkpoint) {
router.push(`/${checkpoint}`);
}
};

return (
<>
{checkpoint && (
<Button
variant={"default"}
onClick={handleClick}
size={"sm"}
className={styles.continueBtn}
>
Continue
<div className={styles.rightIcon}>
<RightArrow />
</div>
</Button>
)}
</>
);

return null;
}
1 change: 1 addition & 0 deletions app/components/ContinueBtn/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as default } from "./ContinueBtn";
4 changes: 4 additions & 0 deletions app/components/NavBarMenus/NavBarMenus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import React, { useState } from "react";
import navBarStyles from "../NavBar/NavBar.module.css";
import { sendGAEvent } from "@next/third-parties/google";
import { useUserSolutionStore } from "@/lib/stores";

export default function NavBarMenu() {
const { colorMode } = useColorMode();
Expand All @@ -29,6 +30,8 @@ export default function NavBarMenu() {

const toast = useToast();

const userSolutionStore = useUserSolutionStore();

return (
<Menu closeOnSelect={false} gutter={4}>
<MenuButton
Expand Down Expand Up @@ -75,6 +78,7 @@ export default function NavBarMenu() {
mt={2}
onClick={() => {
localStorage.removeItem("progress");
userSolutionStore.clearAllCode();
setIsOpen(false);
toast({
title: "Progress Cleared",
Expand Down
4 changes: 2 additions & 2 deletions app/content/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import React from "react";
import styles from "./layout.module.css";
import NavBar from "@/app/components/NavBar";
import { contentManager } from "@/lib/contentManager";
import { usePathname } from "next/navigation";
import { setCheckpoint } from "@/lib/progressSaving";

export default function PageLayout({
children,
Expand All @@ -12,7 +12,7 @@ export default function PageLayout({
params: { markdownPath: string[] };
}) {
const urlPath = usePathname().replace("/content", "").substring(1);

setCheckpoint(`content/${urlPath}`);
return (
<div className={styles.wrapper}>
<NavBar urlPath={urlPath} />
Expand Down
17 changes: 12 additions & 5 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import styles from "./styles/page.module.css";
import cx from "classnames";
import { interFont, outfitFont } from "./styles/fonts";
import CompanyLogos from "./components/CommunityLinks/CommunityLinks";
import HomePageLinks from "./components/HomePageLinks/HomePageLinks";
import { Flex } from "@chakra-ui/react";

import CompanyLogos from "./components/CommunityLinks";
import HomePageLinks from "./components/HomePageLinks";
import dynamic from "next/dynamic";
const ContinueBtn = dynamic(() => import("./components/ContinueBtn"), {
ssr: false,
});
export default function Home() {
return (
<div className={cx(styles.main, outfitFont.className)}>
Expand All @@ -25,7 +27,12 @@ export default function Home() {
</div>
</div>
</div>
<HomePageLinks />
<div className={styles.homePageLinksWrapper}>
<HomePageLinks />
<div className={styles.continueBtnWrapper}>
<ContinueBtn />
</div>
</div>
</div>
<div className={styles.footer}>
<div className={cx(styles.footerText, interFont.className)}>
Expand Down
9 changes: 8 additions & 1 deletion app/styles/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@
gap: 0px;
justify-content: center;
}

.homePageLinksWrapper {
display: flex;
align-items: center;
}
.continueBtnWrapper {
position: absolute;
margin-left: 500px;
}
.footer {
display: flex;
flex-direction: column;
Expand Down
1 change: 1 addition & 0 deletions lib/contentVariables.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// variables to generate the outline.json and iterate through the content

export const contentFolderPath: string = "./content";

export const contentFolderName: string = contentFolderPath.replace("./", "");
export const indexFileName = "index.mdx";
export const instructionsFileName = "instructions.mdx";
Expand Down
17 changes: 17 additions & 0 deletions lib/progressSaving.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function setCheckpoint(path: string) {
if (typeof window === "undefined") return false;
const checkpoint = path;

localStorage.setItem("checkPoint", JSON.stringify(checkpoint));
}

export function getCheckPoint() {
if (typeof window === "undefined") return false;

const checkpoint = localStorage.getItem("checkPoint");

if (checkpoint) {
return JSON.parse(checkpoint);
}
return null;
}
54 changes: 54 additions & 0 deletions lib/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,57 @@ export const useEditorStore = create<Store>()((set) => ({
setEditor: (editor) => set({ editor }),
setMonaco: (monaco) => set({ monaco }),
}));

type UserSolutionsByLesson = {
[key: string]: string | null;
};

type UserSolutionStore = {
userSolutionsByLesson: UserSolutionsByLesson;
saveUserSolutionForLesson: (
chapter: number,
lesson: number,
code: string,
) => void;
getSavedUserSolutionByLesson: (
chapter: number,
lesson: number,
) => string | null;
clearAllCode: () => void;
};

export const useUserSolutionStore = create<UserSolutionStore>()((set, get) => ({
userSolutionsByLesson:
typeof window !== "undefined"
? JSON.parse(localStorage.getItem("codeData") ?? "{}")
: {},

saveUserSolutionForLesson: (
chapter: number,
lesson: number,
code: string,
) => {
const key = `${chapter}.${lesson}`;
set((state) => {
const NewUserSolutionsByLesson = {
...state.userSolutionsByLesson,
[key]: code,
};
localStorage.setItem(
"codeData",
JSON.stringify(NewUserSolutionsByLesson),
);
return { userSolutionsByLesson: NewUserSolutionsByLesson };
});
},

getSavedUserSolutionByLesson: (chapter: number, lesson: number) => {
const key = `${chapter}.${lesson}`;
return get().userSolutionsByLesson[key] ?? null;
},

clearAllCode: () => {
localStorage.removeItem("codeData");
set({ userSolutionsByLesson: {} });
},
}));