 Your NameandClaude Sonnet 5
|
48baeceaa8
|
修复长按消息气泡弹出自定义菜单时,浏览器/系统原生右键菜单跟着一起弹出的问题
chat-message.jsx 新增 suppressNativeContextMenu,在气泡上监听 contextmenu 事件并 preventDefault,
避免长按(或桌面端右键/触屏模拟长按)触发的原生上下文菜单和应用自己的长按菜单重叠显示。
css/base.css 给 .bubble 补了 -webkit-touch-callout:none,同时覆盖 iOS Safari 那条独立于
contextmenu 事件之外的长按呼出菜单(复制/查询/分享...),不影响 user-select,桌面端正常的
点击拖拽选字复制不受影响。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-07 19:50:06 +08:00 |
|
 Your NameandClaude Sonnet 5
|
4ce666042e
|
修复系统消息/我的收藏页"加载更多"按钮文字下沉被裁切的问题
.system-notification-load-more/.library-load-more 只覆盖了全局button的字号和内边距,没覆盖
line-height,实测继承的全局button{line-height:2.2rem}比这两个按钮自己的行高需求高出一大截,
把文字挤到框内偏下的位置,底部还被裁切掉约2px(用getBoundingClientRect量出文字整体比按钮
边框低9.9px)。改用flex居中+height:auto,不再依赖固定line-height撑出的位置。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-07 19:41:59 +08:00 |
|
 Your NameandClaude Sonnet 5
|
f0091fb146
|
修复消息气泡配色未跟设计稿核对过的问题:发出气泡改蓝底白字,接收气泡改浅灰,日期分隔去掉气泡背景
对照 docs/images/个人对话框(我的状态/对方的状态).png:发出=蓝色实心+白字,接收=浅灰+深字,
与其余相关截图口径一致;旧配色(接收浅绿#c4e0a3、发出用页面同款off-white)不匹配任何参考图,
是尚未跟设计稿核对过的原始默认值。顺带修了一个自查时发现的新问题:已读回执图标复用的.blue
工具类和新的蓝色发出气泡背景是同一个色值,之前会变成看不见的勾选图标,改为不透明白色。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-07 19:41:18 +08:00 |
|
 Your NameandClaude Sonnet 5
|
a8b81a51e3
|
修复本轮UI走查发现的6处样式/交互问题
1. 企业注册-统一社会信用代码:扫码图标误用 icon+onIconClick(渲染在输入框左侧、蓝色),
跟设计稿(图标在右侧、深灰色)不符。原因是 Field 组件里 icon 出现在 <input> 之前;
全仓库唯一一处 onIconClick 用法,改用 PasswordField 同款的 action(渲染在右侧),
复用已有的 .auth-inline-icon 灰色样式,不新增CSS。
2. 意见反馈:图片上传按钮跟设计稿差异较大(当前是通栏虚线框+蓝色图标文字,设计稿是
独立白色方块+灰色图标文字);字数"0/300"计数器卡在textarea自带边框和外层白卡的接缝
处,看起来像叠在两层盒子中间。改法:拆分原本跟"举报证据上传"共用的CSS规则,给
.feedback-upload>button 单独设置白底/无边框/灰色/7.5rem方块;去掉textarea自身的
border,让计数器所在的外层卡片成为唯一可见的白色容器。"图片"标题跟数量合并回一行
(原来用space-between分居两端),maxFiles真实上限(5,早前已确认是后端真实值)不变。
3. 青少年模式(开启/关闭/验证三个页面互跳):三处跳转全部误用push(HashNavigation.
navigateTo),导致点返回会先回到流程内已经处理完的页面,跟本工程之前排查过的
同类"tab切换污染历史记录"是同一个模式。新增 navigateReplace()(跟 navigate() 同结构,
内部换成 HashNavigation.navigateReplace,抄的是 message-center-view.jsx/
content-library-view.jsx已验证过的写法),三处调用改用它。
4. 修改密码页面:复用了 profile-editor-page 的通用label/input样式,但没有像
BirthDateEditor那样带上专属wrapper class,导致三个label的<span>不加粗不换行、
紧贴在一起零间距,input还留着给别的编辑器计数器/清空按钮预留的3rem右侧空白。
新增 change-password-fields 样式(参照birth-date-editor的label/span/input规则)。
5. 消息长按弹出菜单:全历史检索确认这个功能从来不是真正的长按手势,唯一实现是点击
气泡右上角一个不透明度只有0.3的小箭头按钮——参考图"消息页面—长按弹出.png"命名的
功能需求和实现从未对齐。给 .bubble 补上真实的touchstart/touchmove/touchend长按
识别(500ms计时器,移动超过10px视为滚动取消),复用同一套菜单构建逻辑(从
handleContextClick里抽出openContextMenu(x,y)公用)。验证时发现并修复了一个重构
引入的真bug:openContextMenu里遗留了两行没改完的e.pageX/e.pageY(应为参数x/y),
会导致点击小按钮和长按两条路径双双抛ReferenceError,已修正并重新验证两条路径都正常。
6. 群公告横幅(群聊导航栏下方常驻提示条):当前是通栏无圆角、贴边无留白、底部硬分隔线、
文字用的是全局默认灰色(#666)文本token,设计稿是浮在灰底上的白色圆角卡片、四周留白、
无分隔线、文字接近黑色。改法:加margin+border-radius+box-shadow做出浮起的卡片感,
去掉border-bottom,颜色改用--clr-bubble-text(消息正文同款深色token,而不是改全局
的--clr-text-primary,避免影响其它用到这个默认文本色的地方)。
验证:本地dev-proxy逐项验证——企业注册图标位置颜色(截图核对)、意见反馈布局(截图核对,
0/5计数器无缝落在白卡右下角)、青少年模式三处调用grep确认无遗漏+navigateReplace底层
history.replaceState机制单独验证不增加history.length、修改密码页面(截图核对label加粗
间距生效)、长按弹出(真实dispatch TouchEvent触发菜单成功,且验证点击小按钮路径未被
重构破坏)、群公告横幅(getComputedStyle核对margin/borderRadius/boxShadow/color/
borderBottom五个属性全部生效)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-07 17:24:24 +08:00 |
|
 Your NameandClaude Sonnet 5
|
6ab2867f27
|
重新构建dev产物,反映本轮死代码清理+收藏批量删除弹层+通讯录CSS修复三处改动
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-07 01:59:33 +08:00 |
|
 Your NameandClaude Sonnet 5
|
0d32569633
|
修复通讯录"新朋友"两处视觉问题:未读红点数字被挤出圆圈、拒绝/通过验证按钮在移动端换行
现象1:新朋友入口的红色未读数徽标(.people-entry-list em)只显示一个纯红点,数字看不见。
现象2:待处理好友申请行的"拒绝"/"通过验证"两个按钮上下堆叠换行,而不是同一行并排。
根因1:全局 button{line-height:2.2rem} 被 <em> 徽标继承,数字所在的匿名行盒远高于
1.2rem 的圆圈本身,place-items:center 只能让整个超高行盒居中,数字被推出圆心、
甚至溢出到按钮白色背景上(白字白底=看起来像空心圆点)。这跟本工程早前那轮全局
line-height 排查是同一类问题(01325ad0),.messages-conversation-meta b 当时已经
用 line-height:1 修过,.people-entry-list em 这条当时漏改了。
根因2:css/base.css 里 @media(max-width:640px) 断点下有一条
.people-request-list article > span, .group-request-list article > span
{flex-direction:column} 的覆盖规则,把默认的横向flex行强制改成纵向堆叠。核对两处
按钮组的实际内容(拒绝/通过验证、拒绝/确定,均为2个短文本按钮)后确认这条覆盖没有
任何布局必要性,纯属误加;因为 im_webapp 主要是移动端场景,这个断点几乎总命中,
所以两个页面的按钮实际上一直都在换行。
修法:给 .people-entry-list em 补上 line-height:1;删除上述@media块里这条多余的
flex-direction:column(连带align-items:flex-start)覆盖规则,让两个列表都退回默认
的横向排列+垂直居中。
验证:本地起dev-proxy,用真实登录账号在通讯录页面注入与真实DOM结构/class名完全
一致的测试节点核对渲染效果——红点数字正确居中显示,两个按钮同一行并排显示,
与预期一致。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-07 01:30:48 +08:00 |
|
 Your NameandClaude Sonnet 5
|
31f2b4afea
|
修复"我的收藏"批量删除仍用原生 window.confirm(),是本工程已确立模式里同一类问题的第三处
现象:跟已经修过的两处同一类问题(46a98782"退出登录"、4773ee5f"解散群组")一样,
content-library-view.jsx 的收藏批量删除也还在用裸 window.confirm(),4773ee5f 的提交
说明里已经点名记录过这一处、留给第七章处理。
根因:window.confirm() 与本工程其它确认弹层的视觉不一致,且部分内嵌 WebView/kiosk
环境会直接禁用同步对话框(confirm() 恒返回 false,删除被静默拦下、用户毫无感知)。
修法:改成两段式 state(点"删除"先进"确认中"态渲染弹层,弹层里的"确定"才真正调用
batchDeleteFavorites),新增 .library-sheet-backdrop/.library-confirm-sheet 样式,
数值抄自本文件已有的 .personal-*/.group-* 同款弹层(46a98782/4773ee5f),保持视觉
语言一致。
验证:真实浏览器注册全新测试账号,在一个真实群聊里发送并收藏两条消息,进入"我的
收藏"多选模式,选中两项后:先测"点删除→点取消"(弹层消失、两项收藏原样保留、未
发出任何 batch-delete 请求);再测"点删除→点确定"(真实 POST .../favorites/
batch-delete 200 成功、两项 ok:true、列表正确清空为"暂无此类收藏"、toast 提示
"已删除所选收藏")。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 17:55:48 +08:00 |
|
Your Name
|
c2e07a89b8
|
清理死代码:sidepanel-view.jsx里被chapterOneViews短路掉的旧版登录/注册/找回密码分支
不是功能修复,纯清理。sidepanel-view.jsx的render()方法开头(chapterOneViews数组
含'login'/'register'/'reset'等)已经对这些view值提前return真实对接后端的
AuthOnboardingView。但同一个render()方法下半部分的三元表达式链里,还留着一份基于
view==='login'/'register'/'reset'的旧分支,分别渲染LoginView/CreateAccountView/
PasswordResetView——由于上面的提前return,这三个view值永远不可能落到这里,是无法
触达的死代码。
确认方式:全仓库搜索确认SidepanelView只有tinode-web.jsx一处渲染入口,其view prop
(state={this.state.sidePanelSelected})只能来自handleHashRoute对SIDE_PANEL_VIEWS
白名单的校验结果——'login'/'register'/'reset'确实在这个白名单里(否则连
sidePanelSelected都设置不了),但chapterOneViews这个子集在render()内部的检查顺序
必然先于旧分支执行,所以旧分支不可能被触达。
删除范围:三个旧分支的JSX(对应import的LoginView/CreateAccountView懒加载/
PasswordResetView懒加载)+ 三个组件源文件本身(login-view.jsx/create-account-view.jsx/
password-reset-view.jsx,删除引用后确认全仓库不含任何其它referrer)+ 两个文件对应的
已生成umd构建产物(webpack不会自动清理不再产出的旧chunk文件,这两个是被删除源文件
遗留下来的孤儿产物)。'cred'分支(ValidationView,不在chapterOneViews里,仍在真实使用)
未改动。defineMessages里对应的login/register/reset翻译key本次未删——它们现在也
无法被messages[view]这条动态查找命中,但涉及i18n提取产物的联动改动超出这次清理范围,
如实记录,未处理。
验证:npm run build:dev干净通过(三个旧chunk不再产出);真实浏览器完整走了一遍
登录(已有账号)/注册(全新手机号)/找回密码(重置刚注册账号的密码,重置后用新密码
重新登录成功)三条流程,均无回归。
|
2026-09-06 17:31:20 +08:00 |
|
Your Name
|
ac8f0cf315
|
补上好友申请前后关系状态聊天页的入口,并把验证留言渲染成真实消息气泡
第3章要求pending_inbound/pending_outbound/none/blocked几种关系状态都能在真实聊天
窗口里看到(参考图 个人对话框(对方的状态)/(我的状态).png、开启沟通页面.png)。
messages-view.jsx里对应的横幅逻辑("以上是打招呼的消息"/"好友申请正在等待处理"等)
本身早就是对的,但排查发现两个问题:
1. 全项目没有任何入口能导航进这个视图——"新朋友"列表(FriendRequests)每一行没有
onClick;好友个人主页(Profile)非好友分支只显示一段cannotAddReason纯文字,没有
任何可点击进聊天的按钮。发起方和接收方在成为好友之前都完全看不到这个视图。
2. 验证留言原文(发好友申请时填的那段话,存在im_friend_requests表的message字段,
不是真实Tinode消息,没有走topic.publish())没有被当作消息渲染出来,参考图里
明确是作为聊天页第一条消息气泡展示的。
修复:
- contact-management-view.jsx: FriendRequests每一行加onClick,用(onCreateTopic||
onTopicSelected)跳进对应聊天(同一套Profile.openChat已经验证过的冷启动安全写法,
接受/拒绝按钮补stopPropagation不受影响);Profile非好友分支(blocked_by_peer/
blocked_by_me/pending_inbound/pending_outbound)补一个"查看聊天"按钮,跟
cannotAddReason文字并排展示(不复用people-profile-actions的两列grid,那是给
发消息+音视频通话两个平级按钮设计的)。ContactManagementView/sidepanel-view.jsx
早前就已经把onCreateTopic透传到位,这次只是多接一层。
- messages-view.jsx: loadRelationshipContext新增loadPendingRequestMessage,
relationship是pending_inbound/pending_outbound时,用relationshipContext自带的
request_id去GET /friend-requests换真实留言文本(没有留言用参考图默认文案"请求
添加你为好友"兜底),渲染成一条合成的消息气泡插在消息列表最前面(不进
messageNodes、不调用发消息接口)——pending_inbound左对齐、pending_outbound右
对齐,气泡样式直接复用chat-message.jsx真实消息已经在用的.bubble/.left/.right
这套CSS(app里"发出的"气泡实际是这套已实现的浅色而非参考图的蓝色,保持跟同一
会话里其它真实消息一致,不単独改这一条的颜色去凑旧版参考图)。
验证:真实两个新账号(vcm909641发起→hankqa14b接收)走了一遍完整流程——
从"新朋友"列表点击行正确进入聊天页,留言气泡+"以上是打招呼的消息"横幅正确显示;
从Profile页"查看聊天"进入,留言气泡右对齐+"好友申请正在等待处理"横幅正确显示;
点"通过验证"接受申请后正确留在列表页不误触发导航;接受后重新打开同一会话,
合成气泡正确消失,被真实的"你们已成为好友,现在可以开始聊天了。"系统消息取代,
无残留/无重复。
|
2026-09-06 17:24:36 +08:00 |
|
Your Name
|
e4b0868036
|
重新构建dev产物(本轮7个并行agent全部完成后的统一构建)
|
2026-09-06 16:44:01 +08:00 |
|
Your Name
|
3790472b4c
|
修复我的收藏/查找聊天记录加载失败时卡死在"加载中…",没有任何错误提示或重试入口
根因:Favorites 和 History(content-library-view.jsx)两个组件的 load() 里,请求失败的
.catch() 只调用 onToast() 弹一条 2 秒自动消失的提示,从未把 items 从初始值 null 变成别的
状态;页面渲染逻辑是"items === null 就显示'加载中…'",两者叠加导致一次真实失败之后页面永远
停在"加载中…",等用户反应过来去看的时候提示早就消失了,分不清"还在加载"和"永远不会加载完"。
这个坑本文件自己的 DownloadPage、group-management-view.jsx 的 GroupFiles、
contact-management-view.jsx 的 Profile 三处早就各自修过(都有一个独立的 loadError 状态),
唯独 Favorites/History 这两个用得最多的列表页当时漏掉了。
真实触发场景:用两个全新账号互加好友、发送图片/视频/文件/语音/文本等真实消息并收藏后,
发现收藏一条较大的(out-of-band)文件附件之后,GET /favorites?type=all 从此稳定返回
500 INTERNAL_ERROR(已确认是纯后端问题,不在前端能修的范围内,另行记录)——但这个后端
问题一暴露出来,"我的收藏"默认落地的"全部"tab 就会用真实数据 100% 复现这里描述的前端缺口:
打开就卡在"加载中…",没有任何办法知道发生了什么、也无法重试。
修复:Favorites/History 都新增 loadError 状态,非 append(首次/切换 tab、类型、搜索词)请求
失败时置为 true、成功时置回 false,切换 type/topic/view/query 时重置;渲染时 items === null
且 loadError 为真则显示"加载失败,请重试"而不是死等的"加载中…"。"加载更多"(append)请求
失败保持原样只弹 toast,不影响已经显示出来的列表。
验证:复现上述 500 场景(GET /favorites?type=all 持续 500),修复前"我的收藏"全部 tab
永久停在"加载中…";重新构建后同一账号同一坏数据状态下正确显示"加载失败,请重试"。
|
2026-09-06 16:39:13 +08:00 |
|
Your Name
|
0d2637861d
|
修复好友个人主页"发消息/音视频通话"对零消息历史的新好友必现"消息已禁用"
根因:Profile.openChat() 一直只调用 onTopicSelected(tinode-web.jsx 的 handleTopicSelected),
这个函数只负责"切换到某个已知 topic",从不发送原生 Tinode {sub} 请求。对一个从未被任何一方
订阅过的全新 P2P 会话(刚通过好友验证、双方都还没发过消息),本地没有缓存的 topic 订阅信息,
messages-view.jsx 的 isWriter 因此永远停在初始值 false,输入框被替换成"消息已禁用"提示且没有
任何入口能发出第一条消息——对每一个"刚加好友、还没聊过天"的真实场景都 100% 必现,不是边缘情况。
tinode-web.jsx 里其实已经有正确处理这个情况的 handleStartTopicRequest(通过 onCreateTopic prop
暴露):判断 topic 是否已缓存,没有缓存就先设置 sub.mode 为完整权限再切换,已缓存则直接退化成
普通的 handleTopicSelected,两种情况都能正确处理。问题是 sidepanel-view.jsx 渲染
ContactManagementView(好友个人主页/群成员资料页所在文件)时,只透传了 onTopicSelected,
从未把 onCreateTopic 一起传下去,导致 Profile 组件即使想调用正确的入口也拿不到。
修复:sidepanel-view.jsx 补上 onCreateTopic 的透传;contact-management-view.jsx 的
ContactManagementView/Profile 都新增接收这个 prop,openChat 优先调用 onCreateTopic
(不可用时兜底回退到原来的 onTopicSelected,避免类型不匹配报错)。
验证:用两个全新注册、互加好友但零消息历史的真实账号(ch7test1/ch7test2)在浏览器里复现——
修复前点"发消息"进入会话后输入框区域固定显示"消息已禁号"且没有任何输入控件,WS 日志确认
自始至终没有对该 p2p topic 发过 {sub};修复后点"发消息"能看到真实的 {sub} 请求(带
acs.mode: JRWPA)、输入框正常出现,并成功发送/收到多条真实文本消息。
|
2026-09-06 16:38:46 +08:00 |
|
 Your NameandClaude Sonnet 5
|
4773ee5f64
|
修复"解散群组"仍用原生 window.confirm(),是本文件唯一未统一的危险操作确认弹层
现象:本工程已经在"退出当前登录"那次修复(46a98782)里明确认定原生 window.confirm()
和其余确认弹层不一致、且在部分内嵌 WebView/kiosk 环境下会被直接禁用(同步对话框调用
无提示地恒返回 false,操作被静默拦下且用户毫无感知)。全仓库复查后发现"解散群组"是
group-management-view.jsx 里唯一还在用这个模式的危险操作——同样不可逆、后果同样重大,
却没有跟上那次统一。
修复:改成两段式状态(先点"解散群组"进入确认态,再点确认态里的"确定"才真正调用
delTopic),复用本文件 OwnerSelect 早就在用的同款弹层视觉语言
(.group-sheet-backdrop/.group-confirm-sheet),不再依赖浏览器原生对话框。
验证:真实浏览器操作,用真实账号在真实群里走了"点解散→点取消"(弹层消失、群未受
影响、群列表可正常继续访问)和"点解散→点确定"(原生 WS {del hard:true} 成功执行,
200 ok,正确跳转回群列表且群已从列表消失)两条路径。
顺带记录:content-library-view.jsx 的收藏批量删除也还在用裸 window.confirm(),
是同一类问题,但属于第七章范围,本轮不动,留给对应章节的测试处理。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 16:30:06 +08:00 |
|
 Your NameandClaude Sonnet 5
|
30e6d5e300
|
修复风控弹窗取消后再次发送同一条回复,引用内容与正文被永久追加重复
现象:对一条历史消息点"回复",输入含链接的文本触发风控弹窗后点"取消发送"
(预览条正确保留,这是既有修复的正常行为),不改动文本再次点发送、这次
在弹窗里点"我已知晓,继续发送"——最终发出的消息里,正文被写了两遍(引用
块本身只有一份),例如输入"排查根因4 xxx",实际发出"[引用] 排查根因4
xxx排查根因4 xxx"。第三次发送同一条文本会变成正文三遍,以此类推。
根因:sendMessage()给回复拼接引用内容时写的是
`Drafty.append(Drafty.sanitizeEntities(this.state.reply.content), msg)`。
Drafty.sanitizeEntities和Drafty.append(umd/tinode.dev.js,第三方SDK,
未改动)都会原地修改第一个参数并把同一个引用返回,从不做深拷贝——
sanitizeEntities内部直接改content.ent[i]再return content,append直接
first.txt+=、first.fmt.push()再return first。这里把this.state.reply.content
(React state里"正在回复"预览条依赖的同一个对象)原样传进去,等于每次
点发送都会把当次输入的正文永久焊死在这条回复引用的原始内容上。
平时这个副作用不可见:一次成功发送后handleCancelReply()马上清掉
state.reply,被污染的对象跟着一起扔掉。但URL风控检查会把真正调用
doSend()(进而调用这行代码)的时机推迟到弹窗resolve之后,"取消发送"
按现有修复(a964d51b)刻意不清空state.reply以保留预览条——这就让
被污染的引用内容留了下来,等下一次点发送时,同一个已经被污染的对象
再被追加一次,正文因此重复。
修复:拼接前对this.state.reply.content做一次深拷贝,让两个SDK方法的
原地修改只作用于用完即扔的副本,不管sendMessage对同一条还没清空的
回复调用多少次都不影响原始状态。
验证:真实浏览器操作,对消息点回复,输入带链接文本发送触发风控弹窗,
点"取消发送"(预览条与文本保留正常),不改文本再次发送并这次确认
"继续发送"——发出的消息引用块和正文都只出现一次,不再重复。
第14+轮最小粒度测试 Ch4 发现(回复+风控弹窗联动场景,紧邻已知修复
a964d51b验证时发现)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 16:24:30 +08:00 |
|
 Your NameandClaude Sonnet 5
|
53e28e3bc6
|
修复"我在本群的昵称"保存时硬编码 version:0,与本文件其余乐观锁写法不一致
现象:GET /groups/{topic}/members/me/profile 实测确认真实带 version 字段(非空),
但 MyNicknameEditor 保存时一直硬编码 updateMyGroupProfile(topic, value, 0),从不使用
这个已经到手的真实版本号。
根因/影响:跟本文件其余"编辑单个字段+乐观锁"写法(GroupNameEditor/
GroupDescriptionEditor 用 getGroup 拿到的真实 version;Governance/OwnerSelect 的
注释更明确写过"version<=0 时服务端会整个跳过乐观锁 WHERE 子句,两次几乎同时的写入会
静默覆盖,没有冲突信号")不一致。实测验证这里同样如此:真实 version 是 1,PUT 时传 0
依然 200 成功,服务端自己把 version 推到了 2——多标签页/多端几乎同时修改自己在本群的
昵称时,后写入的会静默覆盖先写入的。
修复:GET 到的 version 存进 state,保存时原样带上,不再硬编码 0。
验证:真实浏览器操作,改群昵称两次,第二次 PUT 请求前 GET 到的 version 正确是上一次
保存后服务端返回的版本号(1→2→3 依次递增),不再是硬编码的 0。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 16:17:03 +08:00 |
|
 Your NameandClaude Sonnet 5
|
8cb96e9254
|
修复撤回消息在对方界面显示"无效内容"而非"消息已删除"
现象:真实调用撤回接口成功后(recall_window_seconds=120秒窗口内,长按菜单
"撤回"),撤回发起方自己的消息列表里这条消息直接消失,没有问题;但对方
(消息接收方)的聊天界面上,这条消息位置渲染成一个灰色警告图标+"无效内容"
(invalid content),不是预期的"此消息已被撤回/删除"占位提示。
根因:Tinode原生协议里,一条消息被撤回后,服务端把这条消息的content原地
置为null(seq位置保留,其余内容清空)。chat-message.jsx渲染消息内容时按
Drafty富文本、纯字符串、其余情况三段分支处理,content为null时两段判断
(Drafty.isValid和typeof=='string')都不成立,直接落到兜底的"invalid
content"分支——这段代码从未加过null-content(撤回)的专门判断。这跟
messages-view.jsx里另一套依赖msg.hi标记位的"删除"消息渲染(走独立的
<MetaMessage deletedText=.../>组件)是两码事:撤回消息还是走的普通
ChatMessage组件,从未接入deletedText这个机制。
修复:chat-message.jsx新增一个content==null的分支,复用
messages-view.jsx里messages.message_deleted已经注册好的同一个i18n
id/文案("消息已删除",非新增翻译串),渲染为灰色图标+"消息已删除",
不再落到"无效内容"兜底分支。
验证:真实浏览器操作,两个全新注册账号互为好友,A发一条文本消息后
立即长按撤回(120秒窗口内),B的聊天界面上这条消息正确显示"消息已删除"
(此前是"无效内容")。
第14+轮最小粒度测试 Ch4 发现(撤回消息渲染场景)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 16:12:38 +08:00 |
|
 Your NameandClaude Sonnet 5
|
b200dc2525
|
修复长按消息菜单"撤回"功能完全失效:LOCAL_SEQID 经 webpack externals 解析为 undefined
现象:验证第2章长按消息弹出菜单的时间窗口门控时发现,无论消息是不是自己刚发的、群主/普通
成员、发送后多少秒,长按菜单里永远不出现"撤回"这一项——服务端 messagePolicy 明确返回
recall_window_seconds=120,但这个选项在任何情况下都不会出现。
根因:messages-view.jsx 原来 `import { ..., LOCAL_SEQID } from 'tinode-sdk'`,但
webpack.config.js 把 'tinode-sdk' 映射到全局变量 `tinode`(externals: {'tinode-sdk': 'tinode'}),
而这个全局对象(umd/tinode.dev.js 的 UMD 导出)只挂了 {AccessMode, Drafty, TheCard, Tinode}
四个键,从未包含 LOCAL_SEQID——尽管 tinode-sdk 的 ES 模块本身确实导出了这个常量
(umd/tinode.dev.js 内部仍以 `const LOCAL_SEQID = 0xFFFFFFF;` 定义并在多处话题方法里使用,只是
没有挂到对外暴露的全局对象上)。externals 映射对"这个键不在对象上"和"这个导出根本不存在"没有
区分,所以按名字导入被静默解析成 `undefined`,没有任何构建报错。撤回菜单项的门控条件
`params.seq > 0 && params.seq < LOCAL_SEQID` 因此变成 `seq < undefined`,在 JS 里恒为
false——不是时间窗口判断错了,是入口条件本身就从未为真过,撤回功能对所有用户、所有消息实质性
完全失效。
用控制台直接调用 MessagesView 实例的 handleShowMessageContextMenu 并打印各分支布尔值定位到
这一步:seq=2 这样明显合法的正整数,seqOk 却是 false,才追到 LOCAL_SEQID 本身是 undefined。
修复:messages-view.jsx 不再从 'tinode-sdk' 导入 LOCAL_SEQID,改为本文件内本地声明
`const LOCAL_SEQID = 0xFFFFFFF`,与 SDK 内部定义保持一致(未改动 webpack externals 映射本身——
其它页面都依赖它保持现状,改映射风险更大、影响面更广)。
验证:登录测试账号,在群聊里发一条新消息立即长按,确认"撤回"正确出现在菜单中;点击后
`POST /topics/{topic}/messages/{seq}/recall` 返回 200,消息从会话中正确消失;对比一条已经
超过 recall_window_seconds(同时也超过 edit_window_seconds)的旧消息,确认"撤回"和"编辑"
仍正确不显示,时间窗口判断本身未被破坏。
注:umd/index.dev.js 本次未随此提交提交——工作区里并行的其它 agent 仍在改
contact-management-view.jsx / sidepanel-view.jsx 且未提交,重新构建的 index.dev.js 会把它们的
未提交改动一起编译进同一产物文件,为避免混入不相关改动,只提交 messages-view.jsx 按路由懒加载
的独立 chunk(umd/src_views_messages-view_jsx.dev.js 及其 .map),不受这个问题影响。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 16:05:19 +08:00 |
|
 Your NameandClaude Sonnet 5
|
5c688007c8
|
修复会话列表把系统通知/合并转发消息的预览行恒显示成无信息量的"[消息]"
现象:好友通过验证、群成员加入、群邀请卡片("邀请你加入群聊。")这类系统消息
(x-im-type=system_notice/system_action)和合并转发消息,点开会话看到的是完整的一句
人话,但会话列表的预览行不显示这句话,一律显示占位符"[消息]"。
根因:message-center-view.jsx 的 draftyPreview() 只认三种取值来源——纯字符串、
Drafty 附件实体(content.ent[],映射成"[图片]"等标签)、以及 content.txt——最后兜底
到"[消息]"。但 system_notice/system_action 的真实文案字段是 content.text(跟
chat-message.jsx 渲染这类消息时读的字段一致),合并转发消息的文案字段是
content.comment/content.title,三者都不是 content.txt,全部落进兜底分支。
修复:draftyPreview() 补上对 content.text(system_*)和 content.comment/
content.title(forward_bundle)的识别,命中就直接用这句真实文案,仍然兜底到
"[消息]"覆盖真正未知的内容形状。
验证:真实浏览器操作,两个全新注册账号之间邀请入群后,会话列表预览行正确显示
"邀请你加入群聊。",不再是"[消息]"。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:58:45 +08:00 |
|
 Your NameandClaude Sonnet 5
|
64021fe428
|
补上"邀请好友入群"功能,POST /groups/{topic}/invitations 真实可用但前端零调用点
现象:docs/images/内容沟通对话3-开启进群验证与拉人状态.png 里"你邀请时间旅行者、
李惠惠加入群聊"这条系统事件证明产品设计里确实有"从我的好友里直接选人邀请进群"这条
路径;im-api.js 的 inviteGroupMembers()(POST /groups/{topic}/invitations)也早就
封装好,服务端接口本身可用(实测确认:请求成功返回 pending 状态的邀请记录,对方会在
跟我的会话里收到"邀请你加入群聊。立即加入"卡片)。但全仓库检索这个函数零调用点——
群聊设置页成员格的"添加"格子原来点了只弹一句"请通过转发/分享群二维码邀请成员",把
这条真实存在的路径堵成了唯一路径(二维码分享走的是另一条"申请入群待审批"的路,跟
"邀请"语义不同,不能互相替代)。
修复:新增 InviteFriends 组件(复用 MemberPicker 同款字母分组多选布局),数据源换成
好友列表并按当前群成员名单过滤掉已在群里的人,选中后调用 inviteGroupMembers;
GroupSettings 成员格的"添加"改为跳转到这个新页面。
顺带修复一处会导致这个新路由完全打不开的既有陷阵:tinode-web.jsx 的 SIDE_PANEL_VIEWS
是一份独立于 group-management-view.jsx 自己 GROUP_MANAGEMENT_VIEWS 的硬编码合法视图
名单(文件里 104-107 行注释解释过这就是历史上"新增路由忘记同步这份名单导致深链被清空"
的同一类坑)——只在后者加了新路由名、没有同步这份名单,会导致 handleHashRoute 判定
"Unknown sidepanel view" 直接不渲染侧栏,新页面点了跟没点一样,一并补上。
验证:真实浏览器操作,两个全新注册账号(好友关系),从群设置页"添加"格子进入,勾选
好友、点完成,对方在跟我的会话里收到真实的"立即加入"邀请卡片,群设置页成员数不变
(邀请只是待确认,尚未入群,符合预期)。邀请确认接受这一步会 500,是已知后端问题
(新增72,AcceptInvitation 里一处表名/字段名写错,见 im_be/server/db/mysql/
tenant_groups.go:448 用了不存在的 subscriptions/Topic/User 而不是 im_subscriptions/
topic/userid),不是本次改动引入。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:58:24 +08:00 |
|
 Your NameandClaude Sonnet 5
|
b6adc032a2
|
修复发送含链接消息时切换话题,风控弹窗可能把消息送错会话
现象:在会话A输入含链接的消息并点发送,POST /security/urls/check 请求还未返回时切换到
会话B,请求返回后风控弹窗会在会话B里弹出(本该属于会话A),此时点"继续发送",消息被
真实发进了会话B,而不是最初输入它的会话A——跨会话消息误投递。
复现:会话A(kcg051307)输入"https://example.com/racefix?token=fix002"点发送,立即
(在checkURL网络请求返回前)切换到会话B(r12c56b2),风控弹窗在会话B视图下弹出;点
"继续发送"后消息被投递进会话B,会话A完全没有收到,会话B收到了本不该发给它的内容
(本次因B恰好还不是好友触发"发送失败"提示,但发送动作本身已经指向了错误的会话,
如果B是好友会真实送达)。
根因:`getDerivedStateFromProps`在话题切换时会把`riskDialog`重置为null,但这只能防住
"弹窗已经弹出、随后切换话题"这一种时序——它防不住"checkURL还在进行中就切换话题,
此时机的.then()回调在切换之后才执行"这种反过来的时序:这次setState({riskDialog:{...}})
是在切换完成之后才发生的,不会被这次重置覆盖,于是在当前(错误的)话题下重新弹出弹窗,
且弹窗里的`doSend`闭包沿用的是`this.props.sendMessage`,按确认时的当前话题发送,
不是消息最初输入时所在的话题。
修法:sendMessage()进入URL风险检测分支时已经缓存了`topicForDraft`(用于清草稿),
在checkURL的.then()回调最开头加一道判断——如果`this.state.topic`已经不等于
`topicForDraft`,说明用户已经离开发起这次检测的会话,直接返回false(视同消息被放弃,
不弹窗、不发送),不再继续读取风控结果或setState。这与本文件已有的"两条风险消息连续
触发、后一个覆盖前一个"场景处理逻辑(resolve(false)后覆盖)是同一种"宁可丢弃، 不可
错误投递"的一贯取舍,只是这次要防的是切换会话而不是重复触发。
验证:npm run build:dev后浏览器实测——(1) 会话A输入风险链接点发送,立即切换到会话B,
2秒后确认会话B里没有弹出风控弹窗(修复前必现);(2) 切回会话A确认最后一条消息仍是修复
前测试遗留的旧消息,本次新消息没有出现在A也没有出现在B,两个会话都没有收到,符合
"宁可丢弃不可错投"的设计;(3) 对照组(不切换话题、正常触发风控)依旧能看到弹窗并正确
发送/取消,未被这次改动影响。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:57:22 +08:00 |
|
Your Name
|
644b663ed1
|
重新构建本轮2处修复的编译产物(通话小窗预览显隐 + 语音通话菜单翻译)
对应前两个commit的源码改动:src/widgets/call-panel.jsx、
src/i18n/zh.json、src/i18n/zh-TW.json。跑 npm run build:i18n 重新生成
src/i18n.min/{zh,zh-TW}.json,再跑 npm run build:dev 让 webpack 重新
编译对应的 umd 产物。
本次只挑了跟这两处修复相关的产物文件加入本commit:call-panel.jsx 自己
的 chunk(含 sourcemap)、zh/zh-TW 两个 i18n chunk。umd/index.dev.js、
umd/index.dev.js.map,以及 umd/src_views_messages-view_jsx.dev.js 等其它
umd 产物这次构建也发生了变化,但那是因为这台机器上同时有其他并行任务在
改 src/views/messages-view.jsx、tinode-web.jsx、contact-management-view.jsx
等跟本轮通话章节测试无关的文件(构建时是从磁盘当前状态整体编译的,会把
它们尚未提交的改动一起编译进去)——这些不是我这轮的改动,没有一并提交,
保留在工作区未纳入版本控制,避免把别的任务未完成的改动夹带进这个commit。
|
2026-09-06 15:56:44 +08:00 |
|
Your Name
|
56cd91d40e
|
修复语音通话菜单项中文翻译误译成"称呼",实际含义是发起语音电话
会话更多菜单里发起语音通话的入口(tinode-web.jsx menu_item_audio_call,
唯一消费点,2236 行 formatMessage 渲染其 title)中文翻译一直是"称呼",
和视频通话入口"视频电话"并列显示时,用户会把它当成设置备注昵称之类的
功能,点开才发现其实是拨打语音电话——源头是英文 defaultMessage 只有一个
词"Call",同时可以理解成"称呼/命名"或"打电话",翻译流程选错了词义,
zh.json/zh-TW.json(简繁中文)都错,全仓库搜索确认"称呼"/"稱呼"没有
被其它任何 key 使用,不是复用了别的合法翻译。
改成"语音电话"/"語音電話",与同一菜单里视频通话入口现有的"视频电话"
措辞保持一致(未改动"电话"这个词本身的选择,只修正词义选错的问题,
避免额外引入新的用词不一致)。
验证:修复前 tw.props.intl.formatMessage({id:'menu_item_audio_call'})
返回"称呼";重新跑 npm run build:i18n 生成 src/i18n.min 后同一调用
返回"语音电话"。
|
2026-09-06 15:56:12 +08:00 |
|
Your Name
|
9eb9df649d
|
修复视频通话振铃阶段本地小窗预览提前可见且可拖动
本地小窗预览(.call-party.self)此前只由 audioOnly/minimized 控制显隐,
没有校验连接是否真正建立(this.state.remoteStream),导致视频通话振铃
阶段(对方尚未接听、只有本地摄像头画面)小窗就已经以 display:block 可见,
且 dragStart 里对应的分支同样没有这层校验,振铃阶段就能拖动这个本应还
不存在的预览框。
render() 里 .call-party.self 的 disabled 属性补上 !connected 判断,
dragStart 里进入 'self' 拖动分支的条件补上 this.state.remoteStream 校验,
两处保持一致:只有远端媒体真正到达(连接建立)后小窗才出现并可拖动。
验证:用 canvas.captureStream()/AudioContext 构造的合法 MediaStreamTrack
配合 monkey-patch getUserMedia,绕开已知后端通话邀请 400 缺陷直接驱动
CallPanel 组件状态。振铃阶段(waitingForPeer=true, remoteStream 未设置)
下 .call-party.self 计算样式 display:none、无 [disabled] 属性对应的
cursor:grab;调用 handleTrackEvent 模拟远端媒体到达后,display:block、
cursor:grab,且用真实鼠标拖拽(非合成事件)验证了拖动/边界收紧都正常。
|
2026-09-06 15:55:48 +08:00 |
|
 Your NameandClaude Sonnet 5
|
dbf47847fe
|
修复系统消息列表漏查表的"group.join_request.accepted/rejected"机器码
现象:核对第2章系统消息列表最近修复的"group.announcement.updated"查表遗漏问题时,逐个比对
im_be 的 insertGroupEventTx 调用点,发现入群申请审批这一条并不在当初核对的 9 种静态事件码
之列——群管理员在群管理页批准或拒绝入群申请后,系统消息列表会展示原始机器码,而不是可读文案。
根因:TenantGroupJoinRequestResolve(server/db/mysql/tenant_groups.go:642)拼的事件码是
"group.join_request."+status(status 取 server/store/types/groups.go 的
GroupJoinRequestStatusAccepted/Rejected,即 "accepted"/"rejected"),实际产生的是
"group.join_request.accepted"/"group.join_request.rejected" 两个完整机器码。这两个和已收录的
"group.join_request.created"(提交申请时那条)是三个不同的 key,NOTIFICATION_TEXT 表里只有
后者,前两个查表落空,systemNotificationText() 兜底把原始机器码直接展示给用户。
修复:在 message-center-view.jsx 的 NOTIFICATION_TEXT 表里补上这两个 key。
验证:读 im_be 源码逐个核对 insertGroupEventTx 全部调用点,确认这是唯一还缺失的动态拼接事件码;
重新构建后确认产物里两个 key 及对应文案都已生效。未在真实环境触发一条这样的通知做端到端验证——
需要另一账号先申请加群、本账号再批准/拒绝,属于第6章群管理操作范畴,本轮未展开搭建。
注:umd/index.dev.js 本次同样未随此提交提交——工作区里此刻并行的其它 agent 正在改
i18n/zh(-TW).json、group-management-view.jsx、sidepanel-view.jsx、call-panel.jsx、
contact-management-view.jsx 等多个文件且均未提交,重新构建的 index.dev.js 会把它们的未提交
改动一起编译进同一产物文件,为避免混入不相关改动,本次只提交源码。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:51:10 +08:00 |
|
 Your NameandClaude Sonnet 5
|
5ddace97f4
|
修复好友资料/验证页对"当前关系"的两处判断遗漏,与已有的好友申请状态机修复形成完整闭环
现象一:Profile 组件(好友/陌生人资料页)在 getConversationContext 回来后只合并了
relationship 字段,没有一并合并 capabilities——这个组件判断"能否显示添加好友按钮"
用的是 profile.capabilities.can_add_friend,不是 relationship 本身。已实测复现:
刚发送好友申请、relationship 已经变成 pending_outbound 时,若走的是"直接刷新这个
资料页"而不是 Verification 组件自己的发送成功回调(该回调已由另一处提交单独修复),
canAdd 依然读到发送前搜索结果种子里的 can_add_friend:true,"添加好友"按钮继续可点,
可再次触发一次好友申请(配合后端不去重的缺口——新增44)。
现象二:Verification 组件(好友验证/发送申请页)完全没有用 getConversationContext
校正过 relationship,只依赖 getUserProfile——但该接口对非好友直接 403,403 时的
catch 分支什么都不做。真实复现:本次会话内先成为好友、再解除好友关系后,若该好友的
profile-seed 缓存还留着"好友"时期的旧数据,重新打开验证页会一直显示"你们已经是
好友了",且不提供发送框,除非整页刷新清掉内存缓存,否则无法向这个人重新发起好友申请。
修复:两处都在 getConversationContext 成功回调里,把它返回的字段当作当前关系的权威
来源合并进本地 profile 状态——Profile 一并合并 capabilities(而不只是 relationship),
Verification 补上原本没有的 getConversationContext 调用去合并 relationship。
验证:真实浏览器操作,两个全新注册账号之间反复走查——(1) 发送好友申请后直接刷新
资料页,"添加好友"按钮正确消失、显示"好友申请正在等待验证";(2) 成为好友后互相
删除好友关系,立刻重新打开验证页,正确显示可发送的验证留言表单,不再卡在"你们已经
是好友了"。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:41:36 +08:00 |
|
 Your NameandClaude Sonnet 5
|
605cd12a6e
|
修复第1章各认证页之间切换会带出上一个页面的报错文案
现象:在注册页填错邀请码提交后(正确显示"邀请码错误,请重新输入"),点击"已有账号?登录"
切到登录页,登录表单顶部会原样带出这条"邀请码错误"提示——用户在登录页什么都还没做,却先看到
一句跟登录毫不相关的报错。反过来,登录密码错误后点"马上注册"也会把"账号或密码错误"带到刚打开
的注册页。
根因:errorText/errorLevel 是 tinode-web.jsx 顶层的一份全局共享状态,不跟当前显示哪个
sidepanel 绑定。Login 组件自己的"切换企业"链接会在跳转前先调用一次 onError() 清空,说明
之前已经意识到这个状态会带着走,但企业码/登录/注册/忘记密码等页面之间的其它跳转都没有做
同样的处理——其中 Register 等好几个视图目前甚至没有拿到 onError 这个 prop,需要在各处
分别补,比较分散。
修复:在 AuthOnboardingView(chapterOneViews 统一走的入口组件)里加一个以 view 为依赖的
useEffect,view 变化时统一调用 onError() 清空全局报错状态,一次性覆盖企业码/登录/注册/
忘记密码等视图之间的所有切换路径(含浏览器前进后退),不用逐个跳转调用点分别处理。
验证:注册页提交错误邀请码复现"邀请码错误,请重新输入"后点击"登录",确认登录页不再带出
该提示;反向验证登录页密码错误后点击"马上注册",确认注册页同样不再带出"账号或密码错误"。
注:umd/index.dev.js 本轮未随此提交一起提交——auth-onboarding-view.jsx 不是按路由懒加载的
独立 chunk,此刻工作区里另有并行 agent 正在改 contact-management-view.jsx / call-panel.jsx
且未提交,重新构建的 index.dev.js 会把它们尚未提交的改动一起编译进同一个产物文件,为避免把
不相关改动混进这次提交,本次只提交源码,构建产物留给后续统一重新构建的提交处理。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:40:43 +08:00 |
|
 Your NameandClaude Sonnet 5
|
a71aee9487
|
修复"安全"设置页退出登录无二次确认,单击立即登出
现象:从 account-settings-view.jsx"账户设置"、info-view.jsx,以及第2章系统消息列表"前往
安全设置"跳转按钮都能到达 acc-security-view.jsx 这个"安全"页(并非废弃页面,是与
personal-settings-view.jsx"设置"页并存的独立入口),点击"登出"没有任何二次确认,单击
图标就会立即退出登录,且没有任何撤销机会。
根因:该文件里"登出"按钮的 onClick 直接调用 this.props.onLogout(),而同文件里紧挨着的
"删除帐户"按钮走的是 onShowAlert 弹确认框(handleDeleteAccount)。此前 46a98782 那一轮
已经把 personal-settings-view.jsx"设置"页的登出从原生 window.confirm() 改成了自己的
ConfirmSheet/LogoutConfirm,但漏改了这个仍然可达的旧版"安全"页入口。
修复:新增 handleLogoutRequest,复用本文件已有的 onShowAlert 机制弹确认框,文案
("退出登录"/"确定要退出当前登录吗?")与 personal-settings-view.jsx 的 LogoutConfirm
保持一致,不新造一套措辞。
验证:本地登录后访问 #security,点击"登出"确认弹出对话框(标题/正文/按钮文案核对无误);
点击"取消"保持登录态且仍停留在安全页;点击"好"确认后正确清除 auth-token 并退回登录/租户
判断逻辑接管的空 hash。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:36:55 +08:00 |
|
 Your NameandClaude Sonnet 5
|
51e0fc420f
|
修复发送好友申请后个人资料页仍显示可点击的"添加好友"按钮
现象:搜索陌生人->进入资料页->点"添加好友"->发送验证留言成功后,
无论是验证页800ms后的自动跳转、还是手动点返回,回到这个人的资料页时
仍然显示"添加好友"主按钮(本应显示"好友申请正在等待验证"提示文案),
且这个按钮此时仍可点击,会打开验证页再次发起一次申请(配合后端
好友申请不去重的缺口——新增44——实际造成同一个人收到多条重复申请)。
根因:发送成功后 Verification 组件把 profile-seed 缓存的 relationship
patch 成了 pending_outbound,但没有同步更新 capabilities.can_add_friend
——Profile 组件判断是否展示"添加好友"按钮的 canAdd 变量读的是
profile.capabilities.can_add_friend,不是 relationship 本身,遗留的
can_add_friend:true(原样来自发送前的搜索结果种子缓存)导致按钮继续显示。
修复:发送成功后 patch 缓存时把 capabilities.can_add_friend 一并设为
false,和 relationship 保持同步。
验证:真实浏览器操作,两个全新注册账号之间,搜索->进入资料页->添加好友
->发送验证留言,自动跳转回资料页后正确显示"好友申请正在等待验证",
"添加好友"按钮消失,无法重复发起申请。
第14+轮最小粒度测试 Ch3-4 发现(好友申请气泡状态机场景)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:35:50 +08:00 |
|
 Your NameandClaude Sonnet 5
|
552c891cc3
|
修复清除缓存显示"已释放约0.0MB",与实际清理量不符
现象:设置-清除缓存确认后,无论Cache Storage里实际有多少数据,展示的"已释放约
X MB"几乎总是0.0,即使清理动作本身(caches.delete)确实生效(caches.keys()清理后
变为空数组)。
根因:原实现在caches.delete()前后各调用一次navigator.storage.estimate(),用两次
usage之差算释放量。实测确认Chrome的storage.estimate()不会同步反映Cache Storage的
删除——caches.keys()已经确认清空后,estimate()仍继续报告删除前的usageDetails.caches
数值,实测滞后超过500ms未刷新。人为塞入一个2MB的Cache Storage测试项复现:删除成功
但"已释放"显示0.0MB;塞入3MB项验证修复后正确显示3.0MB。
修法:不再依赖storage.estimate()的前后差值,改成删除前逐个遍历所有Cache
Storage条目、读取每个response的真实blob大小并累加,直接得到本次会话即将删除的
真实字节数,不受浏览器存储统计滞后影响。
验证:npm run build:dev后浏览器实测——(1) 无缓存数据时点击"清除缓存",正确显示
0.0MB且弹层能正常从"清空缓存"态切到"缓存已清理"态、点"完成"可关闭;(2) 用JS人为写入
2MB/3MB测试用Cache Storage条目,分别清理后弹层准确显示2.0MB/3.0MB,且caches.keys()
确认条目已被真实删除。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 15:30:40 +08:00 |
|
Your Name
|
cd092108c2
|
重新构建dev产物(清理调试残留日志+本轮3处修复的编译产物)
|
2026-09-06 15:04:00 +08:00 |
|
Your Name
|
4715ea4497
|
修复未登录状态下深链/跳转到某个话题导致应用整体白屏
handleHashRoute 判断 path[1](话题名)是否合法只检查了 Tinode.topicType(topicName)
这一个语法层面的条件——没登录时把hash设成一个真实群/好友的"#/<topic>"地址(比如
退出登录后浏览器历史回退、或者一个带话题参数的通知/深链在未登录状态下被打开),
话题名本身语法上合法,于是mobilePanel被设成'topic-view'。但render()里
MessagesView真正渲染的前提除了mobilePanel还要求!chapterOneActive(即
this.tinode.isAuthenticated())——这个前提没登录时是假的。结果移动端下
SidepanelView的显示条件(mobilePanel=='sidepanel')和MessagesView的显示条件同时
为假,两边都不渲染,应用整体白屏(app-container是空的,没有任何报错),
仅靠换hash也无法恢复,只能整页刷新。
修复:判断话题合法性时把"当前是否已认证"也纳入,未登录时统一走已经验证过安全的
"话题名非法"分支(topicName置null,mobilePanel回退到sidepanel)。
已实测复现:退出登录后把location.hash设成一个真实群的话题地址,修复前白屏,
修复后正确回退。
|
2026-09-06 15:03:48 +08:00 |
|
Your Name
|
ed9a92896c
|
修复系统消息列表原样显示机器码"group.announcement.updated"
NOTIFICATION_TEXT 查表漏收了这一种群事件码。real账号(asn494180)的真实系统消息
列表里实测出现过未翻译的原始事件码——server/db/mysql/tenant_groups.go 的
insertGroupEventTx 一共会写9种 group.* 事件(announcement.updated/created、
join_request.created、member.joined/left/updated、mute.updated、
owner.transferred、role.updated、settings.updated),这张表原来漏了公告更新
这一种,是9种里唯一没查表命中、直接把机器码露给用户看的。
|
2026-09-06 15:03:34 +08:00 |
|
Your Name
|
a81a7b162d
|
修复群系统事件消息重新变回普通气泡+"未找到"作者名(并发编辑意外丢失CSS)
e6e51f62 提交的".meta .bubble.system"样式规则,被同一时间并发运行的另一个agent
提交(8087fd7e)在给css/base.css暂存自己的"通话小窗grab光标"改动时意外整个删掉了
(这个环境多个并发session同时改同一个文件、共享git暂存区,8087fd7e自己的commit信息
也提到了这个现象,但它删掉这条规则时没有同时补上——本轮测试期间反复出现的同一类问题)。
净效果:meta-message.jsx渲染系统事件消息用的".bubble.system"类此前已经没有对应CSS,
群里的成员加入/角色变更/公告发布这类系统提示消息会退回默认气泡样式,重新出现
"未找到"这种内部占位作者名——e6e51f62本来要修的问题又变相复现了。
重新补上这条规则,内容与e6e51f62最初提交的完全一致。
|
2026-09-06 15:03:25 +08:00 |
|
Your Name
|
fa2288c497
|
修复群文件上传不传file_type,图片/视频/语音群文件全部显示成通用文件图标
根因:POST /groups/{topic}/files 的 file_type 是客户端登记时自己声明的字段(im-api.js的
addGroupFile早就留了这个参数),不是服务端拿mime_type反推的——实测:上传一张mime_type
正确记成"image/png"的图片,只要请求体不带file_type,服务端就把它落成字面量"file"(不是
留空)。GroupFiles的onFile上传流程一直没传这个字段,导致FILE_TYPE_META准备好的
image/video/audio/document四种彩色图标从未被真正用上,所有群文件不分类型清一色显示灰色
通用文件图标。
修复:新增fileTypeFromMime辅助函数(与content-library-view.jsx的同名函数用同一套mime前缀
优先、扩展名兜底文档类型的判断口径),上传时算出分类一起传给addGroupFile。
验证:真实浏览器上传一张PNG,接口返回file_type从"file"变成"image",列表图标从灰色通用
文件图标变成绿色图片图标;对照同一会话里upload-fix之前的旧文件,图标仍是灰色(存量数据
不受影响,符合预期,不需要也没有接口能批量回填旧记录)。
|
2026-09-06 13:47:19 +08:00 |
|
 Your NameandClaude Sonnet 5
|
ddf23d40b2
|
修复新会话首次打开时消息历史被静默截断为1条、对方已读回执永不刷新
根因:tinode-web.jsx的prefetchLatestMessagePreviews()(会话列表"最后一条消息"预览)
会在会话列表一渲染就对每个有动态但本地无缓存摘要的话题调用
topic.subscribe(startMetaQuery().withLaterData(1)...),只为预览目的抓取"最新1条"
消息——这常常发生在用户真正点开这个会话之前。这次订阅会把tinode-sdk的
Topic._attached置为true,且把_maxSeq设成那一条消息的seq。
用户随后点开会话时,messages-view.jsx自己的subscribe()发现topic.isSubscribed()
已经是true就直接return,从未真正发出过完整的{sub}请求。即使去掉这个短路,
SDK的Topic.subscribe()本身在_attached为true时也会无条件短路成
Promise.resolve(this),完全不看传入的getParams——所以无论如何都不会把
withLaterSub()/withAux()等真正需要的查询发给服务端。
现场复现:全新注册的两个互为好友账号之间发4条真实消息,全新session下第一次点开
该会话,只渲染出最新1条,其余3条静默丢失;对方的已读回执(应变蓝色双勾)永远
不刷新——这两个现象都会在离开会话再重新打开后自愈(因为离开会清掉_attached),
但用户在此之前看到的是一个残缺、状态错误的会话。
修复分两处:
1. subscribe()内,若topic已经isSubscribed(),先topic.leave(false)(只清客户端和
服务端的订阅态,不清本地已缓存消息),再执行真正的subscribe(),确保这次一定
是货真价实的、服务端会认真处理getParams的订阅请求。
2. 把消息分页查询从withLaterData(MESSAGES_PAGE)("给我比本地_maxSeq更新的消息")
改成withData(undefined, undefined, MESSAGES_PAGE)("给我最新的N条消息",不依赖
本地缓存游标)。前者在_maxSeq被上述预览订阅污染后会查询"since:被污染的seq+1",
服务端自然返回空;后者是绝对查询,对全新话题(_maxSeq为0,两者退化成同一个
since:undefined查询)行为不变,对被污染的话题能正确要回完整历史。
第14+轮最小粒度测试 Ch3-4 发现(P2P单聊消息状态机场景)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:46:58 +08:00 |
|
Your Name
|
8087fd7e97
|
补上视频通话小窗拖动功能遗漏的CSS改动(.call-party.self 的 grab 光标提示)
上一个 commit(call-panel.jsx 里新增的"全屏连线中拖动本地小窗"功能,对应 8.9 双方
视频通话中.png 的"小窗应支持拖动"需求)只提交了 JSX/JS 那部分,配套的 CSS
(.call-party.self 增加 cursor:grab 提示可拖动,[disabled] 态改回 cursor:unset)
当时被同一文件里其他并发改动的暂存操作顶掉,没有一起进去——这个环境里多个并发
session 同时在改 css/base.css,共享 git index 导致 git add 经常把彼此的暂存内容
一起扫进对方的 commit,这是本轮测试期间反复确认到的现象,不是这次改动本身的问题。
JS 逻辑不受影响(拖动本身早就用真实指针事件验证过可用),这里只是补上缺的光标提示
样式,让用户在尝试拖动前就能看出这个小窗是可拖的。
|
2026-09-06 13:40:03 +08:00 |
|
 Your NameandClaude Sonnet 5
|
e6e51f625e
|
群系统事件消息改为居中纯文本渲染,不再走普通气泡+作者名的展示逻辑
根因:群里的系统事件消息(成员加入、角色变更、公告发布等,x-im-type=system_action/
system_notice)服务端并不带真实的 `from`,但 messages-view.jsx 一直把它们和普通消息
一样交给 ChatMessage 渲染——chat-message.jsx 的 fullDisplay 逻辑会在气泡下方额外画一行
发送者名字,解析不到发送者时回退显示"未找到"。对照参考图 内容沟通对话3-开启进群验证与
拉人状态.png,这类系统事件应该是居中的纯灰字提示,不带头像、不带气泡、不带作者名——现状
既不符合参考图,也让"未找到"这种内部占位文案暴露给了用户。
怎么修:meta-message.jsx 新增 systemText 分支,复用日期分隔条同一套"居中提示"视觉语言
(.meta .bubble.system,无背景、纯居中灰字);messages-view.jsx 在消息渲染分支里识别
system_action/system_notice 且不带可操作按钮(group_invitation_created 那种带
"立即加入"按钮的除外,仍走完整气泡展示)的消息,改为渲染 MetaMessage 而不是 ChatMessage。
验证:用真实群(3 个真实账号)走了两遍"扫码申请入群→通过审批",会话里"有成员通过审批
加入了群聊。"正确显示为居中灰字,不再出现气泡、头像和"未找到"作者名;带按钮的
group_invitation_created 消息不受影响,仍是完整气泡+"立即加入"按钮。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:39:35 +08:00 |
|
Your Name
|
e53af93409
|
修复收藏页多选模式下类型状态丢失、跨tab选择静默累加两个问题
根因1:'favorites-select' 是全部类型 tab 共用的同一个路由,本身不带类型信息,Favorites
组件原来进入多选模式时把 type 硬编码回退成 'file',不管进入前实际停在哪个 tab。实测在
"文本"tab 点"多选",列表和头部计数会静默换成"文件"tab 的数据,用户看不出发生了什么,
勾选/删除会作用在自己根本没看到的文件项上——测试过程中因此真的误删了一条文件收藏。
修复:进入多选前把当前 type 作为 hash 参数带上,多选页/分享页的返回箭头、"取消"、发送后
跳转都改用该参数换算的 tab,不再硬编码 'favorites-files'。
根因2:切换类型 tab 只更新 type,没有清空 selected——在"文件"tab 选中项目后切到"图片"tab,
头部仍显示旧的"已选择 N 项",实际操作的是已经不在当前列表里的旧类型收藏,删除/转发没有任何
可见提示。修复:切 tab 时一并清空 selected。
验证:真实浏览器逐步复现两个问题后确认修复生效——在"文本"tab进入多选保留文本列表、
切"图片"tab选择清零、"取消"正确回到多选前所在的图片tab;多选删除功能本身回归正常。
|
2026-09-06 13:38:25 +08:00 |
|
 Your NameandClaude Sonnet 5
|
1228d6a534
|
补上组织架构部门蓝色高亮修复遗漏的CSS改动(.expanded重命名为.me)
上一个commit(1d47c586)只提交了JSX那部分(DeptRow/DeptNode改用containsMe),
对应的CSS改名(.organization-department.expanded → .me)当时被同一文件里
其他并发改动的暂存操作顶掉了,没有一起进去,导致上一commit单看JSX是完整的,
但CSS选择器名字还没改,实际不生效。这里补上,两个commit合起来才是完整修复。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:36:27 +08:00 |
|
 Your NameandClaude Sonnet 5
|
1d47c586d3
|
修复组织架构页部门蓝色高亮跟"是否展开"挂钩,根节点被误判成"我的部门"
DeptRow原来的蓝色高亮(.organization-department.expanded)直接复用"展开/收起"
这个纯UI交互状态:depth 0的根节点默认就是展开的(DeptNode: useState(depth===0)),
导致根节点从打开页面那一刻起就被误染成"当前用户所在部门"的蓝色,与参考图
(docs/images/部门组织框架.png)不符——图里根节点"总裁办50人(一级)"明明是展开状态,
颜色却是黑色默认色,只有真正包含当前用户的"技术4人(二级)"是蓝色实心菱形。
根因是把"展开状态"和"是否是我的部门"这两个独立概念用同一个CSS类混在了一起。改为
新增containsMe(DeptNode根据自己已加载的members列表里有没有当前用户算出,与expanded
彻底解耦,expanded继续只管chevron方向),CSS类从.expanded改名成.me(与PersonRow已有的
.me保持同一套命名)。顺手把三处重复的"裸id/usr前缀id两种形式对比myUserId"的内联判断
提成共享的isMyUserId函数。
当前测试租户组织架构数据本身为空(GET /organization/departments、/members 都是真实
200+空数组,非账号权限问题——kcg051307交叉验证过同样为空),无法用真实多级树肉眼过一遍,
按测试要求改用桩数据验证:mock了总裁办→财务/销售/技术三个部门+技术下4个成员(含当前
用户)复现参考图的结构,展开技术后确认根节点保持黑色、技术正确变蓝,与参考截图逐像素
对照一致;修复前用同一份桩数据可复现根节点被误染蓝的问题。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:35:38 +08:00 |
|
 Your NameandClaude Sonnet 5
|
a964d51ba5
|
修复回复/编辑消息触发风控弹窗时,"正在回复"预览条在弹窗结果出来前就被清空
现象:对一条历史消息点"回复",输入含链接的文本点发送,风控弹窗(安全提醒/警示级)
刚弹出的同时,composer 里"正在回复 XX"的预览条就已经消失了;如果这时候点"取消发送",
这条回复就被永久放弃——原来引用的消息内容和回复关系不再存在,也没有任何提示,唯一的
恢复办法是回到那条历史消息重新点一次"回复"。编辑消息(修改一条已发消息的内容后再次
包含链接)是同一根因的同款问题,只是编辑可以靠重新点"编辑"找回原文,没有回复那么彻底
不可逆。
根因:sendMessage() 原来在根据 this.state.reply / this.props.forwardMessage 组装好
msg/head 之后,同步、无条件地立刻调用了 this.handleCancelReply()(清空 reply/编辑
状态),这一步发生在下面的 URL 风控检查(POST /security/urls/check)之前——但风控
弹窗要等用户点"继续发送"或"取消发送"才会真正决定要不要走 doSend(),这中间的等待期里
UI 状态已经被提前清空了。
修法:把 handleCancelReply() 从三处提前调用点移到 doSend() 内部、真正调用
this.props.sendMessage(...) 之前。这样它只在真的要发送时才执行——不含链接的普通消息
里 doSend() 本来就是同步立即执行,时机与原来完全一样;命中风控的消息里,"继续发送"
时才会触发(此时清空回复状态是正确的,消息确实发出去了),"取消发送"时 doSend()
根本不会被调用,回复/编辑的预览条和引用内容都会完整保留,可以直接修改文本重试或
自己手动取消。转发场景不受影响:转发消息不会触发这条风控检查(originalText 恒为
null),移动调用时机对它是纯粹的时序等价替换。
验证:真实浏览器操作,对一条消息点"回复",输入含 https://example.com/a?token=abc123
的文本发送——警示弹窗弹出的同时预览条仍然可见;点"取消发送"后弹窗消失、预览条依旧
完整保留、composer 文本不变;再次点发送、这次点"我已知晓,继续发送",消息正确以
带引用卡片的回复形式发出,预览条在发送成功后才清空。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:32:59 +08:00 |
|
 Your NameandClaude Sonnet 5
|
cdca509183
|
修复群邀请消息卡片丢弃"立即加入"按钮,被邀请人无法接受邀请
根因:POST /groups/{topic}/invitations 邀请成功后,后端会往邀请人与被邀请人的
单聊会话里发一条 x-im-type=system_action、template=group_invitation_created 的
消息,content.actions 里带着真实可用的 {type:"group_invitation_accept",
label:"立即加入", target_id:<invitation_id>}(对照参考图 6.3 加入群聊.png)。但
chat-message.jsx 原来对所有 system_action/system_notice 消息一律只取
content.text 渲染成纯文本,这个 action 数据被整体丢弃,消息只显示"邀请你加入群聊。"
没有任何按钮——im-api.js 里封装好的 acceptGroupInvitation() 因此从来没有调用点。
怎么修:chat-message.jsx 在原有的文本兜底之前,单独识别 content.actions 里
type=="group_invitation_accept" 的这一种模板,渲染一张带按钮的小卡片(其余
system_action/system_notice 模板的纯文本渲染保持不变,不受影响);
messages-view.jsx 新增 handleAcceptGroupInvitation,调用 acceptGroupInvitation()
并用已有的 showToast 反馈结果,逐层传给 ChatMessage 的新 onAcceptGroupInvitation
prop。按钮点击后按 busy/joined/pending_approval/error 四态展示,避免重复提交。
验证:用两个真实账号(A 建群,直接对 B 发起 POST /invitations)复现,重新构建后
B 的会话里正确出现"邀请你加入群聊。"+"立即加入"按钮;点击后正确调用
POST /group-invitations/{id}/accept 并把返回的失败原因("服务器繁忙,请稍后重试")
展示出来——这一步暴露了一个独立的后端 500 bug(accept 接口本身的实现有问题,
已记录在测试报告里,不在本次前端修复范围内),前端的请求发起、按钮状态机和错误展示
均已确认按预期工作。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:26:14 +08:00 |
|
 Your NameandClaude Sonnet 5
|
a6014b84ee
|
修复桌面端拖动悬浮通话窗口后收窄浏览器至移动端断点,通话条卡在旧桌面坐标不跟随移动端布局
现象:视频通话最小化后处于桌面宽度(>640px)时,拖动悬浮小窗到任意位置(dragStart 会把
定位方式从 CSS 的 right/auto-left 切换成显式 left/top 内联样式,方便拖拽期间自由移动),
之后把浏览器窗口收窄到 640px 以下(真实场景:桌面用户把窗口拖窄到半屏和另一个应用并排),
悬浮条不会像预期那样贴顶变成通栏胶囊(base.css 里
#video-container.minimized:not(.audio-minimized) 的移动端规则:left:0/right:auto/
top:var(--caption-panel-height)/width:100%/height:11rem),而是继续停在拖拽结束时的旧
桌面坐标——因为内联 style 的优先级恒高于任何样式表选择器,不管后者的媒体查询是否匹配,
移动端规则里的 left/right/top 全部被内联值盖住,只有从未被内联样式接触过的 width/height
还能生效,导致宽/高不成比例、位置错位。
修复:CallPanel 新增 handleWindowResize,监听 window resize;每次触发时如果
matchMedia('(width<=640px)') 命中且当前处于 minimized,就清掉 containerRef 上手动写入的
left/right/top 内联样式,把定位权交还给样式表,让移动端规则完整生效。componentDidMount/
componentWillUnmount 里和已有的 pointerdown/move/up 监听一起注册/清理。从未拖拽过时这个
清理是空操作,不影响现有行为。
验证:真实 CallPanel 实例(React Fiber 直接获取),伪造 canvas/AudioContext 媒体流跑通整
个通话中状态,用 computer 工具做真实指针拖拽把悬浮窗从默认右上角拖到别处(确认
style.left/top 已写入、right:auto),再用 resize_window 把视口收窄到 375px 宽并确认应用
自身的 resize 监听正确触发(displayMobile 状态同步更新)——收窄后 style 只剩
"cursor: grab",left/right/top 均被清空,getComputedStyle 显示 left:0/top:53.33px(=
var(--caption-panel-height)按当前根字号换算)/width:375px(100%)/height:146.66px(11rem),
与移动端样式表规则完全一致;再放宽回桌面宽度确认悬浮窗正常回到默认右上角18rem方框位置,
未引入回归。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:22:06 +08:00 |
|
 Your NameandClaude Sonnet 5
|
0748f30cc1
|
实名认证提交成功后补拉一次 GET,用服务端脱敏字段替换本地草稿回显(新增61)
现象(IM-后端问题清单-2026-09-04.md"新增61"):POST /security/realname/submissions
成功响应只有 {status, submission_id},从不带 masked_name/masked_id_number,前端此前
的兜底是回显用户本次会话里刚输入的原始姓名/证件号草稿——多数情况下看起来没问题,
但那是未脱敏的原始值,不是服务端认可的脱敏展示值,且只覆盖"这次会话内",换一个
从未在本会话提交过的场景(比如同一账号换设备/清缓存后第一次看提交结果)就没有任何
草稿可回退,只能空白。
之前的复核已经确认 GET /security/realname 从这个功能最初上线的 commit 起就一直带着
masked_name/masked_id_number 这两个字段,只是提交成功后从未再调用过它。
修法:submit() 里 POST 成功后追加一次 getRealnameStatus(),用响应里的
masked_name/masked_id_number 覆盖本地状态,展示服务端脱敏后的真实值;这次追加请求
失败时静默忽略(提交本身已经成功,不应该因为这次锦上添花的请求失败而回退),保留
原有的本会话草稿兜底作为这次请求解析完成前的过渡显示。
验证:注册全新测试账号(无历史提交),提交姓名"测试用户"+证件号
110105199003077856 后,抓包确认 POST 响应确实只有 {status,submission_id},紧接着
自动触发的 GET 响应带 masked_name:"测*"、masked_id_number:"110***********7856";
页面输入框读取到的实际 value 是这两个脱敏值,不是刚输入的原始文本,证实修复生效。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:21:05 +08:00 |
|
 Your NameandClaude Sonnet 5
|
ded440078a
|
补上用户名注册路径缺失的"无法自助找回密码"提醒文案
需求文档 1.4 第5条硬性要求:用户名方式注册不发送短信,必须提示用户"未绑定手机号
无法自助找回密码,忘记密码后只能联系企业管理员重置"。这句话在注册表单里从未真正
写出来过——不是视觉上被样式盖住,是 JSX 里压根没有对应节点,选择用户名注册的人
完全不会被告知自己放弃了手机号找回密码的能力。
修法:在邀请码输入框下方补上这行提示,只在 usingPhone 为假(用户名注册路径)时
展示,不影响手机号注册路径(那条路径本来就能自助找回密码)。参考图"注册页面.png"
是两个字段都还没交互的初始态,不代表这条路径相关的提示不应该出现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:20:29 +08:00 |
|
 Your NameandClaude Sonnet 5
|
46a98782fd
|
修复"退出当前登录"仍用原生 window.confirm(),与其余三个确认弹层不一致
现象:清除缓存/清除聊天记录/注销账号三处早前已经统一改成组件内 ConfirmSheet 弹层
(本文件其它三处组件自己的注释也在讲这次统一),唯独"退出当前登录"还留着最原始的
window.confirm()。本轮测试发现这在当前浏览器自动化环境下会被判定为"环境不支持同步
弹窗"而直接返回 false(控制台可见"Page dialog suppressed"警告),退出登录点了完全
没反应,且没有任何错误提示——这不只是测试环境的偶然状况:不少内嵌 WebView/嵌入式
容器场景本身就会禁用 confirm()/alert() 这类阻塞式原生对话框,真实用户在这些环境下
点"退出登录"会遇到一模一样的静默无效。
修法:新增 LogoutConfirm 组件,复用同一个 ConfirmSheet(图标 logout,标题"退出登录"),
"退出当前登录"按钮改成 setSheet('logout') 打开这个弹层,与 CacheClear/
ClearHistoryConfirm/DeleteAccountConfirm 完全同构,不再依赖浏览器原生对话框。
验证:npm run build:dev 后在浏览器实测,点击"退出当前登录"正确弹出本地弹层(标题/
文案/确认取消按钮与设计一致,logout 图标正常渲染为图形而非文字),点"确认"后真实
退出登录、回到企业码入口页;点"取消"正确关闭弹层且不登出。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:19:45 +08:00 |
|
 Your NameandClaude Sonnet 5
|
eaafabe94f
|
修复用户名+邀请码注册(无手机号)必现崩溃:Tinode.credential(null) 抛异常
现象:企业码注册流程走"仅用户名+邀请码,不填手机号"这条路径时,点"注册"必然抛出
原始 JS 报错"Cannot destructure property 'val' of 'meth' as it is null",直接
展示在错误提示条上,注册无法完成。
根因:auth-onboarding-view.jsx 的 Register 组件在没有手机号凭证、只有邀请码时,
把 cred_ 显式传成 null(没有可附加的凭证)。tinode-sdk 的 Tinode.credential(meth,...)
用 `typeof meth == 'object'` 判断参数是不是一个 {meth,val,resp,params} 对象,但
JS 里 `typeof null === 'object'` 也是 true,导致对 null 无条件解构,直接抛异常。
所有走"登录"分支的调用点传的从来都是"字段可能是 undefined 的对象",从未传过字面量
null,所以这个 bug 只在"用户名注册且没有手机号凭证"这一条路径上必现。
修法:cred_ 为空时不再调用 Tinode.credential(),直接把 cred 字段设为 undefined,
绕开 SDK 这个不处理 null 输入的缺陷,不改动 vendored 的 tinode-sdk 代码。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:19:26 +08:00 |
|
 Your NameandClaude Sonnet 5
|
fa2c6c2f07
|
修复会议详情页响应会议后toast文案"已已接受"/"已已拒绝"重复"已"字
MeetingDetail.respond()提交回应成功后用 `已${RESPONSE_LABEL[response]}` 拼toast文案,
但RESPONSE_LABEL的accepted/declined两项本身已经是"已接受"/"已拒绝"(给参与人列表
徽标用的),再拼一次"已"前缀就变成"已已接受"/"已已拒绝";tentative没有"已"前缀,
拼出来是"已待定",语义也不通顺。同页面另一个响应入口MeetingCard.respond()
早就手写了三条正确文案(已接受会议邀请/已拒绝会议邀请/已标记为待定),
两处本应一致却各写一套。
改为复用MeetingCard已有的正确文案,不再从RESPONSE_LABEL派生。
已用kcg051307发起、asn494180作为参会人的3场真实会议在浏览器逐一实测三种回应,
确认toast分别正确显示"已接受会议邀请"/"已拒绝会议邀请"/"已标记为待定"。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:18:19 +08:00 |
|
 Your NameandClaude Sonnet 5
|
db75aaa25b
|
修复会议详情页"参会确认"统计遗漏"待定"响应,三项数字之和对不上参会人数
MeetingDetail 的 counts 对象本就统计了 tentative({accepted,pending,declined,tentative}),
但渲染只画了已接受/待响应/已拒绝三项,待定响应被计算出来后从未显示。真实复现:
组织者建会议、参会人回应"待定"后查看会议详情,"参会确认"区域三个数字之和小于
participant_count,且没有任何说明缺的那一份去了哪。
补充第4个统计项(待定),保持原有三项顺序不变;base.css 的
.meeting-status > div 网格列数从3改4(.report-statistics 复用同一条规则,
拆开单独设置避免影响日报/月报团队统计的应提交/已提交/未提交三列布局)。
已用 asn494180(组织者)/kcg051307(参会人) 两个真实账号在浏览器实测:kcg回应"待定"后
version从1变为2,asn的会议详情页刷新后正确显示"1 已接受/0 待响应/0 已拒绝/1 待定",
截图确认4列布局无溢出/挤压。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 13:14:10 +08:00 |
|
Your Name
|
693265658f
|
fix(导航): 关闭聊天/信息面板改为真实返回,不再新增历史记录
用户反馈:从会话列表打开聊天A,点页面自己的"返回"关掉聊天,再打开聊天B,这时候点
浏览器的返回键,会先回到列表,再点一次返回又跳回了聊天A——因为"关闭"这个动作和"打开"
一样都在往浏览器历史里push新记录,而不是弹出(pop)"打开"时push的那条,导致聊天A那条
记录一直遗留在历史栈里,多按一次返回就会被重新翻出来。
两处受影响、同一根因:
- tinode-web.jsx 的 handleHideMessagesView(移动端聊天页左上角"←",messages-view.jsx
的#hide-message-view)——打开聊天(handleTopicSelected)是真实push,关闭时原来无条件
再push一条"没有话题"的状态,现在改成先尝试HashNavigation.goBack(),找不到可回退的
历史才退回原来的push方式。
- info-view.jsx 的 handleBackNavigate(旧版InfoView自己的返回/关闭按钮,目前只有"已
保存的消息"这一个话题还在用它,好友/群的资料页已经在更早的章节改用真正修过返回逻辑
的friend-settings/group-settings)——同样的问题,同样的修法。
已在浏览器验证:连续"开聊天A→点返回关闭→开聊天B"后,浏览器返回键能一次性回到聊天A
从未存在过一样,不会再被重新翻出来;InfoView的关闭按钮点击后hash是异步变化的(真实
history.back()的特征),不再是原来同步的push。
|
2026-09-06 12:29:56 +08:00 |
|
Your Name
|
4fc65b0f40
|
fix(日报): "提醒未提交成员"改用组织架构接口凑候选名单,不再必现400
后端问题清单"新增2"记录过:POST /reports/daily/reminders 要求调用方传入完整候选
user_ids 名单(服务端只做"从候选里筛出未交的人再提醒",不会自己算全公司名单),
但前端一直传空数组,必然命中服务端"user_ids为空"的400校验。
重新核实时发现这条其实不是后端缺口——GET /organization/members 不传 q/department_id
时就会返回调用者可见范围内的全部在职成员(分页),这个接口本来就在会议参会人选择器
里用着,只是"提醒未提交成员"这个按钮没有复用它。改为点击时先分页拉取全部组织成员
user_id,再传给提醒接口;组织架构为空时给出明确提示而不是让请求再次落入400。
已在浏览器验证请求格式(limit=100,与后端实际接受的参数一致)和空组织架构下的
兜底提示;当前测试租户组织架构数据本身为空,未能覆盖"真的有人被提醒"这条路径,
但接口契约已对照 im_be 源码逐字段核实。
|
2026-09-06 12:07:24 +08:00 |
|
Your Name
|
623f7dc40f
|
fix(导航): 排查全项目后修复其余同类tab切换污染历史记录问题
上一提交(e61eb402)只修了"我的收藏"/"查找聊天记录"两处,用户要求排查其他页面是否
也有同样问题。全项目排查<nav>/tabbar/active态切换元素,逐一确认onClick是否直接调用
HashNavigation.navigateTo()(每次都push一条历史记录),找到5处同类实例并修复:
- message-center-view.jsx / workplace-collaboration-view.jsx:底部导航"工作台"<->"消息"
两个tab——这是全应用唯一的根级tab bar,也是点击频率最高的一处,之前同样每次切换都
push一条历史记录。
- workplace-collaboration-view.jsx:日报/月报列表页的"我的xx"<->"团队统计"切换(同一个
组件、同一个页面,只是team boolean不同),同样的问题。
- new-topic-view.jsx:"开始新会话"页的搜索/新群组/通过id三个tab。
- wallpapers.jsx:"壁纸"设置页的图案/图片两个tab。
均改用上一提交新增的HashNavigation.navigateReplace()。已在浏览器逐一实测:连续切换
多次tab后,无论真实浏览器返回还是应用自己的返回按钮,都能一次性返回到进入该页面之前
的真实上一页。
以下经排查确认不受影响,未改动:group-management-view.jsx的群聊分类tab(我创建的/
管理的/加入的)和contact-management-view.jsx的字母索引锚点均只用本地state/scrollIntoView,
不涉及hash跳转;auth-onboarding-view.jsx的登录<->注册切换、new-topic-view.jsx提交后的
三处跳转是真实的"离开当前页"操作,保留push语义符合预期。
|
2026-09-06 11:16:01 +08:00 |
|
Your Name
|
e61eb40250
|
fix(收藏/聊天记录检索): 切换页内tab不再污染浏览器历史记录
用户反馈"我的收藏"页面点击tabs切换(全部/图片/视频/文件/语音/文本/链接)会记录到浏览器
历史记录里,导致点击返回一直在这个页面内切换tab,而不是真正返回上一页。
根因:content-library-view.jsx的Favorites/HistoryNav每次切tab都调用navigate(),
即HashNavigation.navigateTo() -> location.hash = ...,每次赋值都会被浏览器记为一条
新的历史记录。切N次tab就push N条记录,无论是系统返回键还是应用自己的"返回"按钮
(HashNavigation.goBack()),都要先把这N条tab切换记录走完才能真正离开页面。
修复:新增HashNavigation.navigateReplace(),用history.replaceState替换当前历史记录
而不是新增一条(hash该更新还是更新,刷新/复制链接不受影响),并手动派发hashchange事件
供tinode-web.jsx现有的监听器捕获——replaceState本身不会自动触发该事件。Favorites的
TYPE_LABELS tabs和HistoryNav("查找聊天记录"页的聊天记录/图片/视频/文件/链接tabs)
两处tab切换都改用这个新方法,不再计入navigationCount。
已在浏览器实测两处:连续切3次tab后,无论是原生history.back()还是应用返回按钮,都能
一次性返回到进入这个页面之前的真实上一页,不再卡在tab切换历史里。
|
2026-09-06 10:45:24 +08:00 |
|
Your Name
|
01325ad009
|
fix(css): 补齐按钮内标题/内容间距异常的遗漏实例
用户反馈"我的收藏-文件"tab标题(strong)与内容(small)间距过大,根因是全局
`button{line-height:2.2rem}`重置(面向单行居中按钮文案)继承进了改造成
卡片/多行行容器的<button>内部未显式覆盖line-height的<strong>/<small>。
本次除修复用户直接指出的.library-file-card/.library-record-header外,
系统排查全项目同类模式(button内嵌套strong+small),额外修复3处此前遗漏:
- .library-message-results(查找聊天记录-聊天记录tab搜索结果)
- .personal-identity("我"页头像+昵称+企业名身份卡片)
- .report-cards > button(月报列表行,日报列表用article非button故不受影响)
均已在浏览器实测line-height从2.2rem降至1.3,视觉间距恢复正常。
|
2026-09-06 10:40:40 +08:00 |
|
 Your NameandClaude Sonnet 5
|
150db2fc02
|
重新构建dev产物(消息首页会话预览修复)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 10:16:55 +08:00 |
|
 Your NameandClaude Sonnet 5
|
2ee8bac6a0
|
修复消息首页会话预览恒为"暂无消息",即使会话有真实历史消息
message-center-view.jsx 的预览文字读 topic.latestMessage(),这个方法
只会从话题自己的本地消息缓存里取值——而这个缓存只有在话题真正被
subscribe 过一次数据拉取后才会填充。me.contacts()(resetContactList)
给出的是每个联系人真实的、已缓存的Topic实例,包括用户这次会话里
从未打开过的——这些话题的latestMessage()恒为undefined,即使同一个
对象自己的touched/seq字段能证明服务端真实存在历史消息。
新增prefetchLatestMessagePreviews():对chatList里seq>0但本地缓存为空
的话题,一次性拉取最新1条消息填充预览。用一个Set跟踪已尝试过的话题,
避免resetContactList()(几乎每个{meta}/{pres}事件都会触发一次)反复
重新拉取同一个话题。
调试过程中发现并修正了自己犯的两个错误:(1) resetContactList()在
websocket握手完成前就会被本地持久化缓存恢复触发一次,此时的subscribe
必然503失败,加了isConnected()判断跳过,避免过早失败把话题永久标记
为"已尝试";(2) Topic.subscribe(getParams, setParams)的参数顺序搞反,
把查询对象传成了第二个参数(setParams),导致subscribe成功但发出的
{sub}包完全没有get子句,从未真正请求过消息数据。
已用真实浏览器验证:好友kcg051307会话(真实71条历史消息)刷新页面
后预览从"暂无消息"变为真实的最新消息内容,未读角标数字不受影响
(确认没有误将未读消息标记为已读)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 10:16:48 +08:00 |
|
 Your NameandClaude Sonnet 5
|
ca71667975
|
重新构建dev产物(收藏文本折叠/搜索高亮、跳转定位消息两处修复)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 05:31:00 +08:00 |
|
 Your NameandClaude Sonnet 5
|
68044fa1bb
|
修复"查找聊天记录"跳转定位到原消息在全部入口100%失效
componentDidUpdate里的jumpToSeq检查只在这个值发生变化时触发,但
tinode-web.jsx从不会同时渲染MessagesView和第7章侧边栏(!chapterSevenActive
门禁),所以从History的搜索结果/图片宫格/视频宫格跳转,每次都是卸载
第7章视图、挂载一个"首次渲染就已经带着jumpToSeq"的全新MessagesView
实例——componentDidUpdate根本不会跑(它不处理首次挂载)。7.12/7.15/
7.16全部"定位到消息来源"入口100%失效:jumpseq参数永久留在URL里,
会话固定停在最底部。
修复过程中还发现一个真实的竞态:首次挂载意味着componentSetup()刚刚
才发起topic订阅(异步、fire-and-forget),jumpToMessage自己的
topic.getMeta()调用要求订阅已建立,立即调用会抢在订阅完成前——实测
这个抢跑的getMeta()调用真实收到了服务端 {"code":403,"text":"permission
denied"} 拒绝(不是本地兜底,是真实的服务端往返),而真正的订阅请求
当时还在路上。改为轮询已有的isSubscribed()标志(而不是猜一个固定延迟
或改造subscribe()暴露其内部promise),封顶5秒,真正卡住的订阅仍会
落到jumpToMessage自己已有的错误处理上。
第十二轮最小粒度测试 Ch7 发现,已用真实浏览器验证:从工作台等其它
页面跳转过来时,消息正确加载、jumpseq参数正确从URL清除。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 05:30:53 +08:00 |
|
 Your NameandClaude Sonnet 5
|
6882103461
|
实现收藏文本长文本折叠/展开/复制、收藏搜索关键词高亮
需求7.4两处此前完全没接:文本收藏是不限高度的纯<p>,长文本不折叠、
没有展开/收起,也没有复制按钮;收藏搜索的query从未传到文本收藏这一
层,History那边已有的Highlight组件在Favorites里从未被引用(实测:
搜索一个真实命中的关键词,结果列表0个<mark>)。
新增FavoriteText组件:用scrollHeight/clientHeight判断是否真的溢出
(避免给短文本也显示无用的"展开"按钮),复制走和context-menu.jsx
的copyMessageText一致的navigator.clipboard.writeText。
-webkit-line-clamp在当前环境对这个<p>不生效(getComputedStyle能读到
设置值但视觉不裁切),改用line-height反推的max-height+overflow:hidden;
外层容器不能用display:flex,会被UA blockify成flow-root导致同样失效。
第十二轮最小粒度测试 Ch7 发现,已用真实浏览器验证展开/收起切换正常、
搜索关键词高亮正确命中。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 05:30:40 +08:00 |
|
 Your NameandClaude Sonnet 5
|
cccd21aff1
|
修复通讯录字母索引条位置错误,与好友列表重叠
.people-index 用了固定 top:13rem,是"新朋友/群组/文件助手/系统消息"
四行快捷入口成为现在这个样式之前遗留的旧值——实测索引条渲染在约173px
处,而按字母分组的好友列表实际从约375px才开始,索引条整整偏高200px,
盖在快捷入口行上而不是贴着好友列表。按真实布局(header+搜索框+四行
入口+分组标题外边距)重新算出27.5rem。
第十二轮最小粒度测试 Ch5-6 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 05:13:12 +08:00 |
|
 Your NameandClaude Sonnet 5
|
5b836d50b2
|
修复通话发起后立即退出登录导致应用整体白屏崩溃
handleLogout()先断开WS连接、再重置界面状态,界面重置卸载
MessagesView时其componentWillUnmount会尝试发一条挂断信令,vendored
SDK的Connection.sendText()在已关闭的socket上同步抛错,componentWillUnmount
里没有error boundary兜底,导致整个应用被React卸载成白屏。
5处几乎相同的通话信令发送方法(ringing/hang-up/offer/ice-candidate/
answer)合并成一个sendCallSignal()辅助方法,发送前检查连接状态、
发送本身包一层try/catch。
第十二轮最小粒度测试 Ch8 发现(点击通话按钮后立即退出登录复现)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 05:09:23 +08:00 |
|
 Your NameandClaude Sonnet 5
|
4a140e6c79
|
修复消息首页空状态误导文案、未登录完成时静默显示空列表
会话列表为空时统一显示"未找到相关会话"+搜索图标,即使用户根本没有
输入过搜索词——账号本身还没有任何会话时,也被说成"你的搜索没找到东
西"。现在区分:真正搜索无果才显示这句,否则显示"暂无会话"。
另外,myUserId尚未就绪(自动登录未完成/失败)但sidePanelSelected已
经是messages-home(比如收藏的旧链接、恢复的历史标签页)时,原来的
offline判断条件恒为false,页面会静默渲染一个和"账号确实是空的"没法
区分的空列表,用户以为账号没数据而不是还没登录成功。现在myUserId为
空时也按"未连接"处理,诚实展示"加载中"。
第十二轮最小粒度测试 Ch1-2 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 04:59:35 +08:00 |
|
 Your NameandClaude Sonnet 5
|
8575c83980
|
修复举报原因列表文案裁切、举报详情页原因标签换行错位
两处grid都按"预留图标位"设计成3栏(2.7-2.8rem首列+内容+尾部),但
ReportReasons/ReportDetails实际渲染的都只是纯文字span,没有图标元素
——真实文案被挤进过窄的图标列:举报原因列表里"诈骗欺诈"等文案裁切成
看不懂的碎片(部分文字还叠在蓝底图标框上导致白底白字不可见);举报
详情页"举报原因"标签被挤到换行。改成按真实子元素数量分栏(2栏)。
第十二轮最小粒度测试 Ch10-11 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 04:58:36 +08:00 |
|
 Your NameandClaude Sonnet 5
|
052cedb39b
|
重新构建dev产物(实名认证标题、会议响应按钮门禁两处修复)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 04:23:54 +08:00 |
|
 Your NameandClaude Sonnet 5
|
22468edf6b
|
修复已取消/不可响应的会议列表卡片仍提供拒绝/接受按钮,点击后端403
MeetingCard对拒绝/接受按钮的渲染没有像MeetingDetail那样按
permissions.can_respond门禁,取消后的会议(my_response仍是
pending,属正常状态)会一直显示看似可用的按钮,点击后PUT
.../my-response正确返回403 PERMISSION_DENIED,用户点了却没反应。
第十二轮最小粒度测试 Ch9 发现,用真实取消会议(mtg_0w2TnfQiqaMwy8Bj)
复现(该agent进行中因会话限流中断,此项修复在中断前已完成,事后
确认无误后提交)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 04:23:48 +08:00 |
|
 Your NameandClaude Sonnet 5
|
164a41270c
|
修复实名认证已认证状态显示臆造标题"认证信息"
参考图(实名认证.png / 实名认证-已实名.png)里两种状态标题文案完全
一致,都是"请您使用有效身份证信息认证"——原来按status分叉出的"认证
信息"这句是mock阶段从未核对过参考图的臆造文案。
第十二轮最小粒度测试 Ch1-2 发现(该agent进行中因会话限流中断,此项
修复在中断前已完成,事后确认无误后提交)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 04:23:44 +08:00 |
|
 Your NameandClaude Sonnet 5
|
1e5e64ae2c
|
重新构建dev产物(撤回菜单误判、验证码文案、文本收藏撤回态三处修复)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 02:13:55 +08:00 |
|
 Your NameandClaude Sonnet 5
|
b5531b0b7f
|
修复文本收藏/聊天记录检索不识别已撤回状态、P2P来源收藏名称解析404
问题13(收藏availability恒为available)后端修复后暴露的真实缺口:
text类型收藏和History的history-search结果都只判断item.type,从不看
availability,撤回的文本消息会原样一直显示旧内容(History里甚至会
显示后端messageText()对nil content兜底出的字面量"null")。
另外,P2P来源的收藏来源名称解析函数把复合p2p话题id当用户uid传给
/users/{id}/profile,必现404,永远显示原始话题字符串而非好友昵称;
改用已有的getConversationContext接口正确解析。
第十一轮最小粒度测试 Ch7 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 02:13:49 +08:00 |
|
 Your NameandClaude Sonnet 5
|
117bf43c25
|
修复身份验证输错短信验证码时显示"独立密码不正确"
CODE_INVALID 这个reason同时被独立密码校验和短信验证码校验两个不相关
接口复用,错误文案只写了前者的措辞,在身份验证页答非所问。改成对两
种场景都准确的通用措辞。
第十一轮最小粒度测试 Ch10-11 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 02:13:40 +08:00 |
|
 Your NameandClaude Sonnet 5
|
fc757cc043
|
修复消息发送失败/排队中时长按菜单仍提供"撤回"
判断只查 params.seq > 0,未排除 tinode-sdk 本地占位 seq(LOCAL_SEQID,
268435455,同样是正数)——发送失败/仍在排队的消息会被误判成"已持久化
的真实消息",点击撤回后端正确拒绝(无此消息),却给用户一个答非所问
的"请求内容有误"提示。
第十一轮最小粒度测试 Ch3-4 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 02:13:34 +08:00 |
|
 Your NameandClaude Sonnet 5
|
89fdd31cd4
|
通讯录/新朋友/黑名单列表区分"加载失败"与"真的是空"
三处都用 .catch(() => {}) 吞掉网络错误,loading 结束后空数组直接落到
"暂无好友"/"暂无新朋友申请"/"黑名单为空",加载失败和真的没数据在界面
上无法区分。改成 group-management-view.jsx 里 11 处同类组件已经在用
的 loadError 状态模式。
第十轮最小粒度测试 Ch5-6 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 00:57:17 +08:00 |
|
 Your NameandClaude Sonnet 5
|
4dbe76f2bd
|
重新构建dev产物(注册持久化登录、白屏崩溃、视频缩略图三处修复)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 00:49:13 +08:00 |
|
 Your NameandClaude Sonnet 5
|
bab1f13e04
|
修复视频收藏/聊天记录缩略图恒为破损图片、时长换算错误
MediaThumb 把视频实体自身字节(.val/.ref,给 <video> 解码用)当图片
塞进 <img src>,浏览器无法解码,缩略图恒为损坏图标;改用后端已下发但
从未使用的 .preview/.premime 海报字段(lib/formatters.js 的
quoteImageOrVideo()/inlineVideoAttr() 早已用同一约定)。
时长换算:收藏页把毫秒当秒数传给 secondsToTime,2.4秒视频显示
"39:26";History 媒体宫格则完全没接上 duration 字段(mediaMessagePayload
是扁平结构,需要跟 history-files 一样重建 Drafty 实体才能取到)。
第十轮最小粒度测试 Ch7 发现(纠正第九轮"证据不足"的结论)。已用真实
收藏视频实测确认:缩略图正确解码(320x240/96x96),时长正确显示 0:02。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 00:49:04 +08:00 |
|
 Your NameandClaude Sonnet 5
|
9d2db5a648
|
修复转发/切换话题时应用整体白屏崩溃
componentDidUpdate 的 initMessage 分支直接访问 this.messageEditArea
未判空,而输入框在 noInput(转发中)或 disabled(ACS未解析完)时会
被卸载——此时 initMessage(草稿/编辑内容)一变化就会解引用 null,
无 error boundary 兜底,整个应用白屏。
第十轮最小粒度测试 Ch3-4(实测复现2次)与 Ch8(同一处代码路径独立
命中)各自发现同一个bug。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 00:46:37 +08:00 |
|
 Your NameandClaude Sonnet 5
|
35f659249e
|
修复注册流程不写入持久化登录态、注册失败500错误显示原始英文
Register组件缺少Login同款的keep-logged-in挂载副作用,导致注册产生的
新账号在下一次刷新时静默丢失会话(无报错、无跳转登录页)。
另外注册走原生WS {acc}协议而非REST,未接入CTRL_ERROR_TEXT_CN翻译表,
"internal error (500)"等错误会原样展示给用户。
第十轮最小粒度测试 Ch1-2 发现。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-06 00:46:27 +08:00 |
|
 Your NameandClaude Sonnet 5
|
eb4c1cd8da
|
用登录页现有logo重新生成PWA图标/favicon/OG分享图;顺手发现并重建了严重过期的生产构建产物
图标部分:logo.svg/logo96.png/logo192.png/logo32x32.png/logo32x32a.png原来画的是开源Tinode
项目自己的蓝绿橙"t"speech-bubble标志。用登录页AppMark组件的真实CSS参数(白色圆角方卡 + 旋转
-18deg的蓝色squircle + 加粗白色首字母,色值#1677ff,圆角50% 50% 50% 18%)原样重新画成独立
的SVG/PNG资源,不是另起一套新设计——文字/图标/圆角比例/旋转角度均按登录页实测的computed style
数值1:1还原。favicon(32x32)的"未读"变体(右下角绿点)沿用原有的visual language。
og-logo.jpeg(社交分享预览图,og:image meta标签用)问题比图标本身更明显:原图整个背景是金门大桥
照片,前景写着"Tinode / Open Source Instant Messenger",这两行文字是本次engagement里最直接的
一处Tinode品牌泄漏(没有隐藏在代码里,链接分享出去别人就直接看到)。改成深色渐变背景+同一套
logo mark+"MeetingIM"+"即时通讯",不再引用任何第三方项目的名字或素材。
意外发现:umd/index.prod.js(生产构建产物,index.html的<script>直接引用)从很早之前就没有
重新构建过,仍然包含"TinodeWeb"/"web.tinode.co"/tinode.co的隐私政策与服务条款链接/
support@tinode.co邮箱等一整套本轮及更早各轮已经在源码里修掉的Tinode字样——如果这份产物被
原样部署,之前所有品牌清理都不会真正体现在生产环境。已用`npm run build:prod`重新构建(干净
通过,只有两条跟本次改动无关的资源体积警告),确认重建后的产物里"MeetingIM"正确替换了旧品牌名、
真实域名替换了tinode.co。这次只重新生成了产物文件,没有执行任何部署动作。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 23:30:11 +08:00 |
|
 Your NameandClaude Sonnet 5
|
ee2cf6f965
|
修复收藏/聊天记录检索的请求竞态、下载页按钮占一半宽度、两个类名零CSS覆盖
- Favorites/History 组件的 load() 原来没有过期响应保护:切换类型/输入搜索词都会触发新请求,
但如果一个更早发出的、范围更宽的请求(比如"全部")比后发的窄范围搜索请求更晚返回,会无条件
覆盖掉已经正确显示的搜索结果——实测复现过"搜索框显示查询词,列表却是全量未过滤结果"。改成
每次调用 load() 时认领一个自增计数器,响应到达时如果计数器已经被更新的调用推进过,直接丢弃
这个过期响应。History组件(按topic/tab/query切换)是同一个模式,一并修复。
- `.library-download-actions`(下载页loading/done两态的按钮容器)是2列等宽grid,但两个状态
永远只渲染1个按钮——按钮固定占左侧50%宽度,右侧约一半容器宽度是死空白。改成单列。
- `.library-empty-hint`(收藏/聊天记录/下载页的加载中提示文字)和`.library-load-more`
("加载更多"按钮)在base.css里完全没有对应规则,退化成浏览器默认段落间距/按钮外观——
补了基础样式,跟第八轮修复的.library-link-text/.library-record-header是同一类遗漏。
均已重新构建并实测验证(下载按钮宽度、空状态文字样式、加载更多按钮样式均用真实DOM测量确认;
请求竞态修复通过真实收藏页搜索/清空往返验证无回归,竞态本身的精确时序难以在测试环境里稳定
复现,但修复采用的是标准、成熟的自增计数器丢弃过期响应模式)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 18:46:29 +08:00 |
|
 Your NameandClaude Sonnet 5
|
38b43936ce
|
修复桌面端会话内错误提示完全不可见、收藏发送面板显示空白/裸类型名、delivery-marker图标className拼出undefined
- 桌面布局下打开一个会话时,sidepanel-view.jsx 走的是 messages-home 分支(渲染
MessageCenterView),这个分支从来没有渲染过ErrorPanel;messages-view.jsx自己的ErrorPanel
又只在displayMobile时才渲染——两边一凑,桌面端(最常见的使用状态)里任何onError(...)调用
(上传失败、收藏转发失败、URL风控拦截等)全部没有可见的展示出口,用户只会觉得"点了但什么
反应都没有"。已用真实handleError()调用实测确认:修复前DOM里根本没有.info-box节点,修复后
正确渲染且visible。
- "我的收藏"发送面板:favoriteSummary()对图片/视频收藏显示空白(真实数据里这两类的
snapshot.content.txt是占位用的单个空格,被直接当文本展示)、对文本类收藏显示裸露的内部
类型字符串"text"(FAVORITE_TYPE_LABEL没有text这个key,且文本收藏的content本身就是纯字符串
而非{txt:...}对象,原逻辑没处理这种形状)。改成图片/视频/语音三类固定用类型标签、文本类型
优先判断content本身是不是纯字符串、并给FAVORITE_TYPE_LABEL补上text条目。
- received-marker.jsx的已发送/已送达图标(deliveryMarker对这两种状态返回的对象没有color字段)
拼接className时把undefined直接字符串化拼了进去,实际渲染成"material-icons small undefined"。
均已重新构建验证;received-marker/favoriteSummary为纯函数级修复,逻辑经过仔细核对但未逐一
截图复核(不依赖运行时状态,风险低)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 18:42:08 +08:00 |
|
 Your NameandClaude Sonnet 5
|
47148aa75d
|
修复登录页浏览器后退卡死、已认证态下登录/注册的凭据校验绕过、系统消息分页丢失等第1-2章问题
- 最主要的一处:登录成功后点浏览器"后退",会回到一个仍然认为"已登录"的底层连接、但重新挂载出来
的登录表单——按钮永久卡在禁用的"登录中…"(loginDisabled只有失败分支会重置,成功分支从来没有,
因为正常路径下这个页面早就导航走了,没人会看到);如果不管这个直接切换企业码后再次提交,
doLogin()/handleNewAccountRequest()开头的"已认证就直接放行"逻辑会让登录悄悄跳过凭据校验,
或者让注册请求收到后端原始英文错误"already authenticated (409)"。根治方式是在
handleHashRoute加一道门:hash指向登录/注册等纯"未登录态"视图、但连接其实已认证时,直接重定向
离开,不让这类表单有机会被重新渲染出来——已用真实浏览器验证4个入口(login/register/tenant/
reset/cred)全部正确重定向,且确认不影响真正的登录后安全类页面(如identity-check)。
handleLoginSuccessful也顺手补了loginDisabled:false兜底。
- 系统消息列表:后端`GET /system-notifications`明明返回`next_cursor`表示还有更多,前端从来没用过,
未读50+条永远只能看到最近20条、永远没法真正清零。补了"加载更多"分页——已用真实账号(60+条历史
通知)验证,点击后正确追加下一页、游标耗尽后正确不再显示按钮。
- 已处理(通过/拒绝)的好友申请通知,原来只要event_type以"friend.request"开头就显示"查看新朋友"
按钮——不区分"待处理"和"已是历史结果",点了跳到待处理列表也找不到对应项。改成只在
`friend.request.created`时显示,已用真实账号里同时存在created/accepted/rejected三种通知
验证行为符合预期。
- 注册页四处不统一的占位品牌名(讯工捷迅/捷信密使/捷讯/即时通讯,没有一个是真实品牌名)与
系统消息页脚"即时通讯安全团队",统一改用BASE_APP_NAME("MeetingIM")。
- config.js的KNOWN_HOSTS.hosted从web.tinode.co改为web.118im.com(这是WS连接失败时的最终
兜底地址,指向无关第三方域名不只是品牌问题,是真实会连错服务器);host-name.js的
detectServerAddress()原来只要hostname是localhost就无条件返回硬编码的localhost:6060,
完全不管页面实际监听的端口——本项目自己的开发代理跑在8093,清缓存后首次连接必然先撞一次
死连接,改成和非localhost分支一样用页面自己的真实端口。
- 实名认证提交成功后,姓名/证件号输入框原来会变成空白(后端成功响应不含masked_name/
masked_id_number,组件切到只读态时又没有回退到用户自己刚输入的草稿值)——改成没有脱敏字段时
回退显示用户本次会话里刚输入的原始值,不是留白。
- Firebase推送通知初始化失败原来复用登录/注册表单的错误提示通道,可能盖住一个同时发生的真实
登录错误,或在用户还没操作登录表单时就先弹出一条无关提示——推送本身是锦上添花功能,改成只
console记录+静默关闭开关,不再抢占登录页的错误展示位。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 18:36:03 +08:00 |
|
 Your NameandClaude Sonnet 5
|
8b51fd3eb8
|
修复通讯录顶部入口箭头图标错位、群管理员列表移除后读到服务端过期缓存
- .people-entry-list > button 是4列grid,最后一列专留给箭头图标,但"新朋友"未处理申请数为0/
"群组"/"文件助手"/"系统消息"这几个入口没有badge时,JSX只渲染3个真实子元素,grid自动布局
把箭头挤进第3列——实测空出623px空白,图标紧贴文字,完全不是贴右对齐效果。改成显式
grid-column:4 钉住箭头位置,不再依赖前面到底渲染了几个兄弟节点。
- AdminList移除管理员后紧接着的load()重新查询命中了服务端对这个查询形状的过期缓存,界面上
刚移除的管理员会短暂"复活"。改成移除成功后直接用已确认生效的结果本地更新state+patch共享
缓存,不再指望立即重新查询能反映刚发生的变更(这个缓存问题本身是后端"新增7"已记录的问题,
重新查询本身没有办法绕过,前端这次做的是不依赖它)。
均已用真实DOM结构/computed style确认修复生效。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 18:23:19 +08:00 |
|
 Your NameandClaude Sonnet 5
|
7db687602b
|
修复移动端语音通话胶囊被撑成方块、来电页认证徽章在长姓名截断时完全不可见
- #video-container.minimized 的移动端媒体查询规则(宽度100%/高度11rem/方角,本是给视频悬浮窗
修的)和 #video-container.audio-minimized(语音胶囊自己的auto/auto/无阴影)特异度相同
(1,1,0),前者写在文件更后面、源码顺序上赢了——语音通话点最小化后,移动端下胶囊被强行撑成
贯穿屏幕顶部的方形透明条,胶囊本体和还原按钮挤在左上角。补 :not(.audio-minimized) 排除。
- 来电全屏页认证徽章原来和姓名文字共用同一个 white-space:nowrap+ellipsis 的<h1>,姓名够长
触发省略号时,排在姓名后面的徽章(同一行内容里靠后的部分)跟着一起被截断出可视区域之外——
不是"字体显示不全",是整个徽章都看不见。改成flex布局:姓名单独一个可截断的flex子项,
徽章作为flex:none的兄弟节点,不参与省略号裁切,姓名截多短徽章都能跟在后面。
均已用真实DOM结构/getComputedStyle确认修复生效。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 18:19:58 +08:00 |
|
 Your NameandClaude Sonnet 5
|
d573b38d10
|
第九轮(部分):修复日报徽章配色、彻底重做PIN定位方案、清理静态文件品牌残留
- 日报卡片"已提交/未提交"状态徽章补色:DailyReportCard用<article><header><em>结构,原选择器
要求.report-cards直接子<button>,日报卡片永远命中不了、只落到不分状态的兜底样式;月报因为
行本身就是<button>所以没受影响。详情页.report-author em同理补了区分色(原来恒为绿色,"未
提交"用绿色在语义上很怪)。
- 青少年模式/独立密码设置的PIN输入定位改用结构性方案,不再靠猜一个rem偏移量:给
.auth-pin-boxes/.auth-pin-input包一层.auth-pin-wrap,输入框inset:0精确覆盖方块的真实
渲染框,不管外层页面有没有标题、padding多少都自动对齐。上一轮的补丁(.youth-password-gate
.auth-pin-input{top:1.75rem})把重叠率从0%提到32.5%,本轮实测确认还是不够(方块下方
67.5%的面积点不中),根治后4个采样点(四角+中心)全部精确命中输入框。过程中额外发现并
修复了两个更隐蔽的层叠问题:全局input[type=text]的height:2.5rem特异度反超裸class、
textarea/input共享的margin:0.25rem重置——不清掉这两个会让inset:0看起来生效了、实际尺寸/
位置还是被悄悄改掉。
- .report-loading(举报原因页加载态)补基础样式,原来在base.css里零匹配,吃浏览器默认<p>样式。
- 清理静态文件里的Tinode品牌残留:index.html/index-dev.html的<meta>标签(application-name/
description/og:*/canonical,此前只改了<title>,meta标签一直没跟上)、manifest.json(name/
short_name/description改MeetingIM;prefer_related_applications+related_applications整个
删掉——这两个字段会让Android/Chrome"添加到主屏幕"时引导用户去装无关的第三方开源Tinode App,
不只是文案问题);删除.well-known/assetlinks.json(声明了无关第三方App对本域名的URL处理/
登录凭据代理权限,属于遗留模板残留,无真实对应关系,直接移除而非编造虚假内容)。
manifest.json里的图标文件(logo96.png/logo192.png/logo.svg)本身画的就是Tinode的蓝绿橙"t"标志,
是真实的视觉品牌泄漏,但重新设计一套图标涉及视觉识别决策,本次未动,留给用户决定。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 18:17:48 +08:00 |
|
 Your NameandClaude Sonnet 5
|
f688578714
|
清除缓存/清除聊天记录/注销账号确认弹层改为叠加显示,不再是整页路由跳转
这三个"确认"操作原来是独立的hash路由,点击后"设置"整个列表(含标题栏)连同Header一起卸载,
只剩ConfirmSheet的半透明蒙层盖在空白背景上——蒙层本来是设计成衬着被弹出的页面看的,但因为
底下真的没有渲染任何东西,视觉上跟跳转到一个新的空白页面没有区别。
改为SettingsList自己的本地state(sheet: null|'cache'|'history'|'delete'),确认层作为兄弟
节点叠加渲染,列表本身不卸载,不再经过hash导航——照抄group-management-view.jsx的OwnerSelect
(选择新群主)已经在用的正确模式。同时清理了tinode-web.jsx两处硬编码视图白名单里对应的
三个route名(不再是独立路由,无需保留)。
contact-management-view.jsx同类的删除好友/拉黑/清空聊天记录确认层核实过不受影响——它的
ConfirmSheet会重新渲染Settings页面作为背景,视觉上本来就是正确的。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 17:32:39 +08:00 |
|
 Your NameandClaude Sonnet 5
|
d759f67b11
|
第八轮最小粒度测试:修复CSS选择器/JSX结构不匹配的一整类bug,清理Tinode品牌残留
第八轮改用"读CSS选择器+核对JSX结构"的方式复测全部11章,命中了7轮纯接口测试从未发现的一类
问题——选择器写对了但目标元素的实际DOM结构/className对不上,样式静默失效:
- 语音通话最小化胶囊按钮尺寸被同特异度规则覆盖;移动端悬浮通话窗口left/right/width三值同时
生效导致过度约束;来电全屏页认证徽章/姓名标题缺少专属样式覆盖
- 我的收藏-语音卡片三列grid对应实际只有1个子元素,播放器被压缩到卡片左侧一角;链接/记录头部
完全没有基础样式,退化成浏览器默认外观
- 多选模式下"收到的"消息气泡被flex auto-margin一起拖到最右侧;.avatar-box/#send-message-panel
两处逗号断句错误导致后半句选择器丢失祖先限定
- 按住说话悬浮反馈渲染成内联胶囊,而参考图要求的居中悬浮卡片样式其实已经存在(挂在了另一个非
生产demo组件上);批量转发确认框沿用了80%视口高度;audio-player.jsx的playback类点在canvas
上而非包裹层,波形和计时器可能纵向堆叠
- "消息已撤回"占位符误拼了日期分隔条的class而非专属的deleted
品牌清理:浏览器标签页标题、未读数标题更新均硬编码"Tinode";LINK_CONTACT_US等三个链接常量
指向开源Tinode项目自己的域名(联系我们改为跟用户条款/隐私协议一致的toast,不再发起外部mailto)。
以及:空会话丢失.chat-box样式类、青少年模式PIN定位、若干收藏/文件类型展示细节。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 17:09:42 +08:00 |
|
 Your NameandClaude Sonnet 5
|
5e173c55e8
|
修复日报/月报详情页返回目标写死,团队视角进入会被送回"我的"列表
DailyReportCard/月报列表的报告条目在 team=true(团队统计)和 team=false(我的日报/月报)两种
模式下渲染的是同一个组件、同一个"查看详情"跳转,但 DailyReportDetail/MonthlyReportDetail
的返回目标写死成 daily-report-mine/monthly-report-mine——管理者从"团队统计"点进某条下属的
报告,返回却被送到自己的"我的日报/月报"而不是刚才浏览的团队列表。跳转时带上 team 参数,
详情页据此选择正确的返回目标,和同文件里 DailyReportList/MonthlyReportList 自己的
`team ? 'x' : 'workbench'` 返回逻辑保持一致模式。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 15:20:04 +08:00 |
|
 Your NameandClaude Sonnet 5
|
290fc80f3e
|
群二维码行的小图标也应跟箭头贴在一起靠右,而不是单独留在文字标签后面
上一次修复把 margin-left:auto 放在了这一行的最后一个图标(箭头)身上,箭头确实到了最右,
但二维码小图标是这一行倒数第二个子元素,没吃到这个 auto margin,还留在原位——变成"二维码
图标贴左、箭头单独贴右"两边分家。auto margin 改放到这一组里最靠前的图标(二维码图标)身上,
箭头作为它后面的下一个兄弟元素自然跟上,两个图标现在整组一起贴右。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 13:30:43 +08:00 |
|
 Your NameandClaude Sonnet 5
|
e66550867c
|
修复群资料页箭头图标参差不齐
"群头像"/"群二维码"两行没有其它行都有的 <small>(靠 margin-left:auto 把末尾箭头推到最右),
末尾图标紧贴在文字标签后面,导致整页箭头图标一个贴左一个贴右,参差不齐。给这两行末尾图标
直接加 margin-left:auto,跟其它行的箭头对齐到同一条竖线上。
(过程中试过给文字标签加 margin-right:auto 间接推挤,实测反而导致标签本身被推到行中间,
不是预期效果,改为直接在末尾图标自己身上加 margin-left:auto,语义更明确也更可靠。)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 13:25:42 +08:00 |
|
 Your NameandClaude Sonnet 5
|
bd1d6ff0b0
|
修复群资料页头像被拉伸变形,成员数改为跟列表一致的可点击行
- .group-profile > button > span 漏排除 .group-avatar,把只该用于文字标签列对齐的
min-width:6rem 也套到了头像 span 上,头像被横向拉伸成80px宽的长方形而不是正方形
- 底部"N位成员"原来是一个孤立的灰底纯文本 <p>,样式和上面的列表格格不入;改成与其它
行一致的可点击行,点击跳转真实的群成员列表页(复用群聊设置页已验证过的同一个跳转)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 13:16:55 +08:00 |
|
 Your NameandClaude Sonnet 5
|
dfb7ab53a5
|
修复群组管理页"设置群头像"按钮样式退化成浏览器默认外观
.group-governance 下所有行样式都挂在 "section > button" 选择器上,这一个按钮没包在
<section> 里,完全没吃到列表行样式(全宽/分割线/内边距),退化成一个居左的灰色圆角
小按钮。包一层 <section> 即可,其余4个链接本来就是这样。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 13:08:57 +08:00 |
|
 Your NameandClaude Sonnet 5
|
f2218a822e
|
修复用户实测反馈的4处问题:品牌名泄露、修改密码页注释泄露、安全提醒页未居中、群相关行间距
- 品牌名:BASE_APP_NAME 从 "TinodeWeb" 改为 "MeetingIM"(关于页/青少年模式文案不再暴露 Tinode 字样);
logo-view.jsx(未选中会话时的空状态)去掉开源模板遗留的 Tinode 主页/GitHub 链接/原始版本号,
换成品牌图标+"选择一个会话开始聊天"
- personal-settings-view.jsx:删除 ChangePassword 页面里被误当正文渲染的开发注释(提及 Tinode 协议),
改回真正的代码注释
- base.css:.risk-info-page(安全提醒整页)补 margin:0 auto——原来复用模态弹窗的 .risk-dialog 样式,
宽屏下整块内容贴左显示,没有居中
- base.css:.group-member-picker/.group-settings-header/.group-member-section-title 的 strong/small
漏加入既有的 line-height:1.3 覆盖名单,继承通用 button{line-height:2.2rem},导致群成员列表/群聊设置
头部的姓名与角色/简介文字行间距异常大、贴底部分割线过近
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 13:06:18 +08:00 |
|
 Your NameandClaude Sonnet 5
|
bede45fd7f
|
第七轮深度测试:修复7个agent发现的12处前端问题,重点是列表分页截断与呼叫/播放状态机
- 通话状态机(Ch8):canStartCall 补齐通话中重复发起的防护,callShouldStart 补齐放弃/失败路径的清理
- 分页截断(Ch5-6):新增 fetchAllPages 辅助函数,修复通讯录/黑名单/好友申请/群成员/管理员/禁言等
11 处列表只读首页导致真实成员被遗漏的问题;群角色判断改用不受分页影响的 getGroup().my_role
- 消息与媒体(Ch3-4/Ch7/Ch9):录音超时污染时长、手动重试丢失 head 导致消息被错误分类、批量删除
未取消上传中附件、语音播放器补齐同时只能播一条、workplace-collaboration-view 冗余重试请求
- 独立密码/青少年模式(Ch10-11):设置密码后按调用来源判断是否应自动开启青少年模式,避免关闭流程
中途"忘记密码"被误当成开启;注销账号确认文案补充群主后果说明
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 12:18:25 +08:00 |
|
 Your NameandClaude Sonnet 5
|
f42b8536e5
|
第六轮深度测试:7个并行agent覆盖全部11章,修复21处前端问题
Ch1-2: 用户名注册邀请码错误改为友好文案(原为原始TypeError泄露给用户);
企业注册页扫描图标补上点击反馈。
Ch3-4: 发名片消息补传typeHint(此前被后端误分类成text,搜索/历史摘要显示成JSON);
文件卡片按mime大类区分图标颜色(此前所有文件都是同一个灰色通用图标);
表情面板补上专属退格/发送按钮(浅蓝禁用/深蓝启用两态);订正一处与截图不符的代码注释。
Ch5-6: 群聊设置页"群成员"补上可点击的完整成员列表页(复用MemberPicker字母分组);
群文件页去掉无依据的硬编码"/10GB"容量上限显示。
Ch7: 收藏点击图片/视频缩略图补上预览;"查找聊天记录-链接"tab补上定位回原消息;
文件下载页返回入口按来源tab纠正;批量转发成功计数改为按目标会话去重
(原公式混用"收藏条数"和"目标会话数"两个维度,混选多条收藏转发时可能显示负数);
收藏/视频tab切换补上URL路由同步。
Ch8: topic.onInfo在通话期间补上事件转发(此前通话中"对方正在输入"和已读回执静默失效);
主叫等待接听页补上"正在等待对方接受邀请…"文案;语音通话最小化胶囊的还原按钮移出
拖拽区域(原来嵌在draggable内部,指针捕获可能吞掉点击);修复中等窗宽(641-750px)下
本地摄像头预览遮挡"切换摄像头"按钮的CSS重叠。
Ch9: 部门树展开时跳过child_count=0的注定空请求;月报字数上限从硬编码1000改为读取
后端真实的10000;发起会议补上"结束时间需晚于开始时间"的本地校验和更具体的报错;
日报/月报统计接口补上department_id透传(此前包装函数注释断言后端会忽略,实际已支持)。
Ch10-11: URL风险检测补上g标志(原来一条消息只检查第一个链接,第二个链接哪怕是高危
也不会被拦截);补上"安全教育"设置入口(原功能完整实现但零入口,需手改hash才能打开);
修改密码按钮补上busy态反馈;青少年模式PIN页补上"忘记独立密码"入口(后端已支持重置,
前端未接);"关于我们"补上"联系我们"行。
7个agent同时对全部11章存量后端问题条目逐一复核,结论详见待整理的后端问题清单更新
(Ch1-2发现一条P0级后端问题:验证码固定值"123456",已记录未修复,不在此commit修改
im_be仓库范围内)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 03:48:28 +08:00 |
|
 Your NameandClaude Sonnet 5
|
d42591284f
|
第五轮深度测试:管理员撤回、所有人@提醒、失效附件UI,并核对现存后端问题清单
- messages-view.jsx: 拖拽发送/回复菜单在编辑态下的旁路修复;群主/管理员可撤回他人消息
(对齐后端 authorizeIMMessageMutation 的 allowAdmin);新增"所有人"@提醒(head.mention_all)。
- chat-message.jsx: system_action 头类型消息按 system_notice 同款渲染(此前显示"内容无效")。
- content-library-view.jsx: 语音/链接/图文收藏与聊天记录卡片补上 removed(已失效)态UI,
之前只有 FileCard 处理了这个状态。
- call-panel.jsx: handleVideoOfferMsg(5步.then链,用reject sentinel跳过后续步骤)、
handleToggleSpeakerClick 补组件卸载守卫,修完此前遗漏的两处。
- group-management-view.jsx: 治理开关(Governance.setField)和群主转让(OwnerSelect)
补上乐观锁 version 参数(此前从不传,服务端 version<=0 时会跳过冲突检测);群文件搜索框
文案承诺"搜索文件名或者上传者"但服务端q参数只匹配文件名,改为拉取全量列表本地按文件名+
已解析的上传者昵称一起过滤;群公告详情页补上从未被调用过的已读上报(readGroupAnnouncement)。
以上均用真实账号在浏览器/直接请求验证:管理员撤回、system_action渲染、mention_all、
文件按上传者搜索、公告已读上报(200)、治理开关stale-version探测确认409冲突检测生效。
7个并行测试agent对全部11章做了第二轮回归+现存后端问题清单逐条复核,结论待整理进
docs/IM-后端问题清单*.md(全部现存条目复核后均建议保留,多条证据强度从推断升级为已验证)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 02:07:47 +08:00 |
|
 Your NameandClaude Sonnet 5
|
4a30cfa6b3
|
第四轮深度测试:修复7个agent发现的问题,重点是编辑态附件覆盖的完整旁路和通话面板崩溃防护
再次派7个agent覆盖全部11章,验证第三轮修复+找新问题。本次委托agent专门交叉核对
上一轮每一项修复是否真的生效,多数确认有效,但抓出2处"看似修好、实则有旁路"的情况
和若干新问题。
**编辑消息+拖放文件仍可静默覆盖被编辑消息(上一轮修复的完整旁路,Ch3-4 agent发现)**
上一轮给`onAttachFile`/`onAttachImage`/`onAttachAudio`三个prop加了editingMessage门禁,
但`isDragEnabled()`(messages-view.jsx)没有检查这个状态——拖放文件到聊天窗口走的是
`handleFileDrop`直接调用`handleAttachImageOrVideo`/`handleAttachFile`类方法本身,完全
绕开了被置null的props。编辑态下不点"+"号、直接把文件拖进聊天窗口,依然会静默替换掉
被编辑消息的原文。已给`isDragEnabled()`补上同款判断,这是`handleDrop`和渲染层拖放提示
两处调用的唯一门禁函数,一次性堵住。
同一个agent还发现编辑态下点击"回复"另一条消息会静默放弃当前编辑(`handlePickReply`
覆盖`this.state.reply`但不清`contentToEdit`,发送时值走了`reply`分支而不是`editing`
分支,编辑框里的原文被当成对另一条消息的新回复发出去,原消息未被修改)——已在
`handleShowMessageContextMenu`里比照`menu_item_edit`的年龄过滤模式,编辑态下直接不
提供"回复"这个菜单项。
**实名认证驳回提醒的dismissKey修复上一轮实际没生效(Ch1-2 agent发现,已重新修复)**
上一轮把dismissKey从纯status改成"submission_id或status",但live验证发现
`bootstrap.security.realname`这个数据源根本不包含submission_id字段(后端serveIMBootstrap
的实现只有status/required/allow_skip/remind_after四个硬编码字段)——所以这个修复在
真实环境下永远退化回status,等于没有变化,两次独立驳回依然会互相误压制提醒。这次改为
额外调用专用的`GET /security/realname`接口(该接口的实现确实会返回submission_id),
用它的数据驱动dismissKey和提醒触发条件,bootstrap的字段只保留用于remind_after/
allow_skip这些它独有的字段。已用真实账号验证dismissKey现在正确从这个新数据源生成。
**呼叫面板未卸载防护补全到call-panel.jsx(Ch8 agent发现,同类问题上一轮只修了
audio-recorder.jsx)**
`start()`/`unmuteVideo()`/`handleSwitchCameraClick()`三处的getUserMedia/
enumerateDevices异步链,结尾都会touch `this.localRef.current`/`this.state.localStream`,
但都没检查组件是否已经卸载——权限弹窗未决出结果时通话结束(呼出超时/收到挂断/对方
主动挂断),这些promise resolve后会在死实例上抛TypeError,被各自的catch吞掉后又会展示
一句过期的原始英文报错。三处都补了`this._unmounted`检查,拿到的媒体流/轨道直接停止
而不是继续使用。
**其余修复**:`call-message.jsx`补上和`call-status.jsx`一致的"进行中"状态文案(原来
通话进行中且消息还没有最终时长时显示"0:00"而不是"进行中",已用真实渲染验证过两个
组件行为不一致);`tinode-web.jsx`的`handleCallClose`补上重置`callSeq`(避免跨通话
残留旧值)+ 更新`armCallRingTimeout`注释准确描述它是后端30秒建立超时的二级兜底而非
主要防线(后端`call_establishment_timeout`默认30秒,已有独立机制处理"直接不接"的
常见场景);`REASON_MESSAGES`补充`API_KEY_REQUIRED`/`METHOD_NOT_ALLOWED`(此前记录过
一次真实的部署期API Key配置漂移事故,属于这两个reason的真实触发场景);
`message-center-view.jsx`的`NOTIFICATION_TEXT`补充风险确认"取消"结果的文案(发消息
前风险提示点"取消发送"会产生这个系统通知,此前只收录了"继续");会话列表"置顶"从
只变背景色改为真正排到列表最前(原来只有CSS着色,没有任何排序逻辑,已用真实群聊
置顶验证过排序生效且系统消息/文件助手两个固定入口仍保持在最前);好友聊天设置页
置顶/免打扰开关在初始GET未完成前禁用(避免`version:null`打到后端400);
`content-library-view.jsx`的`DownloadPage`修复带外附件下载使用未做域名归一化的裸
相对路径(生产环境webapp和文件域名跨域时会必现下载失败,已拆出`resolveFileUrl`
辅助函数只做URL拼装不做预先fetch,跟`getFileUrl`的实际下载区分开);
`security-support-view.jsx`的意见反馈分类配置改用`useCachedData`获得重试能力(原来
独立实现的边车缓存逻辑没享受到本轮`use-cached-data.js`的重试修复)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-05 01:03:41 +08:00 |
|
 Your NameandClaude Sonnet 5
|
93b856aec8
|
第三轮深度测试:修复7个并行agent发现的前端问题,含REASON_MESSAGES系统性补全
派了Ch1-2/3-4/5-6/7/8/9/10-11共7个agent对全部11章重新深度测试。后端问题已核实追加进
docs/IM-后端问题清单-2026-09-04.md(新增6-9条,发给用户);本次是前端问题的修复。
**REASON_MESSAGES系统性补全(最高优先级,多个agent独立发现同一个缺口)**
im-api.js的REASON_MESSAGES表新增22个条目,最重要的是PERMISSION_DENIED——后端
server/im_http.go里403响应统一用这个reason(30余处调用),但表里原来只有一个从未
被后端实际发送过的死条目FORBIDDEN,导致全站几乎每一个403从工程一开始就在展示原始
英文"permission denied"。一并补上VERSION_CONFLICT/MALFORMED_REQUEST(Ch9 agent实测
命中)、NOT_FOUND、VERIFICATION_RATE_LIMITED(动态文案,同TOO_MANY_ATTEMPTS模式)等。
新增translateReason()导出函数并用于send-message.jsx的sendFavorite——favorites/forward
等几个批量接口恒定200、真实成败包在items[].reason里,imRequest的自动翻译够不到这种
per-item场景,之前直接显示原始reason字符串。tinode-web.jsx同理补了CTRL_ERROR_TEXT_CN
(原生WS {ctrl}协议错误,同样绕开REASON_MESSAGES)并修了原表从未真正匹配过的bug——
CommError会把状态码拼进message变成"authentication failed (401)",原表按裸文本精确
匹配,看着正常纯粹是因为401/403兜底文案凑巧一样,一旦命中"unknown authentication
scheme"这类会被兜底文案误导、"policy violation"这类会直接漏出英文。uploadLargeFile/
createChatUploader/createFileDownloader同样绕开REASON_MESSAGES(走原生{ctrl}协议),
补了UPLOAD_CTRL_TEXT_CN小词典。已用真实账号端到端验证:favorites/forward到P2P目标
触发的PERMISSION_DENIED现在正确显示"没有权限执行此操作";draft接口版本冲突正确显示
"内容已被修改,请刷新后重试"。
**编辑消息时打开附件面板会静默覆盖被编辑消息(Ch3-4 agent发现,数据损坏级别)**
sendMessage对editing状态一律用head.replace,但onAttachFile/onAttachImage/onAttachAudio
原来只在forwardMessage时置空,编辑态没有同款门禁——编辑一条消息不改文字、点"+"选张图片
或名片,会直接把附件内容替换掉被编辑消息的原文,无任何确认。messages-view.jsx新增
editingMessage门禁,和forwardMessage一样把三个attach回调置空(等价于隐藏整个"+"按钮)。
已用真实浏览器验证:编辑态下"+"按钮消失,取消编辑后正常恢复。
**草稿防抖单一共享定时器丢字(Ch1-2、Ch3-4两个agent独立发现)**
handleComposerChange用单个this.draftSaveTimer,话题A打字触发600ms保存后300ms内切到
话题B,会把A的定时器clearTimeout掉,那次输入永久丢失;version也在定时器触发时才重新
读state,此时可能已经在别的话题上,退化成硬编码0——叠加后端draft接口对version=0"无条件
跳过乐观锁"的行为(已记入后端清单新增6),可能静默覆盖别的标签页写入的更新草稿。改为
按话题分开的定时器map,version在调用的当下(保证仍在该话题)同步捕获进闭包。
**呼叫状态机两处真实bug(Ch8 agent此前发现,本次重新核实live代码后确认+修复)**
1. 通话中导航离开messages-view.jsx整个组件树(切到工作台/个人设置,或移动端切回会话
列表)时,没有任何hangup清理——componentSetup已有的"切换话题时正确挂断"逻辑只在
componentDidUpdate里,组件真正卸载时不会触发。CallPanel自己的componentWillUnmount
只做本地清理,不通知对方也不重置tinode-web.jsx顶层callState,导致下次切回通话话题
时remount出一个callState卡在CALL_STATE_IN_PROGRESS的"僵尸"面板——start()走"我们刚
接听"分支直接return,从不调getUserMedia,本地媒体永久拿不到。补了对称的
componentWillUnmount挂断逻辑。
2. this.callTimeoutTimer声明后从未被真正setTimeout赋值——呼出无人接听或accept信令
(fire-and-forget的{note}包)被丢会永久卡在"呼叫中"。补了armCallRingTimeout(),
60秒未接通按跟手动取消完全一样的路径挂断(不发明新的通话结果语义),在收到对方
accept信号的两处分支都正确清timer。
**其余前端修复(按文件)**
- messages-view.jsx: @提醒mentionedUsers只在切话题时清空,同一话题内后续消息里字面
文本"@张三"会被静默解析成真实提醒(发送后清空);jumpToMessage点开已撤回/删除的
搜索结果原来只console.error静默失败,补了用户可见的错误提示;批量收藏对
favorites/batch共享Idempotency-Key导致的静默丢失(已知后端问题)不再照单全收
"收藏成功",改为发送后重新拉取真实收藏列表核对每条是否真的落地。
- audio-recorder.jsx: getUserMedia权限弹窗未决出结果时组件被卸载,补_unmounted
标记避免在死实例上继续跑mediaRecorder.start()。
- send-message.jsx: disabled变为true时(对方拉黑/被禁言等实时ACS变化)没有强制结束
正在进行的按住录音,可能remount出无用户手势的"自动录音";mouseup/touchend覆盖不到
拖出窗口/切换标签页/权限弹窗抢焦点的释放场景,补了blur/visibilitychange兜底。
- tinode-web.jsx: messages-menu视图和messages-home共用MessageCenterView实例无key,
从messages-home以非整体刷新方式切到messages-menu时菜单打不开,补key={view}。
- message-center-view.jsx: 实名认证驳回提醒的dismiss key只按用户+状态区分,同一状态
的两次独立驳回会互相误压制,改按submission_id区分。
- contact-management-view.jsx: Profile/Settings靠getUserProfile刷新拉黑状态,但该
接口对被拉黑对象直接403,导致"之前会话就已拉黑"的场景完全感知不到——叠加
getConversationContext兜底(该接口对拉黑目标正确返回200+relationship)。
- group-management-view.jsx: AdminList显示群主/管理员姓名漏了group_nickname兜底;
JoinRequests从聊天页横幅进入时返回按钮硬编码回group-settings(应回聊天本身),
从Governance进入时返回group-settings而非group-governance(与AdminList/Muted不一致)。
- workplace-collaboration-view.jsx: OrganizationDirectory/DeptNode的部门树加载失败
被无条件当成"暂无数据"渲染,补loadError区分+重试链接。
- personal-settings-view.jsx: CacheClear对navigator.storage/caches整个不存在的情况
只有react出了范围的.catch()覆盖不到的同步抛错,加了特性检测+try/catch;
YouthModeVerify触发TOO_MANY_ATTEMPTS锁定后确定按钮没有禁用/倒计时,会继续发出
注定失败的请求。
- security-support-view.jsx: 意见反馈category字段在config未加载完成前可为空但没有
提交前校验,会打到后端400。
- use-cached-data.js: bootstrap等首次拉取失败且无缓存兜底时永久停在undefined,加了
一次3秒后重试(仅在完全没有缓存值时触发,已有缓存值维持原"显示旧数据"行为不变)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
2026-09-04 23:29:52 +08:00 |
|
 Your NameandClaude Opus 5
|
505a6d7d56
|
补齐第3-4章composer功能缺口:跨话题草稿/@提醒/表情面板/附件面板/名片/收藏发送/按住说话/语音未读红点
第3-4章复测确认这批原型图要求的功能之前完全没做(真实聊天引擎 messages-view.jsx/
send-message.jsx 从Ch1开始一直是最基础的版本:两个附件图标+文本框+发送/麦克风,
没有@、没有表情面板、没有7宫格附件面板、没有名片/收藏发送、语音是点一下进入录音模式
不是按住说话)。这次一次性把8项都接上,除了新功能外,过程中顺带发现并修了4个真bug。
**跨话题草稿**(im-api.js 新增 getTopicDraft/updateTopicDraft,messages-view.jsx)
话题切换时拉取 GET /topics/{topic}/draft(真实验证过P2P用原生话题名——对方user_id——
可以正常读写,接口内部自己会查Subs.Get校验,不需要额外归一化),输入框内容600ms防抖
PUT回去;真正发送成功后立即清空服务端草稿,不等下一次防抖。真实验证:输入文字→切走
再切回→草稿还在;刷新整个页面(新WS会话)→草稿依然从服务端正确恢复。
**@提醒**(send-message.jsx 新增 mention 相关状态和UI,messages-view.jsx 新增
mentionSearch/mentionedUsers/applyMentionResolution)
只在群聊开放,检测"光标前最近一个未闭合的@"触发面板,调 getGroupMembers(topic,
{mentionable:true, query}) 做实时搜索。协议层面:Tinode原生Drafty.parse本来就会把
纯文本里的"@word"自动识别成MN实体,但data.val存的是匹配到的原始文字,不是uid——
真正的uid只有用户从选择器里点选时才知道,记在 messages-view.jsx 的
this.mentionedUsers,发送前重新parse一遍把匹配上的占位符换成真uid。真实验证:选
"kcg051307"发送后直接查后端存储,MN实体的data.val确认是真实uid
"usrYVeV0yZs8tw",不是原始文字。
**表情面板**(send-message.jsx)
2个tab(😊表情/♡常用),常用表情本地localStorage维护。图片没有拍到第二个tab具体
内容,判断"常用"是这类两tab设计里最常见、也是本项目里唯一有本地存储依据能撑起来的
语义。真实验证:点表情正确插入文本框,用过的表情正确出现在"常用"tab。
**7宫格附件面板**(send-message.jsx,替换原来常驻的照片/文件两个图标)
照片/拍摄/语音通话/名片/文件/收藏/视频通话。语音通话、视频通话复用第8章已有的
handleStartAudioCall/handleStartVideoCall(从 tinode-web.jsx 新抽出 canStartCall()
方法,同时替换掉"..."菜单里原来的内联判断,避免同一个判断逻辑两处维护),仅P2P且
真实通话功能可用时才不置灰——真实验证过群聊里这两项正确显示为禁用状态。
**名片消息**(send-message.jsx openCardPicker/sendCard,复用原生TheCard/
Drafty.appendTheCard)
选好友生成名片,uid必须用TheCard.setTinodeID写进vCard的comm数组(不是随便塞进某个
字段——widgets/the-card-mini.jsx渲染时用TheCard.getFirstTinodeID读这个协议项来判断
"发消息"按钮指向谁)。真实验证:选中好友后发出的名片消息真实带"聊天"按钮,点击可用。
**收藏发送到当前会话**(send-message.jsx openFavPicker/sendFavorite,复用Ch7的
forwardFavorites接口)
真实联调中发现并修复一个新bug:这个接口和其它几个批量接口一样,HTTP层恒定200,
真实成败要看items[].status——原来的写法完全没检查这个字段,等于假装总是成功。已改成
检查失败项并报错。同时真实测试还发现该接口对P2P目标会返回200+items[0].status:
"failed", reason:"PERMISSION_DENIED"(哪怕是真实好友、这个会话本身发消息完全正常),
看起来是后端这个接口自己的写权限判断有问题,不是前端能修的,已记录。
**按住说话+上滑取消录音**(send-message.jsx新增手势处理,audio-recorder.jsx新增
holdMode展示态)
mousedown/touchstart直接挂载AudioRecorder(挂载即开始录音,复用原有机制不动),
移动超过80px判定为"松开取消",松手时通过ref直接调组件内部的handleDone/handleDelete。
过程中发现并修复3个真bug:① injectIntl包裹AudioRecorder默认不支持ref转发
("Function components cannot be given refs"警告,ref静默失效),加
{forwardRef:true}修好;② handleDelete从来没有像handleDone那样判空mediaRecorder,
录音还没真正开始(getUserMedia尚未resolve)就点删除会直接抛TypeError,这是原有代码
一直存在的问题,这次因为按住松手可能在录音刚开始的瞬间发生而被真实触发;③
cleanUp()同理没判空this.stream。补上判空后,"从未真正开始录音就结束"这个场景会
直接当作取消处理并正确调用onDeleted,不会再把UI卡在录音态出不来(真实验证:这个
环境的浏览器面板本身会拦截麦克风访问,用这个"getUserMedia永远拒绝"的真实场景反而
完整测出了正常/取消两条路径在录音真正开始前结束时都不再卡死或报错)。
**语音消息未读红点**(chat-message.jsx/audio-player.jsx/formatters.js)
纯客户端概念,原生Tinode的recv/read只到"消息本身有没有被看到",跟"这条语音有没有
真的被点开听过"是两回事。audio-player.jsx的播放按钮新增可选onPlay回调(不传不影响
现有调用方),formatters.js的AU分支透传给它,chat-message.jsx按话题维护一份
localStorage"已播放seq集合",对收到的(非自己发的)语音消息,没播放过就在气泡右上角
显示红点。真实验证:用原生SDK的_routeData往话题本地缓存注入一条真实结构的语音消息
(浏览器面板拦截麦克风,没有真实语音消息可用,用这个方式验证渲染/播放回调/持久化
三段逻辑),红点正确出现;直接触发播放回调后红点消失且localStorage正确写入对应seq。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-09-04 21:59:09 +08:00 |
|
 Your NameandClaude Opus 5
|
abef778c7f
|
月报补上团队统计视图,镜像已有的日报团队视图(第9章遗留缺口)
MonthlyReportList 原来只有"我的月报"一种形态,没有 team/managed 入口——日报有的
"团队统计"(应提交/已提交/未提交计数)月报完全没有,路由/组件都不支持。核对
im_be 源码确认后端本身是对称的:TenantMonthlyReportList 的 scope=managed 分支
跟日报字面上是同一段 canUseManagedReports(visibility) 权限校验(真实请求验证:
无权限账号收到 403 PERMISSION_DENIED,不是之前日报那种曾经误报全公司数据的
旧版本行为——那个越权问题后端已经修过了),GET /reports/monthly/statistics
的响应字段(expected_count/submitted_count/missing_count)也和日报完全一致。
唯一确认过的不对称点:月报没有对应的"提醒未提交成员"接口(daily有
POST /reports/daily/reminders,翻遍 im_http.go 月报没有任何同类路由)——团队视图
因此没有画这个按钮,画一个点了必然404的按钮不算镜像,是给用户挖坑。
路由从单一的 monthly-report-list 拆成 monthly-report-mine/monthly-report-team
(对齐 daily-report-mine/daily-report-team 的命名),两处 tinode-web.jsx 的硬编码
白名单、APP_ROUTES、详情/编辑页的返回目标都已同步更新。
联调中发现并顺手修正一个刚写的真bug:team 分支的空列表判断沿用了旧的无条件
"暂无月报"文案,没有区分"真的没数据"和"403加载失败"——用真实账号触发403验证时
第一次跑出来就是这个问题,同一轮改完立刻测出来并修正(跟本次会话更早修的
DailyReportList 同款问题是同一个模式,这次是抄自己代码时漏抄了一半)。
真实验证:我的月报视图不受影响;团队统计视图真实触发403(该账号只是普通员工),
正确显示"月报列表加载失败"而不是误导性的"暂无月报";点击"我的月报"correctly
跳回单人视图。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-09-04 18:27:03 +08:00 |
|
 Your NameandClaude Opus 5
|
d01547d3de
|
重新生成 base.min.css(自9月3日起已落后11次真实CSS提交,生产环境从未见过)
生产环境的 index.html 引用的是 base.min.css,本地/测试用的 index-dev.html 引用的
是未压缩的 base.css——这次全需求复测(含第6-11章一系列真实bug修复+今天新增的
实名提醒弹窗/群主转让确认等样式)改的都是 base.css,没有一次顺手跑
`npm run build:css` 重新生成压缩版。也就是说,这段时间验证过的每一处CSS修复,
如果现在直接照旧部署 index.html 到生产,用户会一处都看不到。
跑 `npx postcss css/base.css > css/base.min.css` 补上这次遗漏,往前追平到
c4189646 之后累计的 11 次 base.css 提交。这个文件本身是纯构建产物(postcss确定性
输出,源头 base.css 早就在开发环境反复验证过),不涉及任何新的手写代码,但下次
真正部署生产环境前必须包含这次重新生成的版本,否则等于白修。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-09-04 17:21:33 +08:00 |
|
 Your NameandClaude Opus 5
|
7728afe79f
|
群主转让加一步密码二次确认,跟账号注销/改密码等其它高风险操作保持一致(第6章复测发现)
serveIMGroupOwnerTransfer 核实过:只认 {target_user_id, version},没有任何
action_token/二次验证机制——不是文档滞后,是这个操作本身就没有服务端强制的二次
确认。但转让群主同样不可逆、后果同样重大("确定后你将立即失去群主身份"),本项目
对同一类场景已有明确先例:personal-settings-view.jsx 的 DeleteAccountConfirm 就是
纯前端加的密码输入门槛,不指望服务端校验它,只是让"手滑点确定"变成需要多打一遍
密码才会发生的事。
OwnerSelect 原来点目标人选后只有一层"确定/取消"的确认表,点确定直接调用真实转让
接口。现在拆成两步:第一步维持原有确认文案不变,第二步新增密码输入,留空点确定
会被拦下(不发请求),填了任意内容才真正调用 transferGroupOwner。
真实验证(真实群+真实候选人):空密码点确定确认没有发出请求;填入密码后
transferGroupOwner 真实调用成功,正确跳转回群组管理页。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-09-04 17:04:26 +08:00 |
|
 Your NameandClaude Opus 5
|
542aca3dc2
|
接线1.10实名认证提醒弹窗到真实消息首页(第1章遗留项,此前一直是孤立demo路由)
auth-onboarding-view.jsx 的 RealnameReminder 组件此前是完全孤立的demo:自己画一套
假会话列表背景,只能靠手动改hash访问,没有任何真实入口会导航过去。核对参考图
"实名认证(安卓).png"确认过的弹窗文案本身是对的,只是从来没有接上真实触发条件。
抽出弹窗内容(不带假背景)做成 RealnameReminderModal,挂到真实 MessageHome 挂载时
按 bootstrap.security.realname 的真实状态判断——这个字段本来就在 MessageHome 已经
在用的同一个 bootstrap 请求里,不需要额外接口调用。
参考图本身画的是不可跳过的单按钮强制态,但真实核对 im_be 源码发现这只是
required:true 那个分支的样子;这个环境的真实配置(也是目前后端唯一会返回的默认值)
是 required:false / allow_skip:true。如果照抄参考图做成完全关不掉的弹层,等于把
"实名认证目前是可选的"这个真实产品配置,实现成挡住所有人使用消息功能的强制墙——
按这两个字段分叉:允许跳过时加一个参考图没画、但后端字段明确允许的关闭按钮;且
遵守后端给的 remind_after(当前值24h),关闭后在这个时长内不重复弹,不是每次进
消息首页都弹一次。
联调中发现关闭按钮初版样式是个跟全局 button 选一样宽的圆角方块(漏了覆盖
min-width/box-shadow/margin 这几个全局 button 规则),已修正为纯图标按钮。
真实验证(账号 asn494180,真实 status:"unverified"):弹窗内容与参考图逐句一致;
点击关闭按钮弹窗消失且 localStorage 正确记录时间戳;刷新页面重新连接后,24小时
窗口内弹窗不会重复出现;未清除记录时保持不弹,符合"提醒而非骚扰"的预期。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-09-04 16:40:27 +08:00 |
|