详情页返回按来源回到上一页并改用浏览器原生返回

- GameDetailPage 左上角「返回游戏广场」简化为「返回」,不再写死跳广场
- 详情/游玩页返回优先 window.history.back(),由 popstate 还原真实来源页(我的游戏/广场)
- activeAppPageRoutes 给应用历史条目补深度标记并新增 hasAppHistoryBackEntry()
- 深链直开等无可回退条目时用 replaceAppHistoryPath 兜底回广场或详情
- 新增 activeAppPageRoutes 深度语义用例与 PlatformEntryActiveFlowShell「游戏详情返回」两条用例
- 同步里程碑文档 2026-09-30 与 pitfalls 同日条目
This commit is contained in:
2026-09-30 13:05:55 +08:00
parent 0d3937b7eb
commit e976a69f9f
7 changed files with 155 additions and 12 deletions
+13
View File
@@ -3,6 +3,7 @@
import { describe, expect, it } from 'vitest';
import {
hasAppHistoryBackEntry,
isAppHistoryState,
isKnownMainAppPagePath,
pushAppHistoryPath,
@@ -75,4 +76,16 @@ describe('appPageRoutes', () => {
expect(window.location.pathname).toBe('/project');
expect(window.location.search).toContain('hostShell=tauri_desktop');
});
it('只有应用内推送过上一页时才算可原生返回', () => {
window.history.replaceState(null, '', '/games/detail?id=game-1');
expect(hasAppHistoryBackEntry()).toBe(false);
window.history.replaceState(null, '', '/games/mine');
pushAppHistoryPath('/games/detail?id=game-1');
expect(hasAppHistoryBackEntry()).toBe(true);
replaceAppHistoryPath('/games/detail?id=game-2');
expect(hasAppHistoryBackEntry()).toBe(true);
});
});
+26 -2
View File
@@ -26,6 +26,7 @@ const ROUTE_STAGE_BY_PATH = new Map(
const APP_RUNTIME_CONTEXT_QUERY_KEYS =
HOST_BRIDGE_PRESERVED_RUNTIME_CONTEXT_QUERY_KEYS;
export const APP_HISTORY_STATE_KEY = '__genarrativeAppHistoryEntry';
const APP_HISTORY_STATE_DEPTH_KEY = '__genarrativeAppHistoryDepth';
export function normalizeAppPath(pathname: string) {
const trimmedPathname = pathname.trim().toLowerCase();
@@ -92,7 +93,7 @@ function writeAppHistoryPath(path: string, mode: 'push' | 'replace') {
return;
}
const state = buildAppHistoryState();
const state = buildAppHistoryState(mode);
if (mode === 'replace') {
window.history.replaceState(state, '', nextRelativeUrl);
return;
@@ -108,14 +109,37 @@ export function isAppHistoryState(state: unknown) {
);
}
function buildAppHistoryState() {
/**
* 当前历史条目是不是应用内导航写入的、且存在可回退的上一页。
* 直接打开深链时浏览器条目不是应用写入的,此时返回需要走兜底路径,
* 否则 `history.back()` 会直接退出应用。
*/
export function hasAppHistoryBackEntry() {
const state = window.history.state;
if (!isAppHistoryState(state)) {
return false;
}
return readAppHistoryDepth(state as Record<string, unknown>) > 0;
}
function readAppHistoryDepth(state: Record<string, unknown>) {
const depth = state[APP_HISTORY_STATE_DEPTH_KEY];
return typeof depth === 'number' && Number.isFinite(depth) ? depth : 0;
}
function buildAppHistoryState(mode: 'push' | 'replace') {
const currentState =
window.history.state && typeof window.history.state === 'object'
? window.history.state
: {};
const currentDepth = readAppHistoryDepth(
currentState as Record<string, unknown>,
);
return {
...currentState,
[APP_HISTORY_STATE_KEY]: true,
[APP_HISTORY_STATE_DEPTH_KEY]:
mode === 'push' ? currentDepth + 1 : currentDepth,
};
}