外观设置改为单页分区;发帖草稿与板块校验优化,WS 延迟释放避开 Strict Mode 误断。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-20 11:27:39 +08:00
parent 68e90ff6ff
commit 1f2e4b2a33
8 changed files with 684 additions and 618 deletions

View File

@@ -168,6 +168,8 @@ class RealtimeClient {
private retryCount = 0;
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
// Strict Mode / HMR 会瞬时卸载→重挂;延迟断开,避免握手未完成就被 close 刷控制台
private releaseTimer: ReturnType<typeof setTimeout> | null = null;
private readonly handlers = new Map<string, Set<RtHandler>>();
private readonly presenceHandlers = new Set<RtHandler<RtPresenceData>>();
private onlineIds = new Set<number>();
@@ -177,6 +179,7 @@ class RealtimeClient {
/** 建立连接(幂等);登录后调用 */
connect(): void {
if (typeof window === "undefined") return;
this.clearReleaseTimer();
this.wanted = true;
if (this.state === "open" || this.state === "connecting") return;
this.clearReconnectTimer();
@@ -185,8 +188,21 @@ class RealtimeClient {
document.addEventListener("visibilitychange", this.handleVisible);
}
/**
* 组件卸载时调用:短暂延迟后再真正断开。
* 若 Strict Mode / HMR 随即重新 connect,会取消本次断开,保留握手中的连接。
*/
release(delayMs = 120): void {
this.clearReleaseTimer();
this.releaseTimer = setTimeout(() => {
this.releaseTimer = null;
this.disconnect();
}, delayMs);
}
/** 主动断开(登出/卸载);不再自动重连 */
disconnect(): void {
this.clearReleaseTimer();
this.wanted = false;
window.removeEventListener("online", this.handleNetworkBack);
document.removeEventListener("visibilitychange", this.handleVisible);
@@ -194,7 +210,15 @@ class RealtimeClient {
this.clearHeartbeat();
if (this.ws) {
this.ws.onclose = null;
this.ws.close(1000, "client logout");
// CONNECTING 时直接 close 会触发浏览器警告;改等 open 后再关
if (this.ws.readyState === WebSocket.CONNECTING) {
const pending = this.ws;
pending.onopen = () => {
pending.close(1000, "client logout");
};
} else {
this.ws.close(1000, "client logout");
}
this.ws = null;
}
this.state = "idle";
@@ -329,6 +353,13 @@ class RealtimeClient {
}
}
private clearReleaseTimer(): void {
if (this.releaseTimer) {
clearTimeout(this.releaseTimer);
this.releaseTimer = null;
}
}
private clearHeartbeat(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);