修复通话发起失败静默无反应的问题(第8章复测发现)
sendMessageToTopic 自己的 .catch 会吞掉发送失败——publishMessage 在服务端拒绝时只标记
pub._failed=true 让消息列表画红色感叹号(这是给聊天气泡设计的反馈方式),resolve 成 undefined
而不是 reject。通话邀请这条消息不会被用户看到(用户盯着的是全屏通话面板,不是消息列表),走
这条路径等于什么反馈都没有;而且原代码 `ctrl.code` 直接解引用,undefined.code 会抛
TypeError,变成一个没人 catch 的 rejected promise——handleCallClose() 根本不会被调用,
通话面板会卡在"呼叫中"状态出不来。
这条路径今天有了真实后果:后端新增的全局通话开关(im_client_feature)关闭时,服务端会拒绝
这条 {pub},正好命中这个从未被验证过的失败分支。
修复:判断 ctrl 是否存在、补 .catch,两处都调用 handleError 提示 + handleCallClose 复位。
顺带给通话按钮可见性加了一层预判——读共享的 bootstrap 缓存,features.audio_call/video_call
为 false 时不显示按钮;读不到缓存时按原行为放行(fail-open,只是预判不是安全边界)。
已用真实账号验证:通话菜单项在开关为真时正常显示,无回归。真实失败场景(开关被关闭)无法在
当前浏览器沙盒环境下端到端验证(摄像头/麦克风权限被拦截,此前已确认的环境限制),修复依据是
逐层读 tinode-sdk 源码精确追踪出的调用链,不是猜测。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8e703d86eb
commit
1d51e53a46
@@ -29,8 +29,8 @@ import { PACKAGE_VERSION } from '../version.js';
|
||||
import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';
|
||||
import LocalStorageUtil from '../lib/local-storage.js';
|
||||
import { getTenantContext, resolveQRCode, createGroupJoinRequest } from '../lib/im-api.js';
|
||||
import { writeCachedData } from '../lib/use-cached-data.js';
|
||||
import { getTenantContext, resolveQRCode, createGroupJoinRequest, getBootstrap } from '../lib/im-api.js';
|
||||
import { readCachedData, writeCachedData } from '../lib/use-cached-data.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
import { secondsToTime } from '../lib/strformat.js'
|
||||
import { defaultWallpaper, theCard, topicDisplayName, updateFavicon } from '../lib/utils.js';
|
||||
@@ -2022,7 +2022,16 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
webrtc = writer && !!this.tinode.getServerParam('iceServers');
|
||||
// 后端今天新增了一个全局(非按租户)的通话总开关:{pub head:webrtc:...} 现在会先过
|
||||
// im_client_feature 这个开关,关闭时整条消息直接被服务端拒绝。这个开关缺失/查询失败时是
|
||||
// fail-closed(当成关闭),比 Tinode 协议层的 iceServers 配置更容易出现"某天突然打不通"的
|
||||
// 情况。这里读一下各页面已经在共享的 bootstrap 缓存,能读到就按 features 门控;读不到就按
|
||||
// 原来的行为放行——按钮可见性只是一层预判,真正失败时上面 handleCallInvite 已经会报错,
|
||||
// 这里不做的话用户点了按钮才发现打不通,做了至少能提前少显示一个注定失败的入口。
|
||||
const cachedBootstrap = this.state.myUserId ? readCachedData('bootstrap:' + this.state.myUserId) : null;
|
||||
const callFeatureOk = !cachedBootstrap || !cachedBootstrap.features ||
|
||||
(cachedBootstrap.features.audio_call !== false && cachedBootstrap.features.video_call !== false);
|
||||
webrtc = writer && callFeatureOk && !!this.tinode.getServerParam('iceServers');
|
||||
p2p = Tinode.isP2PTopicName(topicName);
|
||||
self = Tinode.isSelfTopicName(topicName);
|
||||
|
||||
@@ -2327,14 +2336,25 @@ class TinodeWeb extends React.Component {
|
||||
switch (callState) {
|
||||
case CALL_STATE_OUTGOING_INITATED:
|
||||
const head = { webrtc: CALL_HEAD_STARTED, aonly: !!audioOnly };
|
||||
// sendMessageToTopic 自己的 .catch 会吞掉发送失败(只标记 pub._failed=true 让消息列表画
|
||||
// 红色感叹号——这是给聊天气泡设计的反馈方式,通话邀请这条消息不会被用户看到,走这条路径
|
||||
// 相当于什么反馈都没有),resolve 成 undefined 而不是 reject。原来这里 `ctrl.code` 直接
|
||||
// 解引用,undefined.code 会抛 TypeError,变成一个没人 catch 的 rejected promise——
|
||||
// handleCallClose() 根本不会被调用,通话面板会卡在"呼叫中"状态出不来,且用户不知道为什么。
|
||||
// 今天这条路径已经有真实后果:新的全局通话开关 (im_client_feature) 关闭时服务端会拒绝这条
|
||||
// {pub},命中的就是这个从未被验证过的失败分支。
|
||||
return this.handleSendMessage(Drafty.videoCall(audioOnly), undefined, undefined, head)
|
||||
.then(ctrl => {
|
||||
if (ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
|
||||
if (!ctrl || ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
|
||||
this.handleError('通话发起失败,请稍后重试', 'err');
|
||||
this.handleCallClose();
|
||||
return ctrl;
|
||||
}
|
||||
this.setState({callSeq: ctrl.params['seq']});
|
||||
return ctrl
|
||||
}).catch(err => {
|
||||
this.handleError((err && err.message) || '通话发起失败,请稍后重试', 'err');
|
||||
this.handleCallClose();
|
||||
});
|
||||
case CALL_STATE_IN_PROGRESS:
|
||||
const topic = this.tinode.getTopic(callTopic);
|
||||
|
||||
+8
-2
@@ -18836,7 +18836,9 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
writer = acs.isWriter();
|
||||
}
|
||||
}
|
||||
webrtc = writer && !!this.tinode.getServerParam('iceServers');
|
||||
const cachedBootstrap = this.state.myUserId ? (0,_lib_use_cached_data_js__WEBPACK_IMPORTED_MODULE_19__.readCachedData)('bootstrap:' + this.state.myUserId) : null;
|
||||
const callFeatureOk = !cachedBootstrap || !cachedBootstrap.features || cachedBootstrap.features.audio_call !== false && cachedBootstrap.features.video_call !== false;
|
||||
webrtc = writer && callFeatureOk && !!this.tinode.getServerParam('iceServers');
|
||||
p2p = tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isP2PTopicName(topicName);
|
||||
self = tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isSelfTopicName(topicName);
|
||||
return [subscribed ? {
|
||||
@@ -19132,7 +19134,8 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
aonly: !!audioOnly
|
||||
};
|
||||
return this.handleSendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Drafty.videoCall(audioOnly), undefined, undefined, head).then(ctrl => {
|
||||
if (ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
|
||||
if (!ctrl || ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
|
||||
this.handleError('通话发起失败,请稍后重试', 'err');
|
||||
this.handleCallClose();
|
||||
return ctrl;
|
||||
}
|
||||
@@ -19140,6 +19143,9 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
callSeq: ctrl.params['seq']
|
||||
});
|
||||
return ctrl;
|
||||
}).catch(err => {
|
||||
this.handleError(err && err.message || '通话发起失败,请稍后重试', 'err');
|
||||
this.handleCallClose();
|
||||
});
|
||||
case _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_IN_PROGRESS:
|
||||
const topic = this.tinode.getTopic(callTopic);
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user