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
-10
@@ -1,15 +1,47 @@
|
||||
@inherits LayoutComponentBase
|
||||
@inherits LayoutComponentBase
|
||||
@implements IDisposable
|
||||
|
||||
<div class="relative flex flex-col md:flex-row w-full h-full">
|
||||
<NavMenu />
|
||||
@* The page and its menu. The overlays it holds (dialogs, menus, sheets: IDOverlayHost, cascaded) close with the phone's
|
||||
Back button: while one is open, Back closes the newest instead of leaving the page; a link followed or the app's own
|
||||
navigation closes them all and goes on. *@
|
||||
<CascadingValue Value="(IDOverlayHost)Overlays" IsFixed="true">
|
||||
<div class="relative flex flex-col md:flex-row w-full h-full">
|
||||
<NavMenu />
|
||||
|
||||
<div class="block flex-1 relative w-full h-full">
|
||||
<div class="block absolute overflow-y-auto w-full h-full p-3 md:py-4 md:pl-2 md:pr-4">
|
||||
@Body
|
||||
<div class="block flex-1 relative w-full h-full">
|
||||
<div class="block absolute overflow-y-auto w-full h-full p-3 md:py-4 md:pl-2 md:pr-4">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ToastHost />
|
||||
<PublicNoticeHost />
|
||||
<ConfirmHost />
|
||||
<ToastHost />
|
||||
<PublicNoticeHost />
|
||||
<ConfirmHost />
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
[Inject] OverlayService Overlays { get; set; }
|
||||
[Inject] NavigationManager Navigation { get; set; }
|
||||
[Inject] AppStatusService Status { get; set; }
|
||||
|
||||
IDisposable changing;
|
||||
|
||||
protected override void OnInitialized() => changing = Navigation.RegisterLocationChangingHandler(OnLocationChanging);
|
||||
|
||||
async ValueTask OnLocationChanging(LocationChangingContext context)
|
||||
{
|
||||
var back = !context.IsNavigationIntercepted && Status.TookBack();
|
||||
if (!Overlays.HasOpen)
|
||||
return;
|
||||
if (back)
|
||||
{
|
||||
context.PreventNavigation();
|
||||
await Overlays.CloseTop(context.CancellationToken);
|
||||
return;
|
||||
}
|
||||
await Overlays.CloseAll(context.CancellationToken);
|
||||
}
|
||||
|
||||
public void Dispose() => changing?.Dispose();
|
||||
}
|
||||
+27
-6
@@ -141,7 +141,27 @@
|
||||
@code {
|
||||
[Inject] ConfirmService Confirm { get; set; }
|
||||
|
||||
[Inject] OverlayService Overlays { get; set; }
|
||||
|
||||
bool isMenuOpen;
|
||||
IDisposable overlay;
|
||||
|
||||
// open, the Back button closes it
|
||||
void SetMenu(bool open)
|
||||
{
|
||||
isMenuOpen = open;
|
||||
if (open && overlay is null)
|
||||
overlay = Overlays.Open(() => InvokeAsync(() =>
|
||||
{
|
||||
SetMenu(false);
|
||||
StateHasChanged();
|
||||
}));
|
||||
else if (!open && overlay is not null)
|
||||
{
|
||||
overlay.Dispose();
|
||||
overlay = null;
|
||||
}
|
||||
}
|
||||
bool isPersonaMenuOpen;
|
||||
|
||||
string PersonaBackground => CascadingState.Persona?.Header is { Length: > 0 } header && !header.Contains("/missing-")
|
||||
@@ -162,37 +182,38 @@
|
||||
{
|
||||
if (!isMenuOpen)
|
||||
return;
|
||||
isMenuOpen = false;
|
||||
SetMenu(false);
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
void CloseMenu() => isMenuOpen = false;
|
||||
void CloseMenu() => SetMenu(false);
|
||||
|
||||
void OnKeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Escape")
|
||||
isMenuOpen = false;
|
||||
SetMenu(false);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
Navigation.LocationChanged -= OnLocationChanged;
|
||||
overlay?.Dispose();
|
||||
await base.DisposeAsync();
|
||||
}
|
||||
|
||||
async Task SwitchPersona(string personaId)
|
||||
{
|
||||
isMenuOpen = false;
|
||||
SetMenu(false);
|
||||
var result = await CascadingState.SwitchPersona(personaId);
|
||||
if (result.IsInvalid)
|
||||
Toast.AddResponseError(result);
|
||||
}
|
||||
|
||||
private void ToggleNavMenu() => isMenuOpen = !isMenuOpen;
|
||||
private void ToggleNavMenu() => SetMenu(!isMenuOpen);
|
||||
|
||||
async Task Logout()
|
||||
{
|
||||
isMenuOpen = false;
|
||||
SetMenu(false);
|
||||
if (!await Confirm.Ask(Localizer["Log out?"], Localizer["Every persona of this login is signed out on this device."], Localizer["Logout"], true))
|
||||
return;
|
||||
Navigation.NavigateTo("logout");
|
||||
|
||||
Reference in new issue
Block a user