路由从 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
+15
-15
@@ -49,7 +49,7 @@ import { topicDisplayName } from '../lib/utils.js';
|
||||
import { acceptGroupInvitation, checkURL, confirmRiskChallenge, createChatUploader, favoriteMessages, getBootstrap,
|
||||
getConversationContext, getFavorites, getFriendRequests, getGroupJoinRequests, getGroupMembers, getLatestGroupAnnouncement,
|
||||
getTopicDraft, recallMessage, updateTopicDraft } from '../lib/im-api.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
import Navigation from '../lib/navigation.js';
|
||||
import { bytesToHumanSize, relativeDateFormat, shortDateFormat } from '../lib/strformat.js';
|
||||
|
||||
// Run timer with this frequency (ms) for checking notification queue.
|
||||
@@ -725,7 +725,7 @@ class MessagesView extends React.Component {
|
||||
.then(ctrl => {
|
||||
if (ctrl.code == 303) {
|
||||
// Redirect to another topic requested.
|
||||
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));
|
||||
Navigation.navigateTo(Navigation.setUrlTopic('', ctrl.params.topic));
|
||||
return;
|
||||
}
|
||||
if (this.state.topic != ctrl.topic) {
|
||||
@@ -1441,28 +1441,28 @@ class MessagesView extends React.Component {
|
||||
}).catch(_ => {});
|
||||
}
|
||||
|
||||
// Note: setUrlSidePanel only overwrites path[0] -- called with window.location.hash while a topic is
|
||||
// Note: setUrlSidePanel only overwrites path[0] -- called with Navigation.currentUrl() while a topic is
|
||||
// open (path[1] is the open topic), it leaves that segment in place, producing e.g.
|
||||
// "#group-announcement-detail/grpXXX?topic=grpXXX". handleHashRoute then re-parses path[1] as a topic
|
||||
// selector on every hashchange, fighting with the sidepanel that's trying to open and leaving the
|
||||
// whole view blank. Build the target hash from an empty path instead of the current one.
|
||||
// "/group-announcement-detail/grpXXX?topic=grpXXX". handleRouteChange then re-parses path[1] as a topic
|
||||
// selector on every route change, fighting with the sidepanel that's trying to open and leaving the
|
||||
// whole view blank. Build the target URL from an empty path instead of the current one.
|
||||
handleShowAnnouncement(e) {
|
||||
e.preventDefault();
|
||||
// `from=chat` lets Announcement (group-management-view.jsx) tell this entry point apart from
|
||||
// the one via group-settings -- both land on the identical path[0]/topic hash otherwise, so
|
||||
// without it the "back" button can't know whether to return to group-settings or to this chat.
|
||||
let hash = HashNavigation.addUrlParam(HashNavigation.setUrlSidePanel('', 'group-announcement-detail'), 'topic', this.state.topic);
|
||||
hash = HashNavigation.addUrlParam(hash, 'from', 'chat');
|
||||
HashNavigation.navigateTo(hash);
|
||||
let hash = Navigation.addUrlParam(Navigation.setUrlSidePanel('', 'group-announcement-detail'), 'topic', this.state.topic);
|
||||
hash = Navigation.addUrlParam(hash, 'from', 'chat');
|
||||
Navigation.navigateTo(hash);
|
||||
}
|
||||
|
||||
handleShowJoinRequests(e) {
|
||||
e.preventDefault();
|
||||
// `from=chat` mirrors handleShowAnnouncement above -- lets JoinRequests (group-management-view.jsx)
|
||||
// return to this chat instead of always landing on group-governance.
|
||||
let hash = HashNavigation.addUrlParam(HashNavigation.setUrlSidePanel('', 'group-join-requests'), 'topic', this.state.topic);
|
||||
hash = HashNavigation.addUrlParam(hash, 'from', 'chat');
|
||||
HashNavigation.navigateTo(hash);
|
||||
let hash = Navigation.addUrlParam(Navigation.setUrlSidePanel('', 'group-join-requests'), 'topic', this.state.topic);
|
||||
hash = Navigation.addUrlParam(hash, 'from', 'chat');
|
||||
Navigation.navigateTo(hash);
|
||||
}
|
||||
|
||||
handleSendFriendVerification(e) {
|
||||
@@ -1471,8 +1471,8 @@ class MessagesView extends React.Component {
|
||||
// current hash, since a topic is open) -- this one additionally never set `uid`, which the
|
||||
// friend-verification page needs (contact-management-view.jsx's Verification reads hashParams.uid)
|
||||
// to know who the verification is for; for a P2P topic that's simply the topic name itself.
|
||||
HashNavigation.navigateTo(HashNavigation.addUrlParam(
|
||||
HashNavigation.setUrlSidePanel('', 'friend-verification'), 'uid', this.state.topic));
|
||||
Navigation.navigateTo(Navigation.addUrlParam(
|
||||
Navigation.setUrlSidePanel('', 'friend-verification'), 'uid', this.state.topic));
|
||||
}
|
||||
|
||||
// 6.3 加入群聊.png:被邀请人在跟邀请人的会话里收到一张"邀请你加入群聊……立即加入"的卡片。
|
||||
@@ -2239,7 +2239,7 @@ class MessagesView extends React.Component {
|
||||
if (!topic || !seq || isNaN(seq)) {
|
||||
return;
|
||||
}
|
||||
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'jumpseq'));
|
||||
Navigation.navigateTo(Navigation.removeUrlParam(Navigation.currentUrl(), 'jumpseq'));
|
||||
const jump = () => window.setTimeout(() => this.handleQuoteClick(seq), 150);
|
||||
if (topic.findMessage(seq)) {
|
||||
jump();
|
||||
|
||||
Reference in New Issue
Block a user