diff --git a/client/package-lock.json b/client/package-lock.json
index 15e7897..61de325 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -75,6 +75,7 @@
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6",
@@ -617,6 +618,7 @@
"integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==",
"devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/react": "*"
}
@@ -735,6 +737,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001400",
"electron-to-chromium": "^1.4.251",
@@ -1321,7 +1324,8 @@
"version": "4.30.4",
"resolved": "https://registry.npmjs.org/filepond/-/filepond-4.30.4.tgz",
"integrity": "sha512-FCwsMvG9iiEs6uobdDrTaKsCgsqys0NuLgPPD8n37AYVYBiiDkrPkk9MSIU5rT2FahYcL1bScYI9huIPtlzqyA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/fill-range": {
"version": "7.0.1",
@@ -1770,6 +1774,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -1795,6 +1800,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
@@ -1952,6 +1958,7 @@
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
"integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.9.2"
}
@@ -2007,6 +2014,7 @@
"resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
"integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -2180,6 +2188,7 @@
"integrity": "sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.15.9",
"postcss": "^8.4.18",
@@ -2256,6 +2265,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
"dev": true,
+ "peer": true,
"requires": {
"@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6",
@@ -2622,6 +2632,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz",
"integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==",
"devOptional": true,
+ "peer": true,
"requires": {
"@types/react": "*"
}
@@ -2702,6 +2713,7 @@
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
"integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
"dev": true,
+ "peer": true,
"requires": {
"caniuse-lite": "^1.0.30001400",
"electron-to-chromium": "^1.4.251",
@@ -3008,7 +3020,8 @@
"filepond": {
"version": "4.30.4",
"resolved": "https://registry.npmjs.org/filepond/-/filepond-4.30.4.tgz",
- "integrity": "sha512-FCwsMvG9iiEs6uobdDrTaKsCgsqys0NuLgPPD8n37AYVYBiiDkrPkk9MSIU5rT2FahYcL1bScYI9huIPtlzqyA=="
+ "integrity": "sha512-FCwsMvG9iiEs6uobdDrTaKsCgsqys0NuLgPPD8n37AYVYBiiDkrPkk9MSIU5rT2FahYcL1bScYI9huIPtlzqyA==",
+ "peer": true
},
"fill-range": {
"version": "7.0.1",
@@ -3299,6 +3312,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "peer": true,
"requires": {
"loose-envify": "^1.1.0"
}
@@ -3316,6 +3330,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+ "peer": true,
"requires": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
@@ -3409,6 +3424,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
"integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
+ "peer": true,
"requires": {
"@babel/runtime": "^7.9.2"
}
@@ -3447,6 +3463,7 @@
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
"integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
+ "peer": true,
"requires": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -3556,6 +3573,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.4.tgz",
"integrity": "sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==",
"dev": true,
+ "peer": true,
"requires": {
"esbuild": "^0.15.9",
"fsevents": "~2.3.2",
diff --git a/client/src/App.jsx b/client/src/App.jsx
index 166b122..aa1930f 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -85,11 +85,9 @@ const App = () => {
} path="dashboard" exact />
} path="profile" exact />
- } path="browse">
- } path=":code">
- } path=":folderId" />
-
-
+ } path="browse" />
+ } path="browse/:code" />
+ } path="browse/:code/:folderId" />
} path="/" />
} path="*" />
diff --git a/client/src/screens/browse/components/browsefolder/index.jsx b/client/src/screens/browse/components/browsefolder/index.jsx
index 5891440..aac9f54 100644
--- a/client/src/screens/browse/components/browsefolder/index.jsx
+++ b/client/src/screens/browse/components/browsefolder/index.jsx
@@ -9,6 +9,8 @@ import { ConfirmDialog } from "./confirmDialog";
import { FolderRename } from "./folderRename.jsx";
import { getSubtreeFileCount } from "../../../../utils/folderUtils";
+import { useNavigate } from "react-router-dom";
+
const BrowseFolder = ({
name,
subject,
@@ -17,6 +19,7 @@ const BrowseFolder = ({
isMobileView = false,
}) => {
const dispatch = useDispatch();
+ const navigate = useNavigate();
const currentFolder = useSelector((state) => state.fileBrowser.currentFolder);
const isBR = useSelector((state) => state.user.user.isBR);
const [showConfirm, setShowConfirm] = useState(false);
@@ -58,6 +61,9 @@ const BrowseFolder = ({
dispatch(PushFolderHistory(currentFolder));
}
dispatch(ChangeFolder(folderData));
+ if (courseCode && folderData?._id) {
+ navigate(`/browse/${courseCode}/${folderData._id}`);
+ }
};
const handleDelete = async (e) => {
diff --git a/client/src/screens/browse/components/collapsible/components/folder/index.jsx b/client/src/screens/browse/components/collapsible/components/folder/index.jsx
index 706797e..851f531 100644
--- a/client/src/screens/browse/components/collapsible/components/folder/index.jsx
+++ b/client/src/screens/browse/components/collapsible/components/folder/index.jsx
@@ -9,8 +9,11 @@ import { ChangeFolder } from "../../../../../../actions/filebrowser_actions";
import { useEffect } from "react";
import { getSubtreeFileCount } from "../../../../../../utils/folderUtils";
+import { useNavigate } from "react-router-dom";
+
const Folder = ({ folder, state }) => {
const dispatch = useDispatch();
+ const navigate = useNavigate();
const _state = useSelector((state) => state.fileBrowser);
const [open, setOpen] = useState(state ? state : false);
const fileCount = getSubtreeFileCount(folder);
@@ -23,6 +26,9 @@ const Folder = ({ folder, state }) => {
const onClick = (folderData) => {
dispatch(ChangeFolder(folderData));
setOpen(true);
+ if (_state.currentCourseCode && folderData?._id) {
+ navigate(`/browse/${_state.currentCourseCode}/${folderData._id}`);
+ }
};
useEffect(() => {
diff --git a/client/src/screens/browse/components/collapsible/index.jsx b/client/src/screens/browse/components/collapsible/index.jsx
index 8a8a898..dc77e7b 100644
--- a/client/src/screens/browse/components/collapsible/index.jsx
+++ b/client/src/screens/browse/components/collapsible/index.jsx
@@ -36,6 +36,7 @@ const Collapsible = ({ course, color, state = false }) => {
const allCourseData = useSelector((state) => state.fileBrowser.allCourseData);
const dispatch = useDispatch();
const navigate = useNavigate();
+ const currentFolder = useSelector((state) => state.fileBrowser.currentFolder);
const { code, folderId } = useParams();
const isCurrentCourse =
@@ -142,12 +143,12 @@ const Collapsible = ({ course, color, state = false }) => {
useEffect(() => {
if (!isCurrentCourse || !code || !folderId) return;
if (code?.toLowerCase() !== normalizedCode) return;
-
const searchedFolder = searchFolderById(currentCourse, folderId);
- if (searchedFolder) {
+
+ if (searchedFolder && searchedFolder._id !== currentFolder?._id) {
dispatch(ChangeFolder(searchedFolder));
}
- }, [isCurrentCourse, code, folderId, normalizedCode, currentCourse, dispatch]);
+ }, [isCurrentCourse, code, folderId, normalizedCode, currentCourse, currentFolder, dispatch]);
const showTree =
!loading &&
diff --git a/client/src/screens/browse/index.jsx b/client/src/screens/browse/index.jsx
index 72af548..b679a6c 100644
--- a/client/src/screens/browse/index.jsx
+++ b/client/src/screens/browse/index.jsx
@@ -67,7 +67,7 @@ const BrowseScreen = () => {
};
if (folderData?.childType == "File" && folderData?.children?.length > 1)
- folderData?.children.sort(sortFile);
+ folderData?.children.sort(sortFile); //sorting current folder files by name
const contributionHandler = (event) => {
const collection = document.getElementsByClassName("contri");
@@ -115,6 +115,18 @@ const BrowseScreen = () => {
}
}, []);
+ const findFolderById = (folders, id) => {
+ if (!folders || !Array.isArray(folders)) return null;
+ for (const folder of folders) {
+ if (folder._id === id) return folder;
+ if (folder.children?.length) {
+ const result = findFolderById(folder.children, id);
+ if (result) return result;
+ }
+ }
+ return null;
+ };
+
useEffect(() => {
if (loading || !code) {
return;
@@ -145,19 +157,15 @@ const BrowseScreen = () => {
) {
const defaultYearIndex = fetchedData.children.length - 1;
const defaultYear = fetchedData.children[defaultYearIndex];
+ let activeFolder = folderId ? findFolderById(fetchedData.children, folderId) : null;
+
if (defaultYear && defaultYear.children) {
dispatch(
ChangeCurrentYearData(defaultYearIndex, defaultYear.children || [])
);
dispatch(ClearFolderHistory()); // Clear history when starting with a new course/year
- dispatch(ChangeFolder(defaultYear));
- refreshCourseFromServer(dispatch, code, {
- yearIndex: defaultYearIndex,
- folderId: folderId || defaultYear._id,
- });
- } else {
+ dispatch(ChangeFolder(activeFolder || defaultYear));
}
- } else {
}
} else {
let fetchingToast = toast.loading("Loading course data...");
@@ -186,12 +194,14 @@ const BrowseScreen = () => {
) {
const defaultYearIndex = fetchedData.children.length - 1;
const defaultYear = fetchedData.children[defaultYearIndex];
+ let activeFolder = folderId ? findFolderById(fetchedData.children, folderId) : null;
+
if (defaultYear && defaultYear.children) {
dispatch(
ChangeCurrentYearData(defaultYearIndex, defaultYear.children || [])
);
dispatch(ClearFolderHistory()); // Clear history when starting with a new course/year
- dispatch(ChangeFolder(defaultYear));
+ dispatch(ChangeFolder(activeFolder || defaultYear));
}
}
} else {
@@ -203,6 +213,40 @@ const BrowseScreen = () => {
run();
}, [loading, code]);
+ useEffect(() => {
+ if (!code || !currCourse || !Array.isArray(currCourse) || currCourse.length === 0) {
+ return;
+ }
+
+ if (folderId) {
+ if (folderData?._id === folderId) return;
+
+ const matched = findFolderById(currCourse, folderId);
+ if (matched) {
+ dispatch(ChangeFolder(matched));
+ } else {
+ fetchFolder(folderId, code)
+ .then((freshFolder) => {
+ if (freshFolder && freshFolder._id) {
+ dispatch(ChangeFolder(freshFolder));
+ } else {
+ toast.error("Folder not found!");
+ navigate(`/browse/${code}`, { replace: true });
+ }
+ })
+ .catch(() => {
+ toast.error("Folder not found!");
+ navigate(`/browse/${code}`, { replace: true });
+ });
+ }
+ } else {
+ const defaultYear = currCourse[currYear !== null && currYear !== undefined ? currYear : currCourse.length - 1];
+ if (defaultYear && folderData?._id !== defaultYear._id) {
+ dispatch(ChangeFolder(defaultYear));
+ }
+ }
+ }, [folderId, code, currCourse, currYear, folderData]);
+
useEffect(() => {
const refreshFolderData = async () => {
if (!folderData?._id || !currCourseCode) return;
@@ -223,40 +267,37 @@ const BrowseScreen = () => {
refreshFolderData();
}, [refreshKey]);
- const findFolderById = (folders, id) => {
- for (const folder of folders) {
- if (folder._id === id) return folder;
- if (folder.children?.length) {
- const result = findFolderById(folder.children, id);
- if (result) return result;
- }
- }
- return null;
- };
-
const HeaderText =
folderData?.childType === "File"
? "Select a file..."
: folderData?.childType === "Folder"
- ? "Select a folder..."
- : currCourse
- ? "No data available for this course"
- : "Select a course...";
+ ? "Select a folder..."
+ : currCourse
+ ? "No data available for this course"
+ : "Select a course...";
+
const handleBackClick = async () => {
if (folderHistory.length > 0) {
const previousFolder = folderHistory[folderHistory.length - 1];
dispatch(PopFolderHistory());
if (previousFolder && previousFolder._id) {
- try {
- const freshFolder = await fetchFolder(previousFolder._id, currCourseCode);
- dispatch(ChangeFolder(freshFolder));
- } catch (err) {
- // Fallback to popped history snapshot
+ dispatch(ChangeFolder(previousFolder));
+ const isRootYear = currCourse.some(y => y._id === previousFolder._id);
+ if (isRootYear) {
+ navigate(`/browse/${currCourseCode}`);
+ } else {
+ navigate(`/browse/${currCourseCode}/${previousFolder._id}`);
}
+
+ } else {
+ navigate(`/browse/${currCourseCode}`);
}
+ } else {
+ navigate(`/browse/${currCourseCode}`);
}
};
- const canGoBack = folderHistory.length > 0;
+
+ const canGoBack = folderHistory.length > 0 || !!(folderId && folderData?._id);
const allCourses = [
...(user.user?.courses || []),
...(user.localCourses || []),
@@ -270,7 +311,7 @@ const BrowseScreen = () => {
try {
dispatch(ChangeCurrentYearData(null, []));
dispatch(ChangeFolder(null));
- dispatch(ClearFolderHistory()); // Clear folder history when changing courses
+ dispatch(ClearFolderHistory());
let courseData = allCourseData?.find(
(course) =>
hasUsableCourseTree(course) &&
@@ -326,6 +367,11 @@ const BrowseScreen = () => {
dispatch(ChangeCurrentYearData(selectedYearIndex, selectedYear.children));
dispatch(ChangeFolder(selectedYear));
dispatch(RefreshCurrentFolder());
+ if (selectedYear._id) {
+ navigate(`/browse/${currCourseCode}/${selectedYear._id}`);
+ } else {
+ navigate(`/browse/${currCourseCode}`);
+ }
}
}
};
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 499a11a..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "CourseHub",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {}
-}