Skip to content

Commit

Permalink
Save the code and add checkpoint so the users can start from where th…
Browse files Browse the repository at this point in the history
…ey left off (#102)

* Save the code and a checkpoint so the users can start from where they left off

* fixed required changes.

* fix: format code

* refactor: Add Nullish coalescing operator

* refactor: added global state management

* refactor: changed variable names

* refactor: fixed minor bug

* added continue button

* refactor

* rename class

* fix rename

* minor change

* format files

---------

Co-authored-by: JeelRajodiya <[email protected]>
  • Loading branch information
pavanydg and JeelRajodiya authored Nov 20, 2024
1 parent 14a864e commit 535d3b2
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 10 deletions.
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: {} });
},
}));

0 comments on commit 535d3b2

Please sign in to comment.