mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
fix(workspace): lead header Back to a parent route when the page opened the session (#1830)
* fix(workspace): lead header Back to a parent route when the page opened the session Settings, Discover, actor and in-portal search registered a header Back that only ran Location.back(). As the first entry of the session (deep link, reload, restored view) that did nothing in Electron and left the app in a browser. WorkspaceBackNavigationService.back(resolveParent) keeps Location.back() while the previous entry is an in-app one, and while that is unknown because the Navigation API is missing. Otherwise it opens the page's parent with replaceUrl, so history Back cannot return to the page: - Settings: the first workspace view (resolveDashboardPath()). - Discover: the catalog section it lists (vod for movies, series for TV). - Actor and search: the portal root, which redirects to its default section within the same navigation. The web E2E opens these pages in a fresh tab: a page.goto in the same tab leaves the previous document behind, often at the parent's URL, so history Back passed without the fix. Electron covers settings after a window reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(workspace): lead first-entry Back to the parent without the Navigation API Review follow-ups (Greptile): - Without the Navigation API (older Safari and Firefox) back() always called Location.back(), so a page that opened the session still left the app. The service now tracks the router's in-app history depth there (trackRouterHistoryDepth): first navigation 0, push +1, replacement keeps it, a traversal restores the depth recorded for its entry. Depth 0 opens the parent; an unknown depth (an entry from before a reload) keeps Location.back(). - Stalker's Discover (movie/tv section), actor and search pages now have tests that they hand the service the parent under the portal :id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(workspace): adopt the router navigation the Back depth tracker missed Review follow-up (Codex, Greptile): the lazy workspace shell creates the Back service after the first NavigationStart, so the tracker saw only its NavigationEnd, left the depth unknown and counted the next push as the first entry. It now adopts the router's current or last successful navigation when it starts: a first navigation is depth 0, a later one leaves the depth unknown (browser history Back), and a late start of the adopted navigation is not counted again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
23 files changed
+940
-55
No files matched your search
@@ -195,7 +195,10 @@ Page-level Back lives only in the workspace header's leading slot (see
|
||||
[Header Back](./workspace-shell.md#header-back)). A routed page, or the shell
|
||||
it renders in, registers it with `registerWorkspaceBack()` instead of drawing
|
||||
an arrow, so Back keeps one position and one look on every page and never
|
||||
floats over a scroll owner. Without a registration the header falls back to
|
||||
floats over a scroll owner. A page whose Back is history Back calls
|
||||
`WorkspaceBackNavigationService.back()` with its parent route rather than
|
||||
`Location.back()`, so Back still leads somewhere when the page opened the
|
||||
session. Without a registration the header falls back to
|
||||
browser history while an in-app previous page exists, and shows nothing
|
||||
otherwise. An arrow that returns within a menu, dialog or player panel is not
|
||||
page navigation and stays in that surface; an error state may repeat the
|
||||
|
||||
@@ -146,9 +146,9 @@ label (else the translated "Back"), whether Escape on the page runs it, and
|
||||
| Page | Registered by | Back runs | ≤640 px |
|
||||
| --- | --- | --- | --- |
|
||||
| Portal, collection, offline and recording details | `PortalDetailShellComponent` while `backAvailable()` | the host's `backClicked` | replaces the drawer toggle |
|
||||
| Xtream and Stalker Discover and actor pages | `DiscoverViewComponent`, `ActorViewComponent` | the route's `Location.back()` | (no drawer) |
|
||||
| In-portal search, Xtream and Stalker | `SearchLayoutComponent` while `backAvailable()` and no inline detail replaces the results | `Location.back()` | (no drawer) |
|
||||
| Settings | `WorkspaceSettingsContextPanelComponent`, which exists exactly while the settings route shows | `Location.back()` | beside the drawer toggle |
|
||||
| Xtream and Stalker Discover and actor pages | `DiscoverViewComponent`, `ActorViewComponent` | the route's history Back; parent: the catalog section Discover lists (`vod` for movies, `series` for TV), the portal's default section for actor | (no drawer) |
|
||||
| In-portal search, Xtream and Stalker | `SearchLayoutComponent` while `backAvailable()` and no inline detail replaces the results | history Back; parent: the portal's default section | (no drawer) |
|
||||
| Settings | `WorkspaceSettingsContextPanelComponent`, which exists exactly while the settings route shows | history Back; parent: the first workspace view (`WorkspaceStartupPreferencesService.resolveDashboardPath()`: the dashboard, or sources when it is hidden) | beside the drawer toggle |
|
||||
|
||||
Detail-page semantics (Escape, browse and watch) are in
|
||||
[Portal Detail Navigation](./portal-detail-navigation.md#detail-scroll-and-focus).
|
||||
@@ -167,6 +167,31 @@ advertises no Escape, because no page handles one for it. Pages that set
|
||||
`backAvailable=false`, such as M3U details, therefore show it too when they
|
||||
were reached by navigation.
|
||||
|
||||
**Parent fallback.** "Parent" in the table above: a registered page whose
|
||||
Back is history Back calls `WorkspaceBackNavigationService.back(resolveParent)`
|
||||
instead of `Location.back()`. It runs `Location.back()` while the previous
|
||||
entry is an in-app one, by the same Navigation API test as the history
|
||||
fallback. Without the API (older Safari and Firefox) the router's history
|
||||
depth decides (`trackRouterHistoryDepth`): the document's first navigation is
|
||||
depth 0, a push adds one, a replacement keeps it and a traversal restores the
|
||||
depth recorded for its entry. The lazy workspace shell creates the service
|
||||
after the first navigation began, so the tracker adopts the router's current
|
||||
or last navigation: a first one is depth 0, a later one leaves the depth
|
||||
unknown. A traversal to an entry from before a reload
|
||||
leaves the depth unknown and keeps `Location.back()`, which then has a
|
||||
previous entry. Otherwise the page opened
|
||||
the session (a deep link, a reload or a restored view), where
|
||||
`Location.back()` does nothing in Electron and leaves the app in a browser.
|
||||
The service then navigates to the page's parent with `replaceUrl`, so history
|
||||
Back cannot return to the page just left; with nothing in-app before it, the
|
||||
parent shows no history fallback. The resolver returns a URL or router commands, may be asynchronous, and
|
||||
returns null when the page knows no parent, which keeps `Location.back()`.
|
||||
Portal pages build their parent with `workspacePortalCommands()`
|
||||
(`@iptvnator/portal/shared/util`) from the route's `:id`; without a section,
|
||||
the portal route's `redirectTo` picks the default section within the same
|
||||
navigation, so the replacement still applies. Detail pages keep their own
|
||||
return logic (`backClicked`).
|
||||
|
||||
When there is nowhere to go, the slot is empty rather than a disabled arrow.
|
||||
Sessions often start on a page that never navigates (an M3U playlist or live
|
||||
TV), where a disabled arrow would stay for the whole session. The cost is one
|
||||
|
||||
Reference in new issue
Block a user