第六轮深度测试: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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d42591284f
commit
f42b8536e5
@@ -124,11 +124,11 @@ const SIDE_PANEL_VIEWS = ['acc_general','archive','blocked','contacts','cred','c
|
||||
'group-list','group-empty','group-invite','group-events','group-join-requests',
|
||||
'group-new-request','group-announcement','group-summary','group-announcement-detail',
|
||||
'group-settings','group-profile','group-avatar','group-nickname','group-description',
|
||||
'group-my-nickname','group-role-message','group-qr','group-governance','group-admins','group-admin-add',
|
||||
'group-my-nickname','group-members','group-role-message','group-qr','group-governance','group-admins','group-admin-add',
|
||||
'group-admin-success','group-admin-partial','group-admin-removed','group-mute-add',
|
||||
'group-muted','group-owner-select','group-owner-confirm','group-files','group-file-upload',
|
||||
'favorites','favorites-mixed','favorites-files','favorites-text','favorites-voice',
|
||||
'favorites-links','favorites-select','favorite-share','file-download','file-downloading',
|
||||
'favorites-links','favorites-image','favorites-video','favorites-select','favorite-share','file-download','file-downloading',
|
||||
'file-downloaded','history-search','history-files','history-links','history-images','history-videos',
|
||||
'call-history','call-incoming','audio-call-ringing','audio-call-active','audio-call-muted',
|
||||
'audio-call-speaker','video-call-ringing','video-call-camera-off','video-call-active',
|
||||
@@ -192,6 +192,19 @@ function loginErrorMessage(err) {
|
||||
if (err && (err.code === 401 || err.code === 403)) return '账号或密码错误';
|
||||
return (err && err.message) || '登录失败,请重试';
|
||||
}
|
||||
// navigateToCredentialsView 拒绝导航时,ctrl.params.reason 用来给用户一句能看懂的话——目前只有
|
||||
// INVITE_CODE_INVALID/INVITE_CODE_REQUIRED 两个真实用真实请求验证过,REVOKED/EXPIRED 是同一命名
|
||||
// 规律下合理的兄弟值,未逐一实测,先按同一套文案兜底,不影响已确认的两个分支。
|
||||
const INVITE_CODE_REASON_TEXT_CN = {
|
||||
INVITE_CODE_REQUIRED: '请填写邀请码',
|
||||
INVITE_CODE_INVALID: '邀请码错误,请重新输入',
|
||||
INVITE_CODE_REVOKED: '邀请码已被撤销,请联系管理员获取新的邀请码',
|
||||
INVITE_CODE_EXPIRED: '邀请码已过期,请联系管理员获取新的邀请码'
|
||||
};
|
||||
function invalidCredentialsReasonMessage(params) {
|
||||
const reason = params && params.reason;
|
||||
return (reason && INVITE_CODE_REASON_TEXT_CN[reason]) || '注册信息校验未通过,请检查后重试';
|
||||
}
|
||||
|
||||
// See armCallRingTimeout below.
|
||||
const CALL_RING_TIMEOUT_MS = 60000;
|
||||
@@ -985,7 +998,9 @@ class TinodeWeb extends React.Component {
|
||||
if (cred) {
|
||||
this.handleError(this.props.intl.formatMessage(messages.code_doesnot_match), 'warn');
|
||||
}
|
||||
TinodeWeb.navigateToCredentialsView(ctrl.params);
|
||||
if (!TinodeWeb.navigateToCredentialsView(ctrl.params) && !cred) {
|
||||
this.handleError(invalidCredentialsReasonMessage(ctrl.params), 'err');
|
||||
}
|
||||
} else {
|
||||
this.handleLoginSuccessful();
|
||||
}
|
||||
@@ -1015,13 +1030,24 @@ class TinodeWeb extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
// ctrl.code>=300 / ctrl.text=='validate credentials' is standard Tinode for "account created,
|
||||
// now confirm a real credential (email code etc.)" -- ctrl.params.cred is guaranteed non-empty in
|
||||
// that case. This backend reuses the exact same code/text pair for its own invite-code errors
|
||||
// (INVITE_CODE_INVALID/INVITE_CODE_REQUIRED, confirmed live), which carry `reason`/`what` instead
|
||||
// of `cred` -- calling this without checking left `params.cred[0]` throwing a raw TypeError that
|
||||
// surfaced to the user as unreadable English. Returns whether it actually navigated, so both call
|
||||
// sites can show a real error message instead when it didn't.
|
||||
static navigateToCredentialsView(params) {
|
||||
if (!Array.isArray(params.cred) || !params.cred.length) {
|
||||
return false;
|
||||
}
|
||||
const parsed = HashNavigation.parseUrlHash(window.location.hash);
|
||||
parsed.path[0] = 'cred';
|
||||
parsed.params['method'] = params.cred[0];
|
||||
parsed.params['token'] = params.token;
|
||||
parsed.params['code'] = params.code;
|
||||
HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));
|
||||
return true;
|
||||
}
|
||||
|
||||
handleLoginSuccessful() {
|
||||
@@ -1458,7 +1484,9 @@ class TinodeWeb extends React.Component {
|
||||
{public: public_, tags: tags_, cred: Tinode.credential(cred_), attachments: attachments});
|
||||
}).then(ctrl => {
|
||||
if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {
|
||||
TinodeWeb.navigateToCredentialsView(ctrl.params);
|
||||
if (!TinodeWeb.navigateToCredentialsView(ctrl.params)) {
|
||||
this.handleError(invalidCredentialsReasonMessage(ctrl.params), 'err');
|
||||
}
|
||||
} else {
|
||||
this.handleLoginSuccessful(this);
|
||||
}
|
||||
@@ -2611,12 +2639,12 @@ class TinodeWeb extends React.Component {
|
||||
const chapterSixActive = ['group-list', 'group-empty', 'group-invite', 'group-events',
|
||||
'group-join-requests', 'group-new-request', 'group-announcement', 'group-summary',
|
||||
'group-announcement-detail', 'group-settings', 'group-profile', 'group-avatar',
|
||||
'group-nickname', 'group-description', 'group-my-nickname', 'group-role-message', 'group-qr', 'group-governance',
|
||||
'group-nickname', 'group-description', 'group-my-nickname', 'group-members', 'group-role-message', 'group-qr', 'group-governance',
|
||||
'group-admins', 'group-admin-add', 'group-admin-success', 'group-admin-partial',
|
||||
'group-admin-removed', 'group-mute-add', 'group-muted', 'group-owner-select',
|
||||
'group-owner-confirm', 'group-files', 'group-file-upload'].includes(activeProductView);
|
||||
const chapterSevenActive = ['favorites', 'favorites-mixed', 'favorites-files', 'favorites-text',
|
||||
'favorites-voice', 'favorites-links', 'favorites-select', 'favorite-share', 'file-download',
|
||||
'favorites-voice', 'favorites-links', 'favorites-image', 'favorites-video', 'favorites-select', 'favorite-share', 'file-download',
|
||||
'file-downloading', 'file-downloaded', 'history-search', 'history-files', 'history-links',
|
||||
'history-images', 'history-videos'].includes(activeProductView);
|
||||
const callExperienceActive = ['call-history', 'call-incoming', 'audio-call-ringing',
|
||||
|
||||
Reference in New Issue
Block a user