fix: column layouts would not stick — five separate faults
1. A language change rebuilt the column defs, which sets the anti-clobber guard, but the effect that clears it listened to only two of the rebuild's causes. Guard stuck on → every column change was silently discarded for the rest of the session. Now keyed on the rebuilt defs themselves, so any future cause is covered. 2. The cluster grid had no guard at all: the same rebuild wrote its defaults over the saved layout, in the cache AND the database. Unrecoverable. 3. Worked-before and cluster read their state before the active profile was known, so they looked up the unscoped cache key (always a miss) and then saved under the scoped one. They now wait for the scope and remount on a profile switch, like the main log. 4. GetUIPref returns an ERROR, not "", while the settings store is not yet scoped — deliberately distinct from "unset". The frontend read it as "no preference", rendered defaults, and the first column event overwrote the good copy. It now retries instead of giving up. 5. The QSL manager had no storageKey, so it shared the main log's layout and each rewrote the other. Also: the DB write is debounced (a resize drag fired dozens of writes) with a flush on close, and a rejected write is retried rather than dropped.
This commit is contained in:
+13
-4
@@ -95,7 +95,7 @@ import { SendSpotModal, type RecentSpotQSO } from '@/components/SendSpotModal';
|
||||
import { WinkeyerPanel, type WKStatus, type WKMacro } from '@/components/WinkeyerPanel';
|
||||
import { RotorCompass } from '@/components/RotorCompass';
|
||||
import { writeUiPref } from '@/lib/uiPref';
|
||||
import { setGridPrefsProfile } from '@/lib/gridPrefs';
|
||||
import { setGridPrefsProfile, flushGridPrefs } from '@/lib/gridPrefs';
|
||||
import { DvkPanel, type DVKMsg, type DVKStat } from '@/components/DvkPanel';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -2356,6 +2356,14 @@ export default function App() {
|
||||
}).catch(() => {});
|
||||
}, []);
|
||||
|
||||
// A column resized in the last seconds before quitting must still reach the
|
||||
// database: the DB write is debounced, so flush it on the way out.
|
||||
useEffect(() => {
|
||||
const flush = () => flushGridPrefs();
|
||||
window.addEventListener('beforeunload', flush);
|
||||
return () => { window.removeEventListener('beforeunload', flush); flush(); };
|
||||
}, []);
|
||||
|
||||
// Every setting is per-profile, so when the active profile changes the whole
|
||||
// main UI re-reads its config (station identity, lists, CAT, keyer). The Go
|
||||
// side reloads its managers; this keeps the React state in sync.
|
||||
@@ -4147,7 +4155,7 @@ export default function App() {
|
||||
</div>
|
||||
<div className="flex-1 min-h-0 flex">
|
||||
<div className="flex-1 min-w-0 flex flex-col min-h-0">
|
||||
<ClusterGrid rows={clusterRenderedRows as any} spotStatus={spotStatus} onSpotClick={handleSpotClick} />
|
||||
<ClusterGrid key={`clg-${activeProfileId ?? 'x'}`} rows={clusterRenderedRows as any} spotStatus={spotStatus} onSpotClick={handleSpotClick} />
|
||||
</div>
|
||||
{clusterShowFilters && renderClusterFilters()}
|
||||
</div>
|
||||
@@ -4156,7 +4164,7 @@ export default function App() {
|
||||
case 'worked':
|
||||
return (
|
||||
<div className="h-full w-full min-h-0 flex flex-col bg-card border border-border rounded-lg overflow-hidden">
|
||||
<WorkedBeforeGrid wb={wbWithAwards as any} myGrid={station.my_grid} awardCols={awardCols} busy={wbBusy} currentCall={callsign} onRowDoubleClicked={(q) => openEdit(q.id as number)}
|
||||
<WorkedBeforeGrid key={`wbg-${activeProfileId ?? 'x'}`} wb={wbWithAwards as any} myGrid={station.my_grid} awardCols={awardCols} busy={wbBusy} currentCall={callsign} onRowDoubleClicked={(q) => openEdit(q.id as number)}
|
||||
onUpdateFromCty={bulkUpdateFromCty} onUpdateFromQRZ={bulkUpdateFromQRZ} onUpdateFromClublog={bulkUpdateFromClublog}
|
||||
onSendTo={bulkSendTo} onSendRecording={bulkSendRecording} onSendEQSL={(ids) => setEqslQsoId(ids[0] ?? null)}
|
||||
onBulkEdit={openBulkEdit} onExportSelected={exportSelectedADIF} onExportSelectedFields={exportSelectedFields}
|
||||
@@ -5485,6 +5493,7 @@ export default function App() {
|
||||
}
|
||||
return (
|
||||
<ClusterGrid
|
||||
key={`clg2-${activeProfileId ?? 'x'}`}
|
||||
rows={rendered as any}
|
||||
spotStatus={spotStatus}
|
||||
onSpotClick={handleSpotClick}
|
||||
@@ -5569,7 +5578,7 @@ export default function App() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="worked" className="mt-0 flex flex-col min-h-0 flex-1">
|
||||
<WorkedBeforeGrid wb={wbWithAwards as any} myGrid={station.my_grid} awardCols={awardCols} busy={wbBusy} currentCall={callsign} onRowDoubleClicked={(q) => openEdit(q.id as number)}
|
||||
<WorkedBeforeGrid key={`wbg-${activeProfileId ?? 'x'}`} wb={wbWithAwards as any} myGrid={station.my_grid} awardCols={awardCols} busy={wbBusy} currentCall={callsign} onRowDoubleClicked={(q) => openEdit(q.id as number)}
|
||||
onUpdateFromCty={bulkUpdateFromCty} onUpdateFromQRZ={bulkUpdateFromQRZ} onUpdateFromClublog={bulkUpdateFromClublog} onSendTo={bulkSendTo} onSendRecording={bulkSendRecording}
|
||||
onSendEQSL={(ids) => setEqslQsoId(ids[0] ?? null)}
|
||||
onBulkEdit={openBulkEdit} onExportSelected={exportSelectedADIF} onExportSelectedFields={exportSelectedFields}
|
||||
|
||||
Reference in New Issue
Block a user