fix(ui): re-sync active server on every details-page request (bfcache-safe)

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) <noreply@anthropic.com>
feat/v1-foundation
boban 2026-06-14 19:40:24 +02:00
parent 0088122ff1
commit 7ea789c078
2 changed files with 34 additions and 4 deletions

View File

@ -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
{

View File

@ -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');