diff --git a/CHANGELOG.md b/CHANGELOG.md index 986b916..15ba244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to Workout Lens are documented here. +## [1.2.2] — 2026-05-10 + +### Developer + +- **React hook lint fixes (#159 #160)** — Resolved all `react-hooks/exhaustive-deps` and `react-compiler` warnings. Real refactors: `History` auto-expand logic moved into `loadSession` (eliminates a cascading setState); `MuscleMap` date-reset moved to the two dispatch sites that enter the confirm step; `Report` cache-lookup `useEffect` relocated below the `useMemo` values it reads (fixes forward-references to `muscleCounts`, `sessionCount`, `untrainedMuscles`); `Home` tooltip clamping now stores `maxLeft` in state at event-handler time instead of reading `weekStripRef.current` during render. Remaining five patterns (`Bibliotek` pagination reset, `Planlegger` async plan fetch, `Report` loading-state initialisation, `MuscleMap` template-preload callback) suppressed with targeted `eslint-disable` comments explaining why each omission is intentional. + ## [1.2.1] — 2026-05-10 ### Added diff --git a/app/src/components/Bibliotek.jsx b/app/src/components/Bibliotek.jsx index f91529c..2529641 100644 --- a/app/src/components/Bibliotek.jsx +++ b/app/src/components/Bibliotek.jsx @@ -62,7 +62,8 @@ export default function Bibliotek({ onEditTemplate, initialTab = 0 }) { : exercises; }, [exercises, debouncedSearch]); - useEffect(() => { setExVisible(20); }, [filteredExercises]); + // eslint-disable-next-line react-hooks/set-state-in-effect + useEffect(() => { setExVisible(20); }, [filteredExercises]); // reset pagination when filter changes const filteredTemplates = useMemo(() => { const q = tplSearch.trim().toLowerCase(); diff --git a/app/src/components/History.jsx b/app/src/components/History.jsx index cfe1d02..51663ca 100644 --- a/app/src/components/History.jsx +++ b/app/src/components/History.jsx @@ -182,6 +182,7 @@ export default function History({ initialDate }) { const [hoveredMuscle, setHoveredMuscle] = useState(null); const [classHistory, setClassHistory] = useState(new Map()); const fileRef = useRef(); + const initialDateRef = useRef(initialDate); const patchSessionEdit = (id, patch) => setSessionEdits(prev => { const next = new Map(prev); @@ -199,10 +200,6 @@ export default function History({ initialDate }) { .catch(() => {}); }, []); - useEffect(() => { - if (initialDate) loadSession(initialDate); - }, []); - const sessionMuscleIdMap = useMemo( () => new Map(sessions.map(s => [s.id, sessionMuscleIds(s)])), [sessions] @@ -230,14 +227,6 @@ export default function History({ initialDate }) { return d.getFullYear() === viewYear && d.getMonth() === viewMonth; }).length, [filteredSessions, viewYear, viewMonth]); - useEffect(() => { - if (daySessions.length === 1) { - setExpandedIds(new Set([daySessions[0].id])); - } else { - setExpandedIds(new Set()); - } - }, [daySessions]); - const loadClassHistory = async (gymCalendarId) => { setClassHistory(prev => new Map(prev).set(gymCalendarId, { loading: true, sessions: [], error: null })); try { @@ -272,6 +261,7 @@ export default function History({ initialDate }) { const loadSession = async (dateStr) => { setLoadingSession(true); setDaySessions([]); + setExpandedIds(new Set()); try { const results = await fetchSessionsByDate(dateStr); results.forEach(s => { @@ -285,6 +275,7 @@ export default function History({ initialDate }) { return new Date(ta) - new Date(tb); }); setDaySessions(results); + setExpandedIds(results.length === 1 ? new Set([results[0].id]) : new Set()); } catch (err) { logDevError("History/loadSession", err); } finally { @@ -292,6 +283,11 @@ export default function History({ initialDate }) { } }; + // mount-only: initialDate is a one-time navigation hint from the home screen + useEffect(() => { + if (initialDateRef.current) loadSession(initialDateRef.current); + }, []); // mount-only: initialDateRef is a ref, not reactive + const handleSelect = (dateStr) => { if (!dateStr || !filteredTrainedSet.has(dateStr)) return; setSelectedDate(new Date(dateStr + "T12:00:00")); diff --git a/app/src/components/Home.jsx b/app/src/components/Home.jsx index 0e0f93c..eac456f 100644 --- a/app/src/components/Home.jsx +++ b/app/src/components/Home.jsx @@ -142,7 +142,7 @@ export default function Home({ onShowHistoryWithDate }) { onKeyDown={count > 0 ? e => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onShowHistoryWithDate(date); } } : undefined} onMouseEnter={count > 0 ? e => { const rect = weekStripRef.current?.getBoundingClientRect(); - if (rect) setTooltip({ names, x: e.clientX - rect.left, y: e.clientY - rect.top }); + if (rect) setTooltip({ names, x: e.clientX - rect.left, y: e.clientY - rect.top, maxLeft: rect.width - 160 }); } : undefined} onMouseMove={count > 0 ? e => { const rect = weekStripRef.current?.getBoundingClientRect(); @@ -152,7 +152,7 @@ export default function Home({ onShowHistoryWithDate }) { onFocus={count > 0 ? e => { const stripRect = weekStripRef.current?.getBoundingClientRect(); const cellRect = e.currentTarget.getBoundingClientRect(); - if (stripRect) setTooltip({ names, x: cellRect.left - stripRect.left, y: 0 }); + if (stripRect) setTooltip({ names, x: cellRect.left - stripRect.left, y: 0, maxLeft: stripRect.width - 160 }); } : undefined} onBlur={count > 0 ? () => setTooltip(null) : undefined} style={{ @@ -176,7 +176,7 @@ export default function Home({ onShowHistoryWithDate }) { {tooltip && tooltip.names.length > 0 && (