From a11ae101fbb18f3f73a33a4d80bc7947641b32de Mon Sep 17 00:00:00 2001 From: Sandeep Das <65163571+Dasyure@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:43:11 +1000 Subject: [PATCH] fix: CF-425: cleaning graph loading screen (#1039) * feat: dark mode functionality added for editMarkModal's input and button elements * feat: dark mode improvement for editMarkModal's cancel button * feat: dark mode improvements for OptionHeader icons underneath the 'Term Planner' tab * feat: dark mode added for the select menu in the settingsMenu tooltip under the TermPlanner tab * feat: dark mode added to SettingMenu's DatePicker element * feat: dark mode for export button done + editModalMark bug fixed * fix: making sure the css for the select element in settingsMenu does not affect other select elements * feat: dark mode improvement for popconfirm for unplan wanring, import tooltip, also moved the button for import and export tooltip to common styles * feat: dark mode scrollbar added * feat: dark mode scrollbar on courseSelector menu had ugly white padding, fixed it to be #333 color instead * fix: href in courseSelector not very readable in dark mode, made the text a bit brighter and made a styled component for it * feat: dark mode added for search bar * feat: dark mode added for remove planner button * fix: forgot to add the new styles.ts file * feat: progressBar's text color and trailing color fixed * feat: dividing line in courseDescription changed from white to a dark grey * feat: bug icon turned into dark mode * feat: dark mode added for quick add and remove buttons in course menu * feat: courseProgression progress bar trailing color changed to dark grey * feat: progress on dark mode for graph, need to save this commit before I merge in the latest changes since the graph was changed * feat: dark mode for graph complete (nodes, arrows, hover states) + label now changes on hover (non-dark mode feature) * feat: buttons on graphical selector are dark mode * feat: saving progress on converting courseDescription panel to dark mode * feat: dark mode added to the sidebar * feat: sidebardrawer color changed, box shadow added to tabs so it looks more visible in dark mode * feat: new images added in help menu in course selector, dark mode versions added too * feat: TermPlanner's help menu tooltips now have dark mode pics and gifs * feat: highlight adjacent nodes and edges on hover * feat: highlight adjacent nodes opacity updated * refactor: graph.ts, changing function names and object names to be more readable: * feat: implemented a function that checks if a course is a prereq based on GraphData without calling the backend * fix: two graphs get rendered if you switch tabs fast enough * feat: created a function to store a hashmap of prereqs for later use for node styling * fix: updated the function that checks for coursePrerequisite Other options are: 1. Rely on GraphData, however it gets laggy when you hover over too many nodes 2. API call - would get slow with multiple requests 3. CourseEdge info gets stored into a hashmap at initialisation, so checking for prereqs is fast afterwards * refactor: graph.ts function and object names made more readable * refactor: rewriting the returns and using spread operator to reduce repetition in graph.ts * feat: highlight prerequisite nodes on hover * refactor: splitting functions up as they were getting too long * fix: if the dark mode button is toggled on and off, it repaints the canvas more than one time * feat: highlighted incoming edge if it's a prerequisite as well * feat: forgot to add pics into the HelpMenu for the new graphical selector hover node feature * feat: unlocked course nodes are now distinct from planned and locked courses feat: HelpMenu pictures were also updated * fix: adding and removing courses in graphical selector, updates the node in the graph without re-render refactor: graph.ts duplicate function removed * fix: endArrow opacity of graph was not changing on hover * fix: instead of using courseSlicer, used axios request to update unlocked nodes after adding to planner * fix: searchbar hidden while graph loads fix: random course node that pops up is hidden now feat: show all courses toggle has a loading screen now too * docs: forgot to update comment which I copy pasted in * fix: no-non-null-assertion warning fixed in graph.ts --------- Co-authored-by: Daysure --- frontend/src/components/Spinner/Spinner.tsx | 4 +-- frontend/src/config/theme.ts | 21 +++++++++-- .../CourseGraph/CourseGraph.tsx | 36 ++++++++++++++----- .../GraphicalSelector/GraphicalSelector.tsx | 11 ++++-- frontend/src/styled.d.ts | 2 ++ 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/Spinner/Spinner.tsx b/frontend/src/components/Spinner/Spinner.tsx index 1349d36c4..384286279 100644 --- a/frontend/src/components/Spinner/Spinner.tsx +++ b/frontend/src/components/Spinner/Spinner.tsx @@ -8,9 +8,9 @@ type Props = { }; const Spinner = ({ text }: Props) => ( - + } /> -
{text}
+

{text}

); diff --git a/frontend/src/config/theme.ts b/frontend/src/config/theme.ts index 697680ff4..7eaea7bc4 100644 --- a/frontend/src/config/theme.ts +++ b/frontend/src/config/theme.ts @@ -190,6 +190,20 @@ export const GlobalStyles = createGlobalStyle` box-shadow: ${({ theme }) => theme.graph?.tabBoxShadow} !important; } + // .spinner-wrapper .anticon anticon-loading anticon-spin ant-spin-dot { + .spinner-wrapper .ant-spin-dot { + color: ${({ theme }) => theme.graph.loadingIcon}; + } + + .loading-spinner p { + color: ${({ theme }) => theme.text}; + text-shadow: + 0.07em 0 ${({ theme }) => theme.graph.loadingText}, + 0 0.07em ${({ theme }) => theme.graph.loadingText}, + -0.07em 0 ${({ theme }) => theme.graph.loadingText}, + 0 -0.07em ${({ theme }) => theme.graph.loadingText}; + } + // Scrollbar settings /* width */ ::-webkit-scrollbar { @@ -339,7 +353,8 @@ export const lightTheme: DefaultTheme = { borderColor: '#c2c2c2', tabColor: '#666', tabBottomBorderColor: '#f0f0f0', - tabTextColor: '#000' + tabTextColor: '#000', + loadingText: '#fff' }, sidebarDrawer: { backgroundColor: '#fff', @@ -484,7 +499,9 @@ export const darkTheme: DefaultTheme = { tabColor: '#fff', tabBottomBorderColor: '#5d5d5d', tabTextColor: '#f1f1f1', - tabBoxShadow: 'inset 16px 0 8px -8px rgba(255, 255, 255, 0.08)' + tabBoxShadow: 'inset 16px 0 8px -8px rgba(255, 255, 255, 0.08)', + loadingIcon: '#d7b7fd', + loadingText: '#000' }, sidebarDrawer: { backgroundColor: '#343239', diff --git a/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx b/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx index 2d9ab4cef..402aaf072 100644 --- a/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx +++ b/frontend/src/pages/GraphicalSelector/CourseGraph/CourseGraph.tsx @@ -39,6 +39,8 @@ type Props = { fullscreen: boolean; focused?: string; hasPlannerUpdated: React.MutableRefObject; + loading: boolean; + setLoading: React.Dispatch>; }; interface CoursePrerequisite { @@ -50,7 +52,9 @@ const CourseGraph = ({ handleToggleFullscreen, fullscreen, focused, - hasPlannerUpdated + hasPlannerUpdated, + loading, + setLoading }: Props) => { const { theme } = useSelector((state: RootState) => state.settings); const previousTheme = useRef(theme); @@ -61,8 +65,8 @@ const CourseGraph = ({ const windowSize = useAppWindowSize(); const graphRef = useRef(null); - const initialising = useRef(false); // prevents multiple graphs being loaded - const [loading, setLoading] = useState(true); + const initialisingStart = useRef(false); // prevents multiple graphs being loaded + const initialisingEnd = useRef(false); // unhide graph after loading complete const [unlockedCourses, setUnlockedCourses] = useState(false); const [prerequisites, setPrerequisites] = useState({}); @@ -185,6 +189,7 @@ const CourseGraph = ({ nodeStrength: 2500, preventOverlap: true, onLayoutEnd: () => { + initialisingEnd.current = true; setLoading(false); } }, @@ -205,8 +210,11 @@ const CourseGraph = ({ edges: courseEdges }; + // Hide graph until it's finished loaded, due to incomplete initial graph generation graphRef.current.data(data); graphRef.current.render(); + graphRef.current.getNodes().forEach((n) => n.hide()); + graphRef.current.getEdges().forEach((e) => e.hide()); graphRef.current.on('node:click', async (ev) => { // Clicking a node loads up course description for the course and set active @@ -279,7 +287,7 @@ const CourseGraph = ({ const setupGraph = async () => { try { - initialising.current = true; + initialisingStart.current = true; const res = await Promise.allSettled([ axios.get(`/programs/graph/${programCode}/${specs.join('+')}`), axios.post( @@ -300,7 +308,7 @@ const CourseGraph = ({ } }; - if (!initialising.current) setupGraph(); + if (!initialisingStart.current) setupGraph(); if (hasPlannerUpdated.current) { hasPlannerUpdated.current = false; getUnlocked(); @@ -319,9 +327,19 @@ const CourseGraph = ({ prerequisites, degree, planner, - hasPlannerUpdated + hasPlannerUpdated, + setLoading ]); + // Show all nodes and edges once graph is initially loaded + useEffect(() => { + if (initialisingEnd.current) { + graphRef.current?.getNodes().forEach((n) => n.show()); + graphRef.current?.getEdges().forEach((e) => e.show()); + initialisingEnd.current = false; + } + }, [loading]); + const showAllCourses = () => { if (!graphRef.current) return; graphRef.current.getNodes().forEach((n) => n.show()); @@ -331,6 +349,7 @@ const CourseGraph = ({ const showUnlockedCourses = useCallback(async () => { if (!graphRef.current) return; try { + setLoading(true); const { data: { courses_state: coursesStates } } = await axios.post( @@ -349,11 +368,12 @@ const CourseGraph = ({ n.getEdges().forEach((e) => e.hide()); } }); + setLoading(false); } catch (e) { // eslint-disable-next-line no-console console.error('Error at showUnlockedCourses', e); } - }, [degree, planner]); + }, [degree, planner, setLoading]); const handleZoomIn = () => { const viewportCenter = graphRef.current?.getViewPortCenterPoint(); @@ -406,7 +426,7 @@ const CourseGraph = ({ return ( {loading ? ( - + ) : ( diff --git a/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx b/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx index 838a75f0e..00ec32fca 100644 --- a/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx +++ b/frontend/src/pages/GraphicalSelector/GraphicalSelector.tsx @@ -14,6 +14,7 @@ const GraphicalSelector = () => { const [fullscreen, setFullscreen] = useState(false); const [courseCode, setCourseCode] = useState(null); const [activeTab, setActiveTab] = useState(HELP_TAB); + const [loading, setLoading] = useState(true); const courseDescInfoCache = useRef({} as CourseDescInfoResCache); const hasPlannerUpdated = useRef(false); @@ -54,10 +55,14 @@ const GraphicalSelector = () => { handleToggleFullscreen={() => setFullscreen((prevState) => !prevState)} focused={courseCode ?? undefined} hasPlannerUpdated={hasPlannerUpdated} + loading={loading} + setLoading={setLoading} /> - - - + {!loading && ( + + + + )} {fullscreen && (