From 93c8f6b9d398db039ca55e46dca42caff722e88b Mon Sep 17 00:00:00 2001 From: rouggy Date: Tue, 30 Jun 2026 11:11:16 +0200 Subject: [PATCH] fix: persistence of awards column bug corrected --- frontend/src/components/RecentQSOsGrid.tsx | 14 ++++++++++++-- frontend/src/components/WorkedBeforeGrid.tsx | 18 +++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/RecentQSOsGrid.tsx b/frontend/src/components/RecentQSOsGrid.tsx index 24a4e1a..cf7605d 100644 --- a/frontend/src/components/RecentQSOsGrid.tsx +++ b/frontend/src/components/RecentQSOsGrid.tsx @@ -254,7 +254,14 @@ export function RecentQSOsGrid({ rows, selectAllSignal, onRowDoubleClicked, onRo // Compute initial column defs: all columns defined, but those not marked // defaultVisible start hidden. The user's saved state (loaded onGridReady) // overrides this so a previously toggled column wins. + // While AG Grid rebuilds columns (award columns load async), it fires column + // events that would otherwise trigger a save of the DEFAULT visibility before + // we re-apply the user's saved state — clobbering it. This flag suppresses the + // auto-save during that window. Set in the memo (runs at render, before the + // column events) and cleared by the re-apply effect below. + const restoringRef = useRef(true); const columnDefs = useMemo[]>(() => { + restoringRef.current = true; const base = COL_CATALOG.map((c) => { const { group: _g, label: _l, defaultVisible, ...rest } = c; return { ...rest, hide: !defaultVisible }; @@ -290,6 +297,7 @@ export function RecentQSOsGrid({ rows, selectAllSignal, onRowDoubleClicked, onRo }); } const saveColumnState = useCallback(() => { + if (restoringRef.current) return; // ignore the events fired by a column rebuild const state = gridRef.current?.api?.getColumnState(); if (state) saveState(COL_STATE_KEY, state); }, []); @@ -301,9 +309,11 @@ export function RecentQSOsGrid({ rows, selectAllSignal, onRowDoubleClicked, onRo // every rebuild so the user's choices win. No-op before the grid is ready. useEffect(() => { const api = gridRef.current?.api; - if (!api) return; const local = loadLocal(COL_STATE_KEY); - if (local) api.applyColumnState({ state: local as ColumnState[], applyOrder: true }); + if (api && local) api.applyColumnState({ state: local as ColumnState[], applyOrder: true }); + // Re-enable saving once AG Grid has settled the column events from the rebuild. + const t = window.setTimeout(() => { restoringRef.current = false; }, 0); + return () => window.clearTimeout(t); }, [awardCols]); function handleRowDoubleClicked(e: RowDoubleClickedEvent) { diff --git a/frontend/src/components/WorkedBeforeGrid.tsx b/frontend/src/components/WorkedBeforeGrid.tsx index 0f86a53..5556829 100644 --- a/frontend/src/components/WorkedBeforeGrid.tsx +++ b/frontend/src/components/WorkedBeforeGrid.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AllCommunityModule, ModuleRegistry, themeQuartz, type ColDef, type ColumnState, type GridReadyEvent, type RowDoubleClickedEvent, @@ -96,7 +96,12 @@ export function WorkedBeforeGrid({ wb, busy, currentCall, onRowDoubleClicked, on const count = wb?.count ?? 0; const entries = wb?.entries ?? []; + // Suppress auto-save while AG Grid rebuilds columns (award columns load async), + // so the default visibility doesn't clobber the user's saved state. See the + // matching note in RecentQSOsGrid. + const restoringRef = useRef(true); const columnDefs = useMemo[]>(() => { + restoringRef.current = true; const base = COL_CATALOG.map((c) => { const { group: _g, label: _l, defaultVisible, ...rest } = c; return { ...rest, hide: !defaultVisible }; @@ -127,10 +132,21 @@ export function WorkedBeforeGrid({ wb, busy, currentCall, onRowDoubleClicked, on }); } const saveColumnState = useCallback(() => { + if (restoringRef.current) return; // ignore events fired by a column rebuild const state = gridRef.current?.api?.getColumnState(); if (state) saveState(COL_STATE_KEY, state); }, []); + // Re-apply the saved column state after the award columns load (they rebuild + // the column set), so the user's visibility choices win over the defaults. + useEffect(() => { + const api = gridRef.current?.api; + const local = loadLocal(COL_STATE_KEY); + if (api && local) api.applyColumnState({ state: local as ColumnState[], applyOrder: true }); + const t = window.setTimeout(() => { restoringRef.current = false; }, 0); + return () => window.clearTimeout(t); + }, [awardCols]); + function isColVisible(colId: string): boolean { const col = gridRef.current?.api?.getColumn(colId); return col ? col.isVisible() : !!COL_CATALOG.find((c) => c.colId === colId)?.defaultVisible;