fix(launcher): fix cfg modal default-open and outside-click persistence
Two bugs in the Launcher Config Modal after the Drawer→Modal migration: 1. Pre-existing persisted configs (missing hide_drawer__cfg field) caused !undefined = true, opening the modal on every fresh load. Fixed by adding a field-level initialization guard after the full-object guard. 2. $-syntax writes inside untrack() were suppressed by svelte-persisted-store, so outside-click closure was never persisted. Fixed by using events_loc.update() directly to ensure the write reaches localStorage serialization. Added equality guard to effect 1 to prevent spurious modal flicker from whole-store re-fires. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,36 @@ if (!$events_loc?.launcher) {
|
||||
hide_drawer__debug: true
|
||||
};
|
||||
}
|
||||
// WHY: The initialization block above only runs when launcher is completely absent.
|
||||
// If the user has an older persisted config (from before the Modal migration),
|
||||
// hide_drawer__cfg may be missing → undefined → !undefined = true → modal opens
|
||||
// on every load. Explicitly initialize it here to ensure it is always a boolean.
|
||||
if ($events_loc.launcher.hide_drawer__cfg === undefined) {
|
||||
$events_loc.launcher.hide_drawer__cfg = true;
|
||||
}
|
||||
|
||||
let modal_cfg_open = $state(!$events_loc.launcher.hide_drawer__cfg);
|
||||
|
||||
// Sync store → modal: biohazard button writes hide_drawer__cfg = false to open.
|
||||
// Equality guard prevents spurious writes from unrelated $events_loc updates
|
||||
// (Svelte 4 whole-store subscription fires on every field write to the store).
|
||||
$effect(() => {
|
||||
const should_open = !$events_loc.launcher.hide_drawer__cfg;
|
||||
if (modal_cfg_open !== should_open) {
|
||||
modal_cfg_open = should_open;
|
||||
}
|
||||
});
|
||||
|
||||
// Sync modal → store: use events_loc.update() directly rather than $-syntax so
|
||||
// the write always reaches the persisted store's serialization. $-syntax writes
|
||||
// inside $effect contexts may be suppressed and not trigger localStorage persistence.
|
||||
$effect(() => {
|
||||
const should_hide = !modal_cfg_open;
|
||||
events_loc.update((loc) => {
|
||||
if (loc.launcher) loc.launcher.hide_drawer__cfg = should_hide;
|
||||
return loc;
|
||||
});
|
||||
});
|
||||
|
||||
// Generate a stable per-device client ID on first load and persist it.
|
||||
// events_loc is backed by svelte-persisted-store (localStorage) so this
|
||||
@@ -898,59 +928,41 @@ $effect(() => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Drawer
|
||||
dismissable={false}
|
||||
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
||||
class="w-full border border-gray-300 bg-orange-50 opacity-90 transition-all duration-300 hover:opacity-97 md:w-96 lg:w-[32rem] dark:border-gray-600 dark:bg-slate-800"
|
||||
placement="left"
|
||||
{...{
|
||||
transitionType: 'fly',
|
||||
transitionParams: {
|
||||
x: -520,
|
||||
duration: 200,
|
||||
easing: sineIn
|
||||
}
|
||||
}}
|
||||
bind:hidden={$events_loc.launcher.hide_drawer__cfg}
|
||||
id="sidebar1">
|
||||
<!-- Stop-propagation wrapper: prevents clicks inside the visual panel from
|
||||
bubbling up to the <dialog> element. The onclick on the <Drawer> above
|
||||
is spread through to the native <dialog> by Flowbite, overriding its
|
||||
broken outsideclose detection. With this wrapper, ONLY genuine backdrop
|
||||
clicks (outside the visible panel) reach the dialog and close the drawer. -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
||||
<div role="presentation" onclick={(e) => e.stopPropagation()}>
|
||||
<Launcher_cfg></Launcher_cfg>
|
||||
<Modal
|
||||
bind:open={modal_cfg_open}
|
||||
autoclose={false}
|
||||
outsideclose
|
||||
size="xl"
|
||||
class="relative flex flex-col items-center justify-center p-0 overflow-hidden"
|
||||
id="modal_cfg">
|
||||
<Launcher_cfg></Launcher_cfg>
|
||||
|
||||
<hr class="my-2 border-gray-300 dark:border-gray-600" />
|
||||
|
||||
<div
|
||||
class="flex max-w-md flex-row flex-wrap items-center justify-center gap-0.5">
|
||||
<div
|
||||
class="bg-surface-100-900 flex max-w-full flex-row flex-wrap items-center justify-center gap-2 border-t border-surface-500/10 p-4">
|
||||
<a
|
||||
href="/events/{$events_slct.event_id}"
|
||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||
<Search size="1em" class="m-1" />
|
||||
Session Search
|
||||
</a>
|
||||
{#if $events_slct?.event_location_id}
|
||||
<a
|
||||
href="/events/{$events_slct.event_id}"
|
||||
href="/events/{$events_slct.event_id}/location/{$events_slct.event_location_id}"
|
||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||
<Search size="1em" class="m-1" />
|
||||
Session Search
|
||||
<MapPin size="1em" class="m-1" />
|
||||
View Selected Location
|
||||
</a>
|
||||
{#if $events_slct?.event_location_id}
|
||||
<a
|
||||
href="/events/{$events_slct.event_id}/location/{$events_slct.event_location_id}"
|
||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||
<MapPin size="1em" class="m-1" />
|
||||
View Selected Location
|
||||
</a>
|
||||
{/if}
|
||||
{#if $events_slct?.event_session_id}
|
||||
<a
|
||||
href="/events/{$events_slct.event_id}/session/{$events_slct.event_session_id}"
|
||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||
<GraduationCap size="1em" class="m-1" />
|
||||
View Selected Session
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if $events_slct?.event_session_id}
|
||||
<a
|
||||
href="/events/{$events_slct.event_id}/session/{$events_slct.event_session_id}"
|
||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||
<GraduationCap size="1em" class="m-1" />
|
||||
View Selected Session
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</Drawer>
|
||||
</Modal>
|
||||
|
||||
<Drawer
|
||||
activateClickOutside={false}
|
||||
|
||||
Reference in New Issue
Block a user