feat(web): 实现强制下线通知与顶号互踢的客户端感知(Ctrl 205 evicted)
按docs/强制下线通知与顶号互踢-后端接口文档.md §2实现。该文档§2.4声称
"SDK侧已内置:收到后关闭autoreconnect并回调onKick(reason, ctrl)"——读
实际安装的tinode-sdk(0.25.3)真实源码确认这个说法不成立:SDK对
code:205/text:"evicted"只有topic级的内部处理(topic._resetSub()),账
号级(topic为空)完全没有特殊逻辑,也没有onKick这个回调。
改为用SDK真实存在、此前项目里完全没用过的onCtrlMessage钩子(每条
{ctrl}包都会触发,含账号级的)在connection.ts里自行判定
"code===205 && text==='evicted' && !topic"。检测到后立即:清空
sessionStorage的token、清空myUserId、调用resetTinode()——其内部的
disconnect()会把SDK自己的#boffClosed标记置真,这是唯一能真正阻止
backoff计时器安排下一次重连的方式(读真实bundle的Connection#init_ws
确认,不存在另外一个"关闭autoreconnect"的方法)。只有面向用户的弹窗
和跳转延后到用户点确定,逻辑上是"会话立刻终止,通知是补充说明",不
是"通知完才终止"。
新增KickedNoticeRoot(挂载在App.tsx顶层,和ConfirmDialogRoot/
GlobalToastRoot同一层级,保证任意页面被踢都能弹出),文案原样照抄文
档§2.4的表格(reason→标题/正文),单一"确定"按钮(不是取消/确定二
选一——已经被踢了,没有"取消"这个选项,给出会误导)。确认后用
window.location.href硬跳转/login,和client.ts的forceReauth()同一个
理由:保证内存里的每一处状态(TanStack Query缓存等)都从头重置,不用
指望每个组件各自正确响应"你已经被登出了"。
topic非空的205(群内移出等topic级事件)不受影响,走原有的SDK内部
处理,不弹这个全局提示。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c3e8f91364
commit
9adadaa57d
@@ -87,3 +87,36 @@ export function validateCredentialsMessage(params: ValidateCredentialsParams | u
|
||||
const reason = params?.reason;
|
||||
return (reason && INVITE_CODE_REASON_TEXT_CN[reason]) || '注册信息校验未通过,请检查后重试';
|
||||
}
|
||||
|
||||
/** 强制下线通知与顶号互踢-后端接口文档.md §2.4's own worked copy, verbatim -- not our own wording,
|
||||
* so this stays a straight lookup rather than something to rephrase later without checking the doc
|
||||
* again. `admin_user_evict`/`admin_device_kick` intentionally share one entry (doc's own table
|
||||
* merges them into a single row). */
|
||||
const KICKED_REASON_TEXT_CN: Record<string, { title: string; message: string }> = {
|
||||
replaced_by_new_login: {
|
||||
title: '账号已在其他设备登录',
|
||||
message: '您的账号已在另一台设备上登录,当前设备已退出。如非本人操作,请及时修改密码。',
|
||||
},
|
||||
admin_user_evict: {
|
||||
title: '账号已被管理员下线',
|
||||
message: '您的账号已被管理员强制下线,如有疑问请联系管理员。',
|
||||
},
|
||||
admin_device_kick: {
|
||||
title: '账号已被管理员下线',
|
||||
message: '您的账号已被管理员强制下线,如有疑问请联系管理员。',
|
||||
},
|
||||
user_deleted: {
|
||||
title: '账号已注销',
|
||||
message: '您的账号已注销,当前会话已退出。',
|
||||
},
|
||||
user_state_changed: {
|
||||
title: '账号已被限制登录',
|
||||
message: '您的账号已被管理员限制登录,如有疑问请联系管理员。',
|
||||
},
|
||||
};
|
||||
|
||||
const KICKED_REASON_FALLBACK = { title: '账号已被强制下线', message: '当前会话已被终止,请重新登录。' };
|
||||
|
||||
export function kickedNoticeText(reason: string | undefined): { title: string; message: string } {
|
||||
return (reason && KICKED_REASON_TEXT_CN[reason]) || KICKED_REASON_FALLBACK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user