From a279f63cb1fa5e6aca1c7117fe7d54ae5bcb9b27 Mon Sep 17 00:00:00 2001 From: condyl Date: Sun, 11 Jan 2026 16:33:43 -0500 Subject: [PATCH] fix: initialize course colors on mount --- .../CourseList/CourseListItemComponent.jsx | 16 ++++-- .../generator/CourseColorsContext.jsx | 53 +++++++++++++++---- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/components/generator/Forms/CourseList/CourseListItemComponent.jsx b/src/components/generator/Forms/CourseList/CourseListItemComponent.jsx index 30bd247..c6312ff 100644 --- a/src/components/generator/Forms/CourseList/CourseListItemComponent.jsx +++ b/src/components/generator/Forms/CourseList/CourseListItemComponent.jsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React, { useContext, useEffect } from "react"; import ExpandLess from "@mui/icons-material/ExpandLess"; import ExpandMore from "@mui/icons-material/ExpandMore"; import DeleteIcon from "@mui/icons-material/Delete"; @@ -45,10 +45,20 @@ export default function CourseListComponent({ removeCourse, }) { const [open, setOpen] = React.useState(false); - const { courseColors, updateCourseColor, getDefaultColorForCourse } = - useContext(CourseColorsContext); + const { + courseColors, + updateCourseColor, + getDefaultColorForCourse, + initializeCourseColor, + } = useContext(CourseColorsContext); const courseCode = course.split(" ")[0] + course.split(" ")[1]; + useEffect(() => { + if (!courseColors[courseCode]) { + initializeCourseColor(courseCode); + } + }, [courseCode]); + const handleRemoveClick = () => { setOpen(false); removeCourse(course); diff --git a/src/lib/contexts/generator/CourseColorsContext.jsx b/src/lib/contexts/generator/CourseColorsContext.jsx index 31a097f..5bfd41f 100644 --- a/src/lib/contexts/generator/CourseColorsContext.jsx +++ b/src/lib/contexts/generator/CourseColorsContext.jsx @@ -1,4 +1,10 @@ -import React, { createContext, useState, useRef } from "react"; +import React, { + createContext, + useState, + useRef, + useCallback, + useEffect, +} from "react"; // Visually distinguishable colors that work well for both light and dark themes // Colors are ordered to maximize contrast between consecutive colors @@ -25,6 +31,7 @@ export const CourseColorsContext = createContext(); export const CourseColorsProvider = ({ children }) => { const [courseColors, setCourseColors] = useState({}); const [usedColors, setUsedColors] = useState([]); + const usedColorsRef = useRef([]); const [timetableHandlers, setTimetableHandlers] = useState(null); const [calendarHandler, setCalendarHandler] = useState(null); @@ -56,17 +63,22 @@ export const CourseColorsProvider = ({ children }) => { }; const getNextColor = () => { + const currentUsedColors = usedColorsRef.current; const availableColors = defaultColors.filter( - (color) => !usedColors.includes(color), + (color) => !currentUsedColors.includes(color), ); if (availableColors.length === 0) { // If all colors are used, start over with a slight variation - const colorIndex = usedColors.length % defaultColors.length; + const colorIndex = currentUsedColors.length % defaultColors.length; return defaultColors[colorIndex]; } return availableColors[0]; }; + useEffect(() => { + usedColorsRef.current = usedColors; + }, [usedColors]); + const colourTimeoutRef = useRef(false); const updateCourseColor = (courseCode, color) => { if (!colourTimeoutRef.current) { @@ -96,22 +108,41 @@ export const CourseColorsProvider = ({ children }) => { if (courseColors[courseCode]) { return courseColors[courseCode]; } - const newColor = getNextColor(); - // Update the colors state immediately to ensure consistency - setCourseColors((prev) => ({ - ...prev, - [courseCode]: newColor, - })); - setUsedColors((current) => [...current, newColor]); - return newColor; + return getNextColor(); }; + const initializeCourseColor = useCallback((courseCode) => { + setCourseColors((prev) => { + if (prev[courseCode]) { + return prev; + } + const currentUsedColors = usedColorsRef.current; + const availableColors = defaultColors.filter( + (color) => !currentUsedColors.includes(color), + ); + let newColor; + if (availableColors.length === 0) { + const colorIndex = currentUsedColors.length % defaultColors.length; + newColor = defaultColors[colorIndex]; + } else { + newColor = availableColors[0]; + } + // Update usedColors + setUsedColors((current) => [...current, newColor]); + return { + ...prev, + [courseCode]: newColor, + }; + }); + }, []); + return (