Skip to content

Commit

Permalink
🐛 fix(PageStack): GoBackToPageAndReplace has subtle issues (#2316)
Browse files Browse the repository at this point in the history
  • Loading branch information
capdiem authored Jan 17, 2025
1 parent a65f703 commit 347ac13
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 17 deletions.
2 changes: 1 addition & 1 deletion docs/Masa.Blazor.Docs/Pages/PageStackPage3.razor
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</a>
<br />
<a class="text-decoration-underline"
@onclick="@(() => NavController.GoBackToPage("/blazor/examples/page-stack/page2/xyz", "/blazor/examples/page-stack/page2/abc"))">
@onclick="@(() => NavController.GoBackToPageAndReplace("/blazor/examples/page-stack/page2/xyz", "/blazor/examples/page-stack/page2/abc"))">
Go back to page 2 and replace with /abc
</a>

Expand Down
65 changes: 49 additions & 16 deletions src/Masa.Blazor/Presets/PageStack/PPageStack.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,19 @@ public partial class PPageStack : PatternPathComponentBase
/// and clear the stack in the next popstate event.
/// </summary>
private string? _uriForPushAndClearStack;

/// <summary>
/// The flag to indicate whether to replace the top page
/// and clear the previous pages in the stack in the next popstate event.
/// </summary>
private (string relativeUri, object? state)? _uriForReplaceAndClearStack;

/// <summary>
/// The flag to indicate whether to go back to a specific page
/// and then replace it with a new page.
/// </summary>
private (string relativeUri, object? state, int delta)? _uriForGoBackToAndReplace;

private string? _lastVisitedTabPath;
private PageType _targetPageType;
private long _lastOnPreviousClickTimestamp;
Expand Down Expand Up @@ -133,7 +139,7 @@ public void Popstate(string absolutePath)

_ = Task.Run(async () =>
{
await Task.Delay(300); // wait for the transition to complete
await Task.Delay(DelayForPageClosingAnimation); // wait for the transition to complete
Pages.RemoveRange(0, Pages.Count - 1);
await InvokeAsync(StateHasChanged);
});
Expand All @@ -144,13 +150,23 @@ public void Popstate(string absolutePath)
if (_uriForReplaceAndClearStack.HasValue)
{
var (relativeUri, state) = _uriForReplaceAndClearStack.Value;

Pages.RemoveRange(0, Pages.Count - 1);
InternalReplaceHandler(relativeUri, state);
_uriForReplaceAndClearStack = null;
return;
}

if (_uriForGoBackToAndReplace.HasValue)
{
var (replaceUri, state, delta) = _uriForGoBackToAndReplace.Value;
NavigationManager.Replace(replaceUri);
replaceUri = GetAbsolutePath(replaceUri);
CloseTopPages(delta, state, replaceUri);
_uriForGoBackToAndReplace = null;
return;
}

var tabbedPattern = _cachedTabbedPatterns.FirstOrDefault(r => r.IsMatch(absolutePath));

if (tabbedPattern is not null)
Expand Down Expand Up @@ -240,9 +256,9 @@ private void InternalStackStackNavManagerOnStackPush(object? sender, PageStackPu
NavigationManager.NavigateTo(e.RelativeUri);
}

private async void InternalStackStackNavManagerOnStackClear(object? sender, PageStackClearEventArgs e)
private void InternalStackStackNavManagerOnStackClear(object? sender, PageStackClearEventArgs e)
{
await Js.InvokeVoidAsync(JsInteropConstants.HistoryGo, -Pages.Count);
_ = Js.InvokeVoidAsync(JsInteropConstants.HistoryGo, -Pages.Count);

var backToLastVisitTab = string.IsNullOrWhiteSpace(e.RelativeUri) ||
_lastVisitedTabPath == GetAbsolutePath(e.RelativeUri);
Expand All @@ -268,17 +284,18 @@ private async void InternalPageStackNavManagerOnStackGoBackTo(object? sender, Pa
return;
}

_popstateByUserAction = true;

await Js.InvokeVoidAsync(JsInteropConstants.HistoryGo, -delta);

CloseTopPages(delta, e.State);

if (!string.IsNullOrWhiteSpace(e.ReplaceUri))
if (string.IsNullOrWhiteSpace(e.ReplaceUri))
{
await Task.Delay(DelayForPageClosingAnimation);
InternalReplaceHandler(e.ReplaceUri, e.State);
_popstateByUserAction = true;

CloseTopPages(delta, e.State);
}
else
{
_uriForGoBackToAndReplace = (e.ReplaceUri, e.State, delta);
}

_ = Js.InvokeVoidAsync(JsInteropConstants.HistoryGo, -delta);
}

private string GetAbsolutePath(string relativeUri) => NavigationManager.ToAbsoluteUri(relativeUri).AbsolutePath;
Expand Down Expand Up @@ -308,29 +325,45 @@ private void HandleOnPrevious()

private void CloseTopPageOfStack(object? state = null) => CloseTopPages(1, state);

private void CloseTopPages(int count, object? state = null)
private void CloseTopPages(int count, object? state = null, string? absolutePath = null)
{
if (!Pages.TryPeek(out var current)) return;

// Set the stacked state to false, which directly determines whether the dialog is displayed
// and will perform a transition animation
current.Stacked = false;

// When the count of pages to be deleted is greater than 1,
// delete the pages between the top of the stack and the target page
// For example, if the stack has 5 pages: [1,2,3,4,5], delete 3(count) pages,
// then delete the page 3 and page 4
if (count > 1)
{
Pages.RemoveRange(Pages.Count - count, count - 1);
}

// The stack is now [1,2,5], update the state of the 2nd page
if (Pages.TryPeekSecondToLast(out var target))
{
target.UpdateState(state);

if (!string.IsNullOrWhiteSpace(absolutePath))
{
target.UpdatePath(absolutePath);
}

target.Activate();
}

InvokeAsync(StateHasChanged);

Task.Run(async () =>
{
await Task.Delay(DelayForPageClosingAnimation); // wait for the transition to complete
// wait for a transition animation from left to right to complete,
// known animation time is 300 ms
await Task.Delay(DelayForPageClosingAnimation);

// Remove page 5 and display page 2
Pages.Pop();

if (Pages.Count == 0)
Expand Down

0 comments on commit 347ac13

Please sign in to comment.