From eade4e399a4a9fc2d1cf90b0daeb68fb6821923c Mon Sep 17 00:00:00 2001 From: thepra Date: Wed, 7 Oct 2026 02:14:42 +0200 Subject: [PATCH] The feed doesn't flash "Empty" while the persona is switched to LoadSession said the session was loaded before SwitchPersona had set the persona, so a render in between (any CascadingState change) showed the feed a loaded session without a persona, and it said "Empty" until the persona came. The session counts as loaded once the persona is known, or couldn't be used. StreamingTests took that flash for the feed and, on a slower start, posted before the stream was open; it now waits until the app has subscribed to the home stream. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw --- LayerComponents/CascadingState.razor | 5 ++++- tests/e2e/Actions/StreamingTests.cs | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/LayerComponents/CascadingState.razor b/LayerComponents/CascadingState.razor index 9cae022..89360e6 100644 --- a/LayerComponents/CascadingState.razor +++ b/LayerComponents/CascadingState.razor @@ -113,8 +113,11 @@ return; } - IsSessionLoaded = true; + // loaded once the persona is known (or couldn't be used): a page rendered while it is switched to would otherwise + // see a loaded session without one, and the feed would say it is empty await SwitchPersona(personaId, cancellationToken); + IsSessionLoaded = true; + StateHasChanged(); } /// Acts as another of the root's personas; every page showing persona data reloads. diff --git a/tests/e2e/Actions/StreamingTests.cs b/tests/e2e/Actions/StreamingTests.cs index 573883e..67a22a5 100644 --- a/tests/e2e/Actions/StreamingTests.cs +++ b/tests/e2e/Actions/StreamingTests.cs @@ -17,6 +17,18 @@ namespace decePubClient.E2E.Actions var text = $"streamed {Guid.NewGuid():N}"[..21]; await using var session = await SignIn.As(browser, persona); var page = session.Page; + // the stream counts once the app has subscribed to the home feed on it (it opens after the first page is read, + // later on a busy page) + var subscribed = new TaskCompletionSource(); + page.WebSocket += (_, socket) => + { + if (socket.Url.Contains("/api/v1/streaming")) + socket.FrameSent += (_, frame) => + { + if (frame.Text?.Contains("\"subscribe\"") == true && frame.Text.Contains("\"user\"")) + subscribed.TrySetResult(); + }; + }; JsonNode post = null; try { @@ -24,10 +36,12 @@ namespace decePubClient.E2E.Actions { await page.GotoAsync($"{Pasture.Site}/"); await page.EvaluateAsync("() => localStorage.removeItem('FeedFilter')"); + subscribed = new(); await page.GotoAsync($"{Pasture.Site}/"); await page.Locator("article[data-uri], [data-feed-empty]").First.WaitForAsync(new() { Timeout = 30000 }); - // the stream opens once the first page is shown - await Task.Delay(1500); + await subscribed.Task.WaitAsync(TimeSpan.FromSeconds(30)); + // (and the server has taken the subscription in) + await Task.Delay(500); post = await PersonaApi.Call(persona, "/api/v1/statuses", HttpMethod.Post, new JsonObject { ["status"] = text, ["visibility"] = "unlisted" }); var fresh = page.GetByRole(AriaRole.Button, new() { Name = "1 new" });