From 9f1dff77f8e83fc6b9e53478aed5b814d7992051 Mon Sep 17 00:00:00 2001 From: Namiiii-sys Date: Tue, 12 Aug 2025 14:35:13 +0530 Subject: [PATCH] Deep Linked the saved Bookmarks to the Actual location --- .../app/[year]/[branch]/[semester]/page.tsx | 246 ++++++++++++++---- notes-aid/src/app/bookmarks/page.tsx | 19 +- notes-aid/src/components/BookmarkButton.tsx | 3 + notes-aid/src/components/ModuleCard.tsx | 22 +- notes-aid/src/components/Navbar.tsx | 2 + notes-aid/src/components/TopicList.tsx | 59 ++++- notes-aid/src/components/Video.tsx | 32 +++ notes-aid/src/components/VideoAccordion.tsx | 31 ++- 8 files changed, 334 insertions(+), 80 deletions(-) diff --git a/notes-aid/src/app/[year]/[branch]/[semester]/page.tsx b/notes-aid/src/app/[year]/[branch]/[semester]/page.tsx index a186fb2..f188eed 100644 --- a/notes-aid/src/app/[year]/[branch]/[semester]/page.tsx +++ b/notes-aid/src/app/[year]/[branch]/[semester]/page.tsx @@ -1,6 +1,6 @@ "use client"; import React, { useState, useEffect, useRef } from "react"; -import { useParams } from "next/navigation"; +import { useParams, useSearchParams } from "next/navigation"; // import ModuleCard from "../components/ModuleCard"; // import TopicList from "../components/TopicList"; // import Navbar from "../components/Navbar"; @@ -87,12 +87,35 @@ const EngineeringCurriculum: React.FC = () => { const typedNotesData = NotesData as NotesDataType; const subjects = slug && typedNotesData[slug]?.[branch]?.[sem]; - // console.log(subjects) const pyq = (pyqLinks as PyqLinks)[slug] || []; - // console.log(pyq) - // const subjects = NotesData.fy.comps.oddSem; + + + const searchParams = useSearchParams(); + const bookmarkId = searchParams.get("bookmarkId"); + +useEffect(() => { + if (!bookmarkId) return; + const [subjectFromBookmark] = bookmarkId.split("-module-"); + + if (subjects && subjects[subjectFromBookmark]) { + setSelectedSubject(subjectFromBookmark); + } + + setTimeout(() => { + const el = document.getElementById(bookmarkId); + if (el) { + el.scrollIntoView({ behavior: "smooth", block: "center" }); + el.classList.add("ring-4", "ring-blue-500"); + + setTimeout(() => { + el.classList.remove("ring-4", "ring-blue-500"); + }, 2000); + } + }, 300); +}, [bookmarkId, subjects]); + const isMountedRef = useRef(isMounted); useEffect(() => { @@ -107,44 +130,175 @@ const EngineeringCurriculum: React.FC = () => { }, [subjects]); - const initialSubject = subjects ? Object.keys(subjects)[0] : ""; - const [selectedSubject, setSelectedSubject] = useState(initialSubject); + const [selectedSubject, setSelectedSubject] = useState(""); const [selectedModule, setSelectedModule] = useState(1); + const [isInitialized, setIsInitialized] = useState(false); - useEffect(() => { - const params = new URLSearchParams(window.location.search); - const fromBookmark = params.get('fromBookmark'); - - if (fromBookmark) { - try { - const bookmarkState = JSON.parse(fromBookmark); - if (bookmarkState.selectedSubject && bookmarkState.selectedModule) { - setSelectedSubject(bookmarkState.selectedSubject); - setSelectedModule(bookmarkState.selectedModule); + if (!subjects || !isMounted || isInitialized) return; + + const subjectParam = searchParams.get("subject"); + const moduleParam = searchParams.get("module"); + const fromBookmark = searchParams.get("fromBookmark"); + const bookmarkId = searchParams.get("bookmarkId"); + + console.log("Initializing after mount with:", { + subjectParam, + moduleParam, + fromBookmark, + bookmarkId, + subjects: Object.keys(subjects) + }); + + let targetSubject = ""; + let targetModule = 1; + + if (bookmarkId) { + console.log("Processing bookmarkId:", bookmarkId); + + if (bookmarkId.includes('-module-')) { + const [subjectName, moduleInfo] = bookmarkId.split('-module-'); + console.log("New format - Subject name:", subjectName, "Module info:", moduleInfo); + + const subjectKey = Object.keys(subjects).find(key => + subjects[key].name === subjectName || + subjects[key].name === decodeURIComponent(subjectName) || + key === subjectName + ); + + console.log("Found subject key:", subjectKey); + + if (subjectKey && subjects[subjectKey]) { + targetSubject = subjectKey; + + const moduleNum = parseInt(moduleInfo); + if (!isNaN(moduleNum)) { + const availableModules = Object.keys(subjects[subjectKey].modules); + if (availableModules.includes(moduleNum.toString())) { + targetModule = moduleNum; + console.log("New format parsing successful:", { targetSubject, targetModule }); + } else { + targetModule = parseInt(availableModules[0]) || 1; + console.log("Module not found, using first:", targetModule); + } + } + } else { + console.log("Subject not found for name:", subjectName); + console.log("Available subjects:", Object.entries(subjects).map(([key, subject]) => ({ key, name: subject.name }))); + } + } else { + const parts = bookmarkId.split('-'); + console.log("Old format - Bookmark parts:", parts); + + if (parts.length >= 3 && parts[0] === 'subject') { + const subjectKey = parts[1]; + console.log("Extracted subject key:", subjectKey); + + const modulePart = parts.find(part => part.startsWith('module')); + if (modulePart) { + const moduleNum = parseInt(modulePart.replace('module', '')); + console.log("Extracted module number:", moduleNum); + + if (subjects[subjectKey]) { + targetSubject = subjectKey; + + const availableModules = Object.keys(subjects[subjectKey].modules); + if (availableModules.includes(moduleNum.toString())) { + targetModule = moduleNum; + console.log("Old format parsing successful:", { targetSubject, targetModule }); + } else { + targetModule = parseInt(availableModules[0]) || 1; + console.log("Module not found, using first:", targetModule); + } + } else { + console.log("Subject not found:", subjectKey, "Available:", Object.keys(subjects)); + } + } + } } - } catch (e) { - console.error('Error parsing bookmark state', e); } - } - }, []); - useEffect(() => { - if (subjects && selectedSubject) { - const params = new URLSearchParams(window.location.search); - const moduleParam = params.get("module"); - - if (moduleParam) { - setSelectedModule(parseInt(moduleParam)); - } else { - const firstModuleKey = Object.keys( - subjects[selectedSubject]?.modules || {} - )[0]; - setSelectedModule(firstModuleKey ? parseInt(firstModuleKey) : 1); + if (subjectParam && subjects[subjectParam]) { + targetSubject = subjectParam; + console.log("Overriding with URL subject param:", targetSubject); + + if (moduleParam) { + const moduleNum = parseInt(moduleParam); + const availableModules = Object.keys(subjects[subjectParam].modules); + if (availableModules.includes(moduleNum.toString())) { + targetModule = moduleNum; + console.log("Overriding with URL module param:", targetModule); + } + } + } + + if (!targetSubject && fromBookmark) { + try { + const bookmarkState = JSON.parse(fromBookmark); + if (bookmarkState.selectedSubject && bookmarkState.selectedModule && subjects[bookmarkState.selectedSubject]) { + targetSubject = bookmarkState.selectedSubject; + targetModule = bookmarkState.selectedModule; + console.log("Using legacy bookmark:", { targetSubject, targetModule }); + } + } catch (e) { + console.error("Error parsing bookmark state", e); + } + } + + if (!targetSubject) { + targetSubject = Object.keys(subjects)[0]; + const firstModuleKey = Object.keys(subjects[targetSubject]?.modules || {})[0]; + targetModule = firstModuleKey ? parseInt(firstModuleKey) : 1; + console.log("Using defaults:", { targetSubject, targetModule }); } - } - }, [selectedSubject, subjects]); + console.log("Final initialization:", { targetSubject, targetModule }); + + // Only set state if values are different or if this is first initialization + if (!isInitialized || targetSubject !== selectedSubject || targetModule !== selectedModule) { + console.log("Setting state:", { from: { selectedSubject, selectedModule }, to: { targetSubject, targetModule } }); + setSelectedSubject(targetSubject); + setSelectedModule(targetModule); + } + setIsInitialized(true); + + }, [subjects, searchParams, isMounted, isInitialized, selectedSubject, selectedModule]); + + // Handler for subject selection + const handleSubjectSelect = (key: string) => { + console.log("Subject selected:", key); + setSelectedSubject(key); + let firstModuleKey: string | undefined; + if (subjects && typeof subjects === "object" && key in subjects) { + firstModuleKey = Object.keys((subjects as Subjects)[key]?.modules || {})[0]; + } + const newModule = firstModuleKey ? parseInt(firstModuleKey) : 1; + console.log("Setting module to:", newModule); + setSelectedModule(newModule); + + // Update URL reflect current state + const params = new URLSearchParams(searchParams.toString()); + params.set('subject', key); + params.set('module', newModule.toString()); + + const newUrl = `${window.location.pathname}?${params.toString()}`; + console.log("Updating URL to:", newUrl); + window.history.pushState({}, '', newUrl); + }; + + // Handler for module selection + const handleModuleSelect = (moduleNum: number) => { + console.log("Module selected:", moduleNum); + setSelectedModule(moduleNum); + + const params = new URLSearchParams(searchParams.toString()); + params.set('subject', selectedSubject); + params.set('module', moduleNum.toString()); + + const newUrl = `${window.location.pathname}?${params.toString()}`; + console.log("Updating URL to:", newUrl); + window.history.pushState({}, '', newUrl); + }; const { progressData, updateVideoProgress, resetProgress } = useProgress(selectedSubject); @@ -211,16 +365,7 @@ const EngineeringCurriculum: React.FC = () => { return (
{ - setSelectedSubject(key) - const firstModuleKey = Object.keys( - subjects[key]?.modules || {} - )[0] - // console.log(key, firstModuleKey) - setSelectedModule( - firstModuleKey ? parseInt(firstModuleKey) : 1 - ) - }} + onClick={() => handleSubjectSelect(key)} className={`p-4 rounded-4xl cursor-pointer transition-all flex-1 max-w-[120px] sm:max-w-[150px] md:max-w-none text-center ${ selectedSubject === key @@ -301,14 +446,15 @@ const EngineeringCurriculum: React.FC = () => { subjects[selectedSubject].modules[moduley].topics.length } isActive={selectedModule === moduley} - onClick={() => setSelectedModule(moduley)} + onClick={() => handleModuleSelect(moduley)} numberOfVideosCompleted={ progressData.moduleProgress[moduley] || 0 } numberOfVideos={numberVideoInModule(moduley)} currentSubject={selectedSubject} - - + year={slug} + branch={branch} + semester={sem} /> ); } @@ -344,6 +490,10 @@ const EngineeringCurriculum: React.FC = () => { // moduleKey={`${selectedSubject}-module${selectedModule}`} moduleKey={`${selectedModule}`} subjectName={selectedSubject} + year={slug} + branch={branch} + semester={sem} + bookmarkId={bookmarkId} />
@@ -389,4 +539,4 @@ const EngineeringCurriculum: React.FC = () => { ); }; -export default EngineeringCurriculum; +export default EngineeringCurriculum; \ No newline at end of file diff --git a/notes-aid/src/app/bookmarks/page.tsx b/notes-aid/src/app/bookmarks/page.tsx index 8ff8dfc..b133eae 100644 --- a/notes-aid/src/app/bookmarks/page.tsx +++ b/notes-aid/src/app/bookmarks/page.tsx @@ -10,6 +10,9 @@ interface BookmarkItem { url?: string; module?: number; topic?: string; + year?: string; + branch?: string; + semester?: string; } type BookmarkType = 'modules' | 'topics' | 'videos'; @@ -35,10 +38,13 @@ export default function BookmarksPage() { }; const filteredBookmarks = bookmarks.filter(bookmark => { - if (activeTab === 'modules') return !bookmark.id.includes('topic') && !bookmark.id.includes('video'); - if (activeTab === 'topics') return bookmark.id.includes('topic'); - if (activeTab === 'videos') return bookmark.id.includes('video'); - return true; + if (activeTab === 'modules') + return !bookmark.id.includes('topic') && !bookmark.id.includes('video'); + if (activeTab === 'topics') + return bookmark.id.includes('topic') && !bookmark.id.includes('video'); + if (activeTab === 'videos') + return bookmark.id.includes('video'); + return true; }); return ( @@ -79,7 +85,10 @@ export default function BookmarksPage() { >
- +

{item.title}

diff --git a/notes-aid/src/components/BookmarkButton.tsx b/notes-aid/src/components/BookmarkButton.tsx index 2394dcd..5458958 100644 --- a/notes-aid/src/components/BookmarkButton.tsx +++ b/notes-aid/src/components/BookmarkButton.tsx @@ -8,6 +8,9 @@ interface BookmarkItem { subject: string; module?: number; topics?: string; + year?: string; + branch?: string; + semester?: string; url?: string; state?: { selectedsubject: string; diff --git a/notes-aid/src/components/ModuleCard.tsx b/notes-aid/src/components/ModuleCard.tsx index ee669c6..64277a5 100644 --- a/notes-aid/src/components/ModuleCard.tsx +++ b/notes-aid/src/components/ModuleCard.tsx @@ -12,7 +12,10 @@ interface ModuleCardProps { onClick: () => void; numberOfVideos: number; numberOfVideosCompleted: number; - currentSubject: string; + currentSubject: string; + year: string; + branch: string; + semester: string; } const ModuleCard: React.FC = ({ @@ -23,7 +26,10 @@ const ModuleCard: React.FC = ({ onClick, numberOfVideos, numberOfVideosCompleted, - currentSubject + currentSubject, + year, + branch, + semester }) => { // const total = 100; // const [done, setdone] = useState(20); @@ -55,6 +61,7 @@ const ModuleCard: React.FC = ({ return ( <>
= ({ subject: subjectName, type: 'module', module: module, - state: { - selectedsubject: currentSubject, - selectedmodule: module - } + year, + branch, + semester, + state: { + selectedsubject: currentSubject, + selectedmodule: module + } }} /> {
+
+
diff --git a/notes-aid/src/components/TopicList.tsx b/notes-aid/src/components/TopicList.tsx index 795d4c6..0b429b7 100644 --- a/notes-aid/src/components/TopicList.tsx +++ b/notes-aid/src/components/TopicList.tsx @@ -1,8 +1,10 @@ +"use client"; import React, { useEffect, useState } from "react"; import { BookOpen, ChevronDown } from "lucide-react"; import VideoAccordion from "./VideoAccordion"; import ProgressBar from "./ProgressBar"; import { BookmarkButton } from "./BookmarkButton"; + interface Topic { title: string; description: string; @@ -32,6 +34,10 @@ interface TopicListProps { ) => void; moduleKey: string; subjectName: string; + year: string; + branch: string; + semester: string; + bookmarkId?: string | null; } const TopicList: React.FC = ({ @@ -41,21 +47,54 @@ const TopicList: React.FC = ({ updateVideoProgress, moduleKey, subjectName, + year, + branch, + semester, + bookmarkId }) => { const [openTopicIndex, setOpenTopicIndex] = useState(null); - // const [completedItems, setCompletedItems] = useState>({}); const toggleTopic = (index: number) => { setOpenTopicIndex(openTopicIndex === index ? null : index); - // setDone((x)=>x+10); }; - // console.log("Module Key is: "+moduleKey) - useEffect(() => { setOpenTopicIndex(null); }, [topics]); + // Auto expanding th topic if it matches bookmarkId + useEffect(() => { + if (!bookmarkId) return; + + const topicPrefix = `subject-${subjectName}-module${moduleKey}-topic`; + if (bookmarkId.startsWith(topicPrefix)) { + const topicTitle = bookmarkId.replace(topicPrefix, ''); + const topicIndex = topics.findIndex(topic => + topic.title.replace(/\s/g, '') === topicTitle + ); + if (topicIndex !== -1) { + setOpenTopicIndex(topicIndex); + } + } + }, [bookmarkId, topics, subjectName, moduleKey]); + + useEffect(() => { + if (!bookmarkId) return; + + // Checking if this bookmark is for a video within any topic + const videoPrefix = `subject-${subjectName}-module${moduleKey}-topic`; + if (bookmarkId.includes('-video') && bookmarkId.startsWith(videoPrefix)) { + + const topicId = bookmarkId.split('-video')[0].replace(videoPrefix, ''); + const topicIndex = topics.findIndex(topic => + topic.title.replace(/\s/g, '') === topicId + ); + if (topicIndex !== -1 && openTopicIndex !== topicIndex) { + setOpenTopicIndex(topicIndex); + } + } +}, [openTopicIndex,bookmarkId, topics, subjectName, moduleKey]); + if (!topics || topics.length === 0) { return (
@@ -65,8 +104,6 @@ const TopicList: React.FC = ({
); } - // const total = 100; - // const [done, setDone] = useState(70); return (
@@ -127,12 +164,12 @@ const TopicList: React.FC = ({ } const completedTopics = progressData.topicProgress[topicKey] || 0; - console.log(completedTopics); return (
= ({ id: `subject-${subjectName}-module${moduleKey}-topic${topic.title.replace(/\s/g, "")}`, title: topic.title, type: 'topic', - subject: subjectName + subject: subjectName, + year, + branch, + semester }} /> = ({ moduleKey={moduleKey} updateVideoProgress={updateVideoProgress} subjectName={subjectName} + bookmarkId={bookmarkId} />
) : ( @@ -232,4 +273,4 @@ const TopicList: React.FC = ({ ); }; -export default TopicList; +export default TopicList; \ No newline at end of file diff --git a/notes-aid/src/components/Video.tsx b/notes-aid/src/components/Video.tsx index 69f6f35..ab4bb90 100644 --- a/notes-aid/src/components/Video.tsx +++ b/notes-aid/src/components/Video.tsx @@ -3,6 +3,7 @@ import React from "react"; import { RefObject, Dispatch, SetStateAction } from "react"; import { CheckSquare, Square, ChevronDown } from "lucide-react"; import { BookmarkButton } from "./BookmarkButton"; +import { useEffect } from "react"; interface VideoProps { checked: boolean; @@ -21,6 +22,10 @@ interface VideoProps { moduleKey: string; topicKey: string; subjectName: string; + year: string; + branch: string; + semester: string; + bookmarkId?: string | null; } function Video({ @@ -34,6 +39,10 @@ function Video({ moduleKey, topicKey, subjectName, + year, + branch, + semester, + bookmarkId }: VideoProps) { // console.log(videoKey) @@ -48,6 +57,24 @@ function Video({ const key = `${subjectName}-module${moduleKey}-topic${topicKey}-video${videoKey}`; const isCompleted = progressData.completeVideos[key] === true; + useEffect(() => { + if (!bookmarkId) return; + + const videoId = `subject-${subjectName}-module${moduleKey}-topic${topicKey}-video${videoKey}`; + if (bookmarkId === videoId && openVideoIndex !== index) { + toggleVideo(index); + } + }, [ + bookmarkId, + index, + moduleKey, + openVideoIndex, + subjectName, + toggleVideo, + topicKey, + videoKey + ]); + return ( <>
@@ -82,6 +109,11 @@ function Video({ module: Number(moduleKey), topics: topicKey, type: 'video', + year, + branch, + semester, + + }} /> void; - subjectName:string; -}> = ({ videos,topicKey,updateVideoProgress,moduleKey,subjectName }) => { + subjectName: string; + bookmarkId?: string | null; +}> = ({ videos, topicKey, updateVideoProgress, moduleKey, subjectName, bookmarkId }) => { + const { year, branch, semester } = useParams<{ year: string; branch: string; semester: string }>(); + const [openVideoIndex, setOpenVideoIndex] = useState(null); const videoRefs = useRef<(HTMLIFrameElement | null)[]>([]); @@ -27,22 +31,21 @@ const VideoAccordion: React.FC<{ setChecked(videos.map(() => false)); }, [videos]); - // console.log(topicKey) return (
- {videos.map((video, index) => { - - return(
( +
) - })} +
+ ))}
); };