fix(pres_mgmt): config sync round 2 — save race, dead fields, launcher gate, version stamps
Four fixes found while tracing why Manager-saved Config page changes (QR, POC column, etc.) weren't reliably reaching pres_mgmt_loc: 1. Config page save was a race, not deterministic. The save handler only called load_ae_obj_id__event() (SWR — returns stale Dexie cache immediately, refreshes in the background, not awaited) and assumed that "picked up the new config." It never called sync_config__event_pres_mgmt() itself. Now calls it directly with the just-saved draft, so the editing browser updates instantly with no race. Kept the load_ae_obj_id__event() call (default try_cache: true) for propagating to other browsers/tabs via Dexie — do not pass try_cache: false there, that skips the Dexie write entirely. 2. Removed the dead "Lock Config" Sync/Unlink toggle in the sign-in panel (e_app_access_type.svelte). It wrote to four fields ($ae_loc.lock_config/sync_local_config, pres_mgmt_loc.current.lock_config/sync_local_config) that are never read anywhere (confirmed via full-repo grep), and confusingly shared a name with the real, functional "Lock Config" checkbox on the Pres Mgmt Config page. Removed the button and the now-orphaned lock_config/sync_local_config fields from PresMgmtLocState. 3. show__launcher_link was never assigned by sync_config__event_pres_mgmt() — only its inverse hide__launcher_link was. The toggle button's `show__launcher_link || trusted_access` visibility gate (in 3 menu files) always collapsed to trusted-only, ignoring the admin's setting. Added the missing assignment. 4. AE_PRES_MGMT_LOC_VERSION was bumped to 2 this morning claiming it "forces a localStorage reset" — it didn't, because _check_and_wipe() was never wired up for ae_pres_mgmt_loc, and even if it had been, the store never wrote a __version field to compare. Fixed: the store's serializer now stamps __version, and store_versions.ts wires the check. Found and fixed the same bug already live in ae_leads_loc, except worse there — it was wiping leads users' local prefs on EVERY page load, not just once. All logged in PROJECT__AE_Events_PressMgmt_Config_Cleanup.md. svelte-check: 0 errors, 0 warnings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1060,6 +1060,12 @@ export function sync_config__event_pres_mgmt({
|
||||
// Launcher links (show__ in remote → invert to hide__ in local display state)
|
||||
loc.hide__launcher_link =
|
||||
!(pres_mgmt_cfg_remote?.show__launcher_link ?? false);
|
||||
// Mirror the raw remote flag too — the toggle BUTTON's own visibility (not the
|
||||
// launcher link content itself) is gated on show__launcher_link directly in
|
||||
// ae_comp__events_menu_opts.svelte / event_page_menu.svelte / location_page_menu.svelte.
|
||||
// This was never assigned before, so that gate always collapsed to trusted_access-only.
|
||||
loc.show__launcher_link =
|
||||
pres_mgmt_cfg_remote?.show__launcher_link ?? false;
|
||||
|
||||
// Navigation / UI constraints
|
||||
loc.limit__navigation =
|
||||
|
||||
@@ -8,12 +8,10 @@ import { afterNavigate } from '$app/navigation';
|
||||
import {
|
||||
Lock,
|
||||
LockOpen,
|
||||
RefreshCw,
|
||||
ShieldEllipsis,
|
||||
ShieldMinus,
|
||||
ShieldPlus,
|
||||
ShieldUser,
|
||||
Unlink,
|
||||
User,
|
||||
UserCheck,
|
||||
UserRound,
|
||||
@@ -29,8 +27,6 @@ import {
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
// import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||
// Ideally the Event related stores should not be imported here?
|
||||
import { pres_mgmt_loc } from '$lib/stores/ae_events_stores__pres_mgmt.svelte';
|
||||
// import { db_events } from "$lib/db_events";
|
||||
|
||||
// export let hidden: boolean = false;
|
||||
@@ -361,47 +357,14 @@ function handle_clear_access() {
|
||||
|
||||
<div class="transition-all">
|
||||
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
|
||||
{#if $ae_loc.manager_access}
|
||||
{#if $ae_loc?.sync_local_config}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$ae_loc.sync_local_config = false;
|
||||
pres_mgmt_loc.current.sync_local_config = false;
|
||||
|
||||
$ae_loc.lock_config = false;
|
||||
pres_mgmt_loc.current.lock_config = false;
|
||||
|
||||
// dispatch_sync_local_config_changed();
|
||||
// tick();
|
||||
return false;
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-success border-success-500 hover:preset-filled-success-500 border transition-all hover:transition-all *:hover:inline"
|
||||
title="Syncing the local configuration with the remote configuration.">
|
||||
<RefreshCw size="1em" class="m-1" />
|
||||
<span class="hidden"> Sync </span>
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$ae_loc.sync_local_config = true;
|
||||
pres_mgmt_loc.current.sync_local_config = true;
|
||||
|
||||
$ae_loc.lock_config = true;
|
||||
pres_mgmt_loc.current.lock_config = true;
|
||||
|
||||
// dispatch_sync_local_config_changed();
|
||||
// tick();
|
||||
return true;
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-warning border-warning-500 hover:preset-filled-warning-500 border transition-all hover:transition-all *:hover:inline"
|
||||
title="Currently not syncing with the remote server. Re-sync the local configuration with the remote configuration?">
|
||||
<Unlink size="1em" class="m-1" />
|
||||
<span class="hidden"> Re-sync? </span>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
<!-- Removed 2026-06-16: dead "Lock Config" Sync/Unlink toggle. It wrote to
|
||||
$ae_loc.sync_local_config/lock_config and pres_mgmt_loc.current.sync_local_config/
|
||||
lock_config, but nothing in the codebase ever read any of those four fields —
|
||||
confirmed via full-repo grep. It also confusingly shared a name with the real,
|
||||
functional "Lock Config" checkbox on the Pres Mgmt Config page (which gates
|
||||
sync_config__event_pres_mgmt()'s remote->local sync via the value actually
|
||||
stored in event.mod_pres_mgmt_json.lock_config, not this local mirror).
|
||||
See PROJECT__AE_Events_PressMgmt_Config_Cleanup.md. -->
|
||||
|
||||
<!-- {#if $ae_loc.edit_mode}
|
||||
<button
|
||||
|
||||
@@ -17,10 +17,22 @@
|
||||
*/
|
||||
import { PersistedState } from 'runed';
|
||||
import { leads_loc_defaults } from './ae_events_stores__leads_defaults';
|
||||
import { AE_LEADS_LOC_VERSION } from './store_versions';
|
||||
|
||||
export const leads_loc = new PersistedState('ae_leads_loc', leads_loc_defaults, {
|
||||
serializer: {
|
||||
serialize: JSON.stringify,
|
||||
deserialize: (raw: string) => ({ ...leads_loc_defaults, ...JSON.parse(raw) })
|
||||
// Stamp __version on every write so store_versions.ts's _check_and_wipe() can
|
||||
// detect a breaking schema change and clear stale browsers on next load. Found
|
||||
// 2026-06-16: this was previously bare JSON.stringify with no __version field,
|
||||
// which made _check_and_wipe('ae_leads_loc', ...) see undefined !== expected
|
||||
// every time and wipe this store on EVERY page load. This import also guarantees
|
||||
// store_versions.ts's wipe side-effect runs before this PersistedState reads
|
||||
// from localStorage (ES module execution order).
|
||||
serialize: (value) =>
|
||||
JSON.stringify({ ...value, __version: AE_LEADS_LOC_VERSION }),
|
||||
deserialize: (raw: string) => {
|
||||
const { __version, ...stored } = JSON.parse(raw);
|
||||
return { ...leads_loc_defaults, ...stored };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,10 +14,19 @@
|
||||
*/
|
||||
import { PersistedState } from 'runed';
|
||||
import { pres_mgmt_loc_defaults } from './ae_events_stores__pres_mgmt_defaults';
|
||||
import { AE_PRES_MGMT_LOC_VERSION } from './store_versions';
|
||||
|
||||
export const pres_mgmt_loc = new PersistedState('ae_pres_mgmt_loc', pres_mgmt_loc_defaults, {
|
||||
serializer: {
|
||||
serialize: JSON.stringify,
|
||||
deserialize: (raw: string) => ({ ...pres_mgmt_loc_defaults, ...JSON.parse(raw) })
|
||||
// Stamp __version on every write so store_versions.ts's _check_and_wipe() can
|
||||
// detect a breaking schema change and clear stale browsers on next load. This
|
||||
// import also guarantees store_versions.ts's wipe side-effect runs before this
|
||||
// PersistedState reads from localStorage (ES module execution order).
|
||||
serialize: (value) =>
|
||||
JSON.stringify({ ...value, __version: AE_PRES_MGMT_LOC_VERSION }),
|
||||
deserialize: (raw: string) => {
|
||||
const { __version, ...stored } = JSON.parse(raw);
|
||||
return { ...pres_mgmt_loc_defaults, ...stored };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -86,10 +86,6 @@ export interface PressMgmtRemoteCfg {
|
||||
// Fields synced from remote are overwritten on every event load when lock_config=true.
|
||||
// ---------------------------------------------------------------------------
|
||||
export interface PresMgmtLocState {
|
||||
// --- System / lock state (mirrored from remote for display) ---
|
||||
sync_local_config: boolean;
|
||||
lock_config: boolean;
|
||||
|
||||
// --- Query / search preferences ---
|
||||
use_12h: boolean;
|
||||
qry_enabled: 'all' | 'not_enabled' | 'enabled';
|
||||
@@ -268,9 +264,6 @@ export interface PresMgmtSessState {
|
||||
|
||||
// Persisted pres_mgmt local config — survives browser sessions.
|
||||
export const pres_mgmt_loc_defaults: PresMgmtLocState = {
|
||||
// System / lock state
|
||||
sync_local_config: false,
|
||||
lock_config: false,
|
||||
|
||||
// Query / search
|
||||
use_12h: true,
|
||||
|
||||
@@ -120,11 +120,16 @@ if (
|
||||
_check_and_wipe('ae_loc', AE_LOC_VERSION);
|
||||
_check_and_wipe('ae_events_loc', AE_EVENTS_LOC_VERSION);
|
||||
_check_and_wipe('ae_idaa_loc', AE_IDAA_LOC_VERSION);
|
||||
// ae_leads_loc (PersistedState, runed) stamps __version itself in its custom
|
||||
// serializer — see ae_events_stores__leads.svelte.ts. FIXED 2026-06-16: this was
|
||||
// previously bare JSON.stringify with no __version field, so this check always saw
|
||||
// undefined !== expected and wiped ae_leads_loc on EVERY page load.
|
||||
_check_and_wipe('ae_leads_loc', AE_LEADS_LOC_VERSION);
|
||||
// ae_pres_mgmt_loc uses PersistedState (runed) which stores raw JSON without a __version
|
||||
// field. The _check_and_wipe mechanism requires __version in the stored data — do NOT
|
||||
// add it here until pres_mgmt_loc_defaults includes __version. For now the key is new
|
||||
// (no stale data exists) so no wipe is needed.
|
||||
// ae_pres_mgmt_loc (PersistedState, runed) stamps __version itself in its custom
|
||||
// serializer — see ae_events_stores__pres_mgmt.svelte.ts. Importing that module runs
|
||||
// this file's side effects first (ES module order), so this check sees real stored
|
||||
// data, not a race against the PersistedState constructor.
|
||||
_check_and_wipe('ae_pres_mgmt_loc', AE_PRES_MGMT_LOC_VERSION);
|
||||
}
|
||||
|
||||
function _check_and_wipe(key: string, expected_version: number): void {
|
||||
|
||||
@@ -147,7 +147,23 @@ async function save() {
|
||||
fields: { mod_pres_mgmt_json: draft },
|
||||
log_lvl: 1
|
||||
});
|
||||
// Reload the event so the sync function picks up the new config
|
||||
|
||||
// Sync this browser's pres_mgmt_loc immediately from the just-saved draft.
|
||||
// WHY: load_ae_obj_id__event() below is SWR — it returns the stale Dexie
|
||||
// cache right away and refreshes from the API in the background (fire and
|
||||
// forget), so it cannot be relied on to update pres_mgmt_loc in time. Calling
|
||||
// the sync function directly here makes the editor's own browser update
|
||||
// deterministically, with no race against Dexie/liveQuery propagation.
|
||||
events_func.sync_config__event_pres_mgmt({
|
||||
pres_mgmt_cfg_remote: draft,
|
||||
log_lvl: 1
|
||||
});
|
||||
|
||||
// Also kick off the normal SWR reload (try_cache: true, the default) so
|
||||
// Dexie gets the fresh record in the background — this is what lets OTHER
|
||||
// browsers/tabs pick up the change next time they query Dexie. Do not pass
|
||||
// try_cache: false here — that skips the Dexie write entirely (see
|
||||
// GUIDE__SvelteKit2_Svelte5_DexieJS.md "try_cache: false Bug").
|
||||
await events_func.load_ae_obj_id__event({
|
||||
api_cfg: $ae_api,
|
||||
event_id: event_id,
|
||||
|
||||
Reference in New Issue
Block a user