路由从 hash 换成 History API(pushState/replaceState/popstate),不再依赖 location.hash
HashNavigation 改名 Navigation(src/lib/navigation.js),接口不变,内部改用真实路径: parseUrlHash/composeUrlHash 改名 parseUrl/composeUrl;navigateTo/navigateReplace 改调 pushState/replaceState 后手动派发 popstate(pushState/replaceState 不会像赋值 location.hash 那样自动触发事件,这里复用 navigateReplace 早就在用的同一个手动 dispatch 手法);goBack() 不用改,history.back() 本来就是真实浏览器历史机制。新增 Navigation.currentUrl() 替代调用方 原本直接读的 window.location.hash(这个类从来没包装过这个读取)。 一处需要专门处理的边界情况:当 path[0] 是空字符串("不选中侧栏视图、只开一个会话"这个仓库里 的合法状态)时,拼出的真实路径开头会变成双斜杠,被浏览器当协议相对地址解析、试图跳到一个叫 会话id的host,pushState 直接抛 SecurityError。composeUrl/parseUrl 内部用占位 token "_" 处理 这种情况,对外的调用方(19个文件)完全无感。 tinode-web.jsx 的 handleHashRoute 改名 handleRouteChange,监听从 hashchange 换成 popstate; service-worker.js 的推送通知点击跳转手动改写成同样的路径格式(这个文件在打包之外单独执行, 没法 import Navigation)。index.html/index-dev.html 加了 <base href="/">——所有静态资源引用 都是相对路径,hash 路由下页面路径永远是 /,换真实路径后深层地址刷新会按当前目录解析出错误 资源地址导致白屏,这一行是标准修法。nginx.conf 的 try_files 兜底规则本来就适用两种路由,只 更新了注释。 实机验证:地址栏不再有 #;点击会话/设置等各类视图正常跳转;浏览器前进后退和应用内"返回" 按钮都正常;收藏页内部的类型 tab 切换(navigateReplace)确认不产生新的历史记录;在会话页/ 个人中心页刷新、直接用完整深层路径打开新标签页都能正确恢复到位,资源正常加载不白屏。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
97a6b88d59
commit
4e8f45d7d8
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Tinode } from 'tinode-sdk';
|
||||
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
import Navigation from '../lib/navigation.js';
|
||||
import { BASE_APP_NAME } from '../config.js';
|
||||
import { getBootstrap, getRealnameStatus, getSystemNotifications, markSystemNotificationRead } from '../lib/im-api.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
@@ -15,9 +15,9 @@ import { topicDisplayName } from '../lib/utils.js';
|
||||
// group-management-view.jsx 的同名辅助函数保持一致(不带 topic 时显式清掉,避免把上一个页面
|
||||
// 残留的 topic 参数带进不相关的页面)。
|
||||
const navigate = (view, topic) => {
|
||||
let hash = HashNavigation.setUrlSidePanel(window.location.hash, view);
|
||||
hash = topic ? HashNavigation.addUrlParam(hash, 'topic', topic) : HashNavigation.removeUrlParam(hash, 'topic');
|
||||
HashNavigation.navigateTo(hash);
|
||||
let hash = Navigation.setUrlSidePanel(Navigation.currentUrl(), view);
|
||||
hash = topic ? Navigation.addUrlParam(hash, 'topic', topic) : Navigation.removeUrlParam(hash, 'topic');
|
||||
Navigation.navigateTo(hash);
|
||||
};
|
||||
|
||||
// BottomNavigation's 消息<->工作台 switch is the app's root-level tab bar, not a navigation to a
|
||||
@@ -27,7 +27,7 @@ const navigate = (view, topic) => {
|
||||
// two bottom tabs) made the back button just cycle through past tab switches instead of leaving the
|
||||
// app, exactly like a standard bottom-tab-bar app's back button should not have to unwind every past
|
||||
// tab switch to back out.
|
||||
const navigateReplace = (view) => HashNavigation.navigateReplace(HashNavigation.setUrlSidePanel(window.location.hash, view));
|
||||
const navigateReplace = (view) => Navigation.navigateReplace(Navigation.setUrlSidePanel(Navigation.currentUrl(), view));
|
||||
|
||||
// Was a hardcoded FREQUENT_CONTACTS mock (5 fake names, always clicking through to the same
|
||||
// direct-message-view.jsx demo regardless of which avatar was tapped -- confirmed independently by
|
||||
@@ -501,7 +501,7 @@ function SystemMessages({authenticated}) {
|
||||
};
|
||||
|
||||
return <div className="system-messages-page">
|
||||
<header className="messages-sub-header"><button onClick={() => { if (!HashNavigation.goBack()) navigate('messages-home'); }} aria-label="返回"><Icon>arrow_back_ios_new</Icon></button><h1>系统消息</h1><span /></header>
|
||||
<header className="messages-sub-header"><button onClick={() => { if (!Navigation.goBack()) navigate('messages-home'); }} aria-label="返回"><Icon>arrow_back_ios_new</Icon></button><h1>系统消息</h1><span /></header>
|
||||
<main className="system-notification-list">
|
||||
{loading ? <p className="messages-empty"><Icon>hourglass_empty</Icon>加载中…</p> : null}
|
||||
{error ? <p className="messages-empty"><Icon>error_outline</Icon>{error}</p> : null}
|
||||
|
||||
Reference in New Issue
Block a user