From e7cb4edf3a769bcc5e3f42500363e35e9a43009c Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 25 Sep 2026 11:51:28 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=BB=E4=BA=86iOS/Android=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E6=BA=90=E7=A0=81=E5=90=8E=E4=BF=AE=E6=AD=A3=EF=BC=9Abusy?= =?UTF-8?q?=E7=A1=AE=E5=AE=9E=E6=98=AF=E7=9C=9F=E5=AE=9E=E7=9A=84=E9=80=9A?= =?UTF-8?q?=E8=AF=9D=E7=8A=B6=E6=80=81=EF=BC=8C=E4=B9=8B=E5=89=8D=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E9=94=99=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 早前(Part I/K)判断"busy从未在持久化消息里真实出现过,只是本应用自己在 live信令上用的值",所以没把它并入 CallWebrtcState,只在本地 UI 状态单独 加了个 union。这次拿到手机端真实源码核对,iOS(ServerMessages.swift的7态 枚举)和Android(MsgServerData.java的BUSY)都把busy当成服务端下发的正式 head.webrtc取值之一,和accepted/finished等其余几个是同一级别的东西, 不是本应用凭空发明的——之前的判断依据不足。 补上 case 'busy' 的通话结果文案("对方忙线中",和已有的实时来电忙线提示 用词一致),去掉 stores/calls.ts 里两处不再需要的 `| 'busy'` 单独union。 Co-Authored-By: Claude Sonnet 5 --- web/src/lib/messages/dispatch.ts | 12 ++++++++---- web/src/lib/tinode/calls.ts | 9 +++++++-- web/src/stores/calls.ts | 11 +++++------ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/web/src/lib/messages/dispatch.ts b/web/src/lib/messages/dispatch.ts index 2b60a8e8..cba5b72a 100644 --- a/web/src/lib/messages/dispatch.ts +++ b/web/src/lib/messages/dispatch.ts @@ -156,10 +156,12 @@ export function formatCallDuration(ms: number): string { * 1953/1954): `missed` alone covers both "未接听" and "已取消" (a callee who let it ring out and a * caller who cancelled before pickup both replace to the SAME wire value) -- direction picks the * text, not the wire value, using `mine` (was the ORIGINAL invite sent by me, i.e. was I the - * caller) exactly the way this app already threads that prop through MessageRow. No `busy` value - * exists in the confirmed real enum (only accepted/finished/disconnected/missed/declined) despite - * the old app's own UI vocabulary having one -- not rendered here since nothing legitimate can - * produce it yet. + * caller) exactly the way this app already threads that prop through MessageRow. `busy` WAS assumed + * to never appear in a persisted message (only ever seen on this app's own live, ephemeral + * `sendCallHangup(...,'busy')` signal) -- disproven by reading the real iOS/Android source + * (2026-09-25): both platforms' own SDKs declare `busy` as a genuine `head.webrtc` enum member the + * SERVER controls, same as every other case here, so a persisted busy result is a real thing this + * app can receive, not just something it can locally signal to a live peer. */ export function callResultDisplay(msg: StoredMessage, mine: boolean): { icon: string; text: string; danger: boolean } | null { const content = callContentOf(msg); @@ -177,6 +179,8 @@ export function callResultDisplay(msg: StoredMessage, mine: boolean): { icon: st return { icon: 'phone_missed', danger: true, text: '已拒绝' }; case 'disconnected': return { icon: 'phone_missed', danger: true, text: '异常结束' }; + case 'busy': + return { icon: 'phone_missed', danger: true, text: '对方忙线中' }; case 'accepted': case 'started': return { icon, danger: false, text: '通话中' }; diff --git a/web/src/lib/tinode/calls.ts b/web/src/lib/tinode/calls.ts index a4299c6f..719754a3 100644 --- a/web/src/lib/tinode/calls.ts +++ b/web/src/lib/tinode/calls.ts @@ -21,8 +21,13 @@ export interface CallContent { } /** `head.webrtc` values a call message cycles through, replacing the SAME `seq` in place (doc line - * 1953) -- `started` is the live invite; the rest are terminal results the server writes over it. */ -export type CallWebrtcState = 'started' | 'accepted' | 'finished' | 'disconnected' | 'missed' | 'declined'; + * 1953) -- `started` is the live invite; the rest are terminal results the server writes over it. + * `busy` was previously excluded here (assumed to be a client-only, never-persisted value -- see + * the now-superseded reasoning this replaced in stores/calls.ts/dispatch.ts) until reading the real + * iOS/Android app source (2026-09-25 cross-client audit) confirmed it's a genuine enum member both + * platforms' own SDKs declare for this exact server-controlled field (`ServerMessages.swift`'s + * 7-case enum, `MsgServerData.java`'s `BUSY`), not a web-only invention. */ +export type CallWebrtcState = 'started' | 'accepted' | 'finished' | 'disconnected' | 'missed' | 'declined' | 'busy'; export function callContentOf(msg: StoredMessage): CallContent | null { const webrtc = msg.head?.webrtc; diff --git a/web/src/stores/calls.ts b/web/src/stores/calls.ts index ac2be0b5..d99bf7d5 100644 --- a/web/src/stores/calls.ts +++ b/web/src/stores/calls.ts @@ -42,11 +42,10 @@ interface CallState { // stays true even when the mic never actually attaches at all (permission denied/no device) -- a // real user reported this as "看起来接通了" for a call where no audio ever worked on either side. mediaJoined: boolean; - // `'busy'` is never a real PERSISTED `head.webrtc` value (confirmed live, see dispatch.ts's own - // comment) so it's deliberately NOT part of `CallWebrtcState` -- but it IS a real value this app's - // own `sendCallHangup(..., 'busy')` puts on the LIVE, ephemeral `{info hang-up}` signal a second - // caller receives, which flows straight into this same field. - endReason: CallWebrtcState | 'busy' | null; + // `busy` is now part of CallWebrtcState itself (2026-09-25: confirmed against real iOS/Android + // source that the server genuinely uses this value, not just this app's own live + // `sendCallHangup(..., 'busy')` signal a second caller receives) -- no separate union needed here. + endReason: CallWebrtcState | null; /** Was THIS session the one that placed the call (via startCall), not the one that received it -- * needed to pick "已取消" vs "未接听" for the same wire value the same way dispatch.ts's * callResultDisplay already does for the permanent chat-bubble record of the same call. */ @@ -65,7 +64,7 @@ interface CallState { switchCamera: () => Promise; setHasRemoteVideo: (v: boolean) => void; _reset: () => void; - _endWithReason: (reason: CallWebrtcState | 'busy') => void; + _endWithReason: (reason: CallWebrtcState) => void; } const IDLE = {