From 7ea789c0787efd627267d5ef7f86ebfd6403beec Mon Sep 17 00:00:00 2001 From: boban Date: Sun, 14 Jun 2026 19:40:24 +0200 Subject: [PATCH] fix(ui): re-sync active server on every details-page request (bfcache-safe) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a hydrate() hook so Show re-asserts its server as active on the wire:poll tick and any action — covering a wire:navigate bfcache restore that skips mount(), without the earlier racy client-side POST. Idempotent + server-side, no race. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/Livewire/Servers/Show.php | 22 ++++++++++++++++++---- tests/Feature/ServerContextSyncTest.php | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/Livewire/Servers/Show.php b/app/Livewire/Servers/Show.php index 04460e1..e79acae 100644 --- a/app/Livewire/Servers/Show.php +++ b/app/Livewire/Servers/Show.php @@ -61,15 +61,29 @@ class Show extends Component */ public function mount(): void { - // Viewing a server's details makes it the ACTIVE server, so the sidebar switcher - // (and the server-scoped sections) always reflect the server on screen — no drift - // between the URL subject and the sidebar selection. - session(['active_server_id' => $this->server->id]); + $this->syncActiveServer(); // identity + gauges render from the persisted row immediately; // the live snapshot loads lazily via wire:init -> load(). } + /** + * Re-assert this server as the ACTIVE one on EVERY request to this component (mount, + * the wire:poll tick, any action). This is what keeps the sidebar switcher + scoped + * sections in sync with the server on screen — and it also covers a wire:navigate + * bfcache restore (Back/Forward), which skips mount() but still hits the server on + * the next poll/interaction. Server-side + idempotent, so there is no request race. + */ + public function hydrate(): void + { + $this->syncActiveServer(); + } + + private function syncActiveServer(): void + { + session(['active_server_id' => $this->server->id]); + } + /** Lazy: pull the full SSH snapshot after the shell renders, behind skeletons. */ public function load(FleetService $fleet): void { diff --git a/tests/Feature/ServerContextSyncTest.php b/tests/Feature/ServerContextSyncTest.php index cd4610b..ce0f0b6 100644 --- a/tests/Feature/ServerContextSyncTest.php +++ b/tests/Feature/ServerContextSyncTest.php @@ -29,6 +29,22 @@ class ServerContextSyncTest extends TestCase $this->assertSame($b->id, session('active_server_id')); } + public function test_interacting_with_a_details_page_resyncs_active_server(): void + { + // Simulates a bfcache restore where mount() did not run and the session points + // elsewhere: the next request to the component (poll/action → hydrate) re-syncs. + $this->actingAs(User::factory()->create()); + $a = $this->server('A'); + $b = $this->server('B'); + + $component = Livewire::test(Show::class, ['server' => $a]); + session(['active_server_id' => $b->id]); // drift (as after Back/Forward) + + $component->call('pollMetrics'); + + $this->assertSame($a->id, session('active_server_id')); + } + public function test_switching_on_a_details_page_targets_the_new_server(): void { $a = $this->server('A');