From d1e6b3b92d8c889bb2f2bf1052b0c0dd3a7ec0f7 Mon Sep 17 00:00:00 2001 From: Anis SMAIL Date: Thu, 30 Jan 2025 13:28:16 +0100 Subject: [PATCH] add item loading mechanic, replace onClickNode by onExpandedChange --- .../App/Studies/StudyTree/StudyTreeNode.tsx | 18 +++------- .../App/Studies/StudyTree/index.tsx | 33 +++++++++++-------- .../components/App/Studies/StudyTree/types.ts | 2 +- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/webapp/src/components/App/Studies/StudyTree/StudyTreeNode.tsx b/webapp/src/components/App/Studies/StudyTree/StudyTreeNode.tsx index 7d3644f06f..335fcd6859 100644 --- a/webapp/src/components/App/Studies/StudyTree/StudyTreeNode.tsx +++ b/webapp/src/components/App/Studies/StudyTree/StudyTreeNode.tsx @@ -21,10 +21,10 @@ import { t } from "i18next"; export default memo(function StudyTreeNode({ studyTreeNode, parentId, - onNodeClick, + itemsLoading, }: StudyTreeNodeProps) { - const isLoadingFolder = studyTreeNode.hasChildren && studyTreeNode.children.length === 0; const id = parentId ? `${parentId}/${studyTreeNode.name}` : studyTreeNode.name; + const isLoadingFolder = itemsLoading.includes(id); const sortedChildren = useMemo( () => R.sortBy(R.prop("name"), studyTreeNode.children), @@ -33,28 +33,20 @@ export default memo(function StudyTreeNode({ if (isLoadingFolder) { return ( - onNodeClick(id, studyTreeNode)} - > + ); } return ( - onNodeClick(id, studyTreeNode)} - > + {sortedChildren.map((child) => ( ))} diff --git a/webapp/src/components/App/Studies/StudyTree/index.tsx b/webapp/src/components/App/Studies/StudyTree/index.tsx index 8f33cd0ab0..0054ccc112 100644 --- a/webapp/src/components/App/Studies/StudyTree/index.tsx +++ b/webapp/src/components/App/Studies/StudyTree/index.tsx @@ -20,7 +20,7 @@ import { updateStudyFilters } from "../../../../redux/ducks/studies"; import { SimpleTreeView } from "@mui/x-tree-view/SimpleTreeView"; import { getParentPaths } from "../../../../utils/pathUtils"; import * as R from "ramda"; -import { useState } from "react"; +import { useState, type SyntheticEvent } from "react"; import useEnqueueErrorSnackbar from "@/hooks/useEnqueueErrorSnackbar"; import useUpdateEffectOnce from "@/hooks/useUpdateEffectOnce"; import { fetchAndInsertSubfolders, fetchAndInsertWorkspaces } from "./utils"; @@ -31,6 +31,7 @@ import StudyTreeNodeComponent from "./StudyTreeNode"; function StudyTree() { const initialStudiesTree = useAppSelector(getStudiesTree); const [studiesTree, setStudiesTree] = useState(initialStudiesTree); + const [itemsLoading, setItemsLoading] = useState([]); const folder = useAppSelector((state) => getStudyFilters(state).folder, R.T); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const dispatch = useAppDispatch(); @@ -41,7 +42,7 @@ function StudyTree() { useUpdateEffectOnce(() => { // be carefull to pass initialStudiesTree and not studiesTree at rootNode parameter // otherwise we'll lose the default workspace - updateTree("root", initialStudiesTree, initialStudiesTree); + updateTree("root", initialStudiesTree); }, [initialStudiesTree]); /** @@ -60,12 +61,13 @@ function StudyTree() { * @param rootNode - The root node of the tree * @param selectedNode - The node of the item clicked */ - async function updateTree(itemId: string, rootNode: StudyTreeNode, selectedNode: StudyTreeNode) { - if (selectedNode.path.startsWith("/default")) { + async function updateTree(itemId: string, rootNode: StudyTreeNode) { + if (itemId.startsWith("root/default")) { // we don't update the tree if the user clicks on the default workspace // api doesn't allow to fetch the subfolders of the default workspace return; } + setItemsLoading([...itemsLoading, itemId]); // Bug fix : this function used to take only the itemId and the selectedNode, and we used to initialize treeAfterWorkspacesUpdate // with the studiesTree closure, referencing directly the state, like this : treeAfterWorkspacesUpdate = studiesTree; // The thing is at the first render studiesTree was empty. @@ -89,7 +91,7 @@ function StudyTree() { .map((child) => `root${child.path}`); } else { // If the user clicks on a folder, we add the path of the clicked folder to the list of paths to fetch. - pathsToFetch = [`root${selectedNode.path}`]; + pathsToFetch = [itemId]; } const [treeAfterSubfoldersUpdate, failedPath] = await fetchAndInsertSubfolders( @@ -106,17 +108,23 @@ function StudyTree() { ); } setStudiesTree(treeAfterSubfoldersUpdate); + setItemsLoading(itemsLoading.filter((e) => e !== itemId)); } //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// - const handleTreeItemClick = async (itemId: string, studyTreeNode: StudyTreeNode) => { - dispatch(updateStudyFilters({ folder: itemId })); - updateTree(itemId, studiesTree, studyTreeNode); + const handleItemExpansionToggle = async ( + event: SyntheticEvent, + itemId: string, + isExpanded: boolean, + ) => { + if (isExpanded) { + dispatch(updateStudyFilters({ folder: itemId })); + updateTree(itemId, studiesTree); + } }; - //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// @@ -133,12 +141,9 @@ function StudyTree() { overflowY: "auto", overflowX: "hidden", }} + onItemExpansionToggle={handleItemExpansionToggle} > - + ); } diff --git a/webapp/src/components/App/Studies/StudyTree/types.ts b/webapp/src/components/App/Studies/StudyTree/types.ts index bdeeab28c2..d91833701b 100644 --- a/webapp/src/components/App/Studies/StudyTree/types.ts +++ b/webapp/src/components/App/Studies/StudyTree/types.ts @@ -30,5 +30,5 @@ export interface NonStudyFolderDTO { export interface StudyTreeNodeProps { studyTreeNode: StudyTreeNode; parentId: string; - onNodeClick: (id: string, node: StudyTreeNode) => void; + itemsLoading: string[]; }