From 76cf41e7a95263aae56a5a40e000eb9e1021a2a1 Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 19 May 2026 15:32:14 -0700 Subject: [PATCH] fix(ui): open sync-setup modal when Connect clicked from Settings The sync-setup modal was silently blocked by its own guard: other_modal_scrims checks for any ModalScrim without SyncSetupScreen, but the Settings panel IS a ModalScrim, so clicking Connect from within Settings always hit the guard and returned early. Two fixes: - handle_sync_buttons: set SettingsScreen.0 = false when ConnectSync is pressed so settings closes as the event is fired - open_sync_setup_modal: exclude SettingsPanel from other_modal_scrims to handle the deferred-despawn timing window (settings scrim entity still exists in the world until command buffers flush at frame end) - Make SettingsPanel pub so sync_setup_plugin can reference it Co-Authored-By: Claude Sonnet 4.6 --- solitaire_engine/src/settings_plugin.rs | 10 ++++++++-- solitaire_engine/src/sync_setup_plugin.rs | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/solitaire_engine/src/settings_plugin.rs b/solitaire_engine/src/settings_plugin.rs index 3ea6d35..5a60169 100644 --- a/solitaire_engine/src/settings_plugin.rs +++ b/solitaire_engine/src/settings_plugin.rs @@ -94,7 +94,7 @@ pub struct SettingsChangedEvent(pub Settings); /// Marker on the root Settings panel entity. #[derive(Component, Debug)] -struct SettingsPanel; +pub struct SettingsPanel; /// Marks the `Text` node showing the live SFX volume value. #[derive(Component, Debug)] @@ -1137,6 +1137,7 @@ fn handle_sync_buttons( mut configure_sync: MessageWriter, mut logout_sync: MessageWriter, mut delete_account: MessageWriter, + mut screen: ResMut, ) { for (interaction, button) in &interaction_query { if *interaction != Interaction::Pressed { @@ -1144,7 +1145,12 @@ fn handle_sync_buttons( } match button { SettingsButton::SyncNow => { manual_sync.write(ManualSyncRequestEvent); } - SettingsButton::ConnectSync => { configure_sync.write(SyncConfigureRequestEvent); } + SettingsButton::ConnectSync => { + // Close settings before the sync-setup modal opens so the + // guard in open_sync_setup_modal doesn't block on our own scrim. + screen.0 = false; + configure_sync.write(SyncConfigureRequestEvent); + } SettingsButton::DisconnectSync => { logout_sync.write(SyncLogoutRequestEvent); } SettingsButton::DeleteAccount => { delete_account.write(DeleteAccountRequestEvent); } _ => {} diff --git a/solitaire_engine/src/sync_setup_plugin.rs b/solitaire_engine/src/sync_setup_plugin.rs index 92ef4bf..5e94723 100644 --- a/solitaire_engine/src/sync_setup_plugin.rs +++ b/solitaire_engine/src/sync_setup_plugin.rs @@ -52,7 +52,7 @@ use crate::events::{ SyncLogoutRequestEvent, }; use crate::font_plugin::FontResource; -use crate::settings_plugin::{SettingsResource, SettingsScreen, SettingsStoragePath}; +use crate::settings_plugin::{SettingsPanel, SettingsResource, SettingsScreen, SettingsStoragePath}; use crate::resources::TokioRuntimeResource; use crate::sync_plugin::SyncProviderResource; use crate::ui_modal::{spawn_modal, ModalScrim}; @@ -205,10 +205,14 @@ impl Plugin for SyncSetupPlugin { // --------------------------------------------------------------------------- /// Opens the sync-setup modal when `SyncConfigureRequestEvent` is received. +#[allow(clippy::type_complexity)] fn open_sync_setup_modal( mut events: MessageReader, existing: Query<(), With>, - other_modal_scrims: Query<(), (With, Without)>, + // Exclude SettingsPanel: the Connect button closes settings in the same + // frame it fires SyncConfigureRequestEvent, but Bevy despawns are deferred + // so the settings scrim still exists in the world during this system. + other_modal_scrims: Query<(), (With, Without, Without)>, mut commands: Commands, mut focused: ResMut, font_res: Option>,