Files
decePubClient/Services/OverlayService.cs
T
thepraandClaude Opus 5.5 f1ac5eaad2 Back closes what is open
On a phone the Back button left the page under an open menu, sheet or dialog. An open overlay now says so (new
IDOverlayHost, cascaded by MainLayout; OverlayService keeps them newest last): DModal and DDropdown through the
cascade, so the D components stay free of app services, and the nav menu, PhoneSheet and the full-screen globe through
the service. MainLayout's location-changing handler lets Back close the newest overlay instead of leaving the page; a
link followed or the app's own navigation (signing out after its question, say) closes them all and goes on. Blazor
can't tell Back from the app's navigation, so a capture-phase popstate listener in main.js marks Back
(AppStatusService.TookBack). No history entries are pushed. Phone flow: Back closes the menu, a card's More sheet, the
composer and the full-screen globe one after another without leaving the feed, and with nothing open goes back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:49:49 +02:00

65 lines
1.3 KiB
C#

using decePubClient.Components.Daisy;
namespace decePubClient.Services
{
/// <summary>
/// The overlays open now, newest last (IDOverlayHost): MainLayout closes the newest when the Back button is pressed
/// while one is open, and all of them when the app goes elsewhere.
/// </summary>
public class OverlayService : IDOverlayHost
{
readonly List<Entry> open = [];
public bool HasOpen
{
get
{
lock (open)
return open.Count > 0;
}
}
public IDisposable Open(Func<Task> close)
{
var entry = new Entry(this, close);
lock (open)
open.Add(entry);
return entry;
}
public async Task CloseTop(CancellationToken cancellationToken = default)
{
Entry top;
lock (open)
top = open.LastOrDefault();
if (top is null)
return;
top.Dispose();
await top.Close();
}
public async Task CloseAll(CancellationToken cancellationToken = default)
{
Entry[] all;
lock (open)
{
all = [.. open];
open.Clear();
}
foreach (var entry in all.Reverse())
await entry.Close();
}
sealed class Entry(OverlayService owner, Func<Task> close) : IDisposable
{
public Func<Task> Close { get; } = close;
public void Dispose()
{
lock (owner.open)
owner.open.Remove(this);
}
}
}
}