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
This commit is contained in:
1 parent
5522fe4fbb
commit
f1ac5eaad2
12 files changed
+290
-17
No files matched your search
@@ -42,6 +42,9 @@ public class AppStatusService(IJSRuntime jsRuntime)
|
||||
|
||||
public bool IsMobileMedia() => JsSyncRuntime.Invoke<bool>("isMobileMedia");
|
||||
|
||||
// whether the navigation being decided came from the Back (or Forward) button
|
||||
public bool TookBack() => JsSyncRuntime.Invoke<bool>("tookBack");
|
||||
|
||||
// whether the pointer can hover (a mouse); a touch screen can't, so what shows on hover there opens on a tap
|
||||
public bool CanHover() => JsSyncRuntime.Invoke<bool>("canHover");
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user