fix: call media-connect state, blocklist banner staleness, group rename, avatar URL
- stores/calls.ts, CallRoot.tsx: distinguish signaling-connected from actual media-joined (mediaJoined flag), so a call whose mic never attaches no longer shows a live timer/"网络良好"; propagate a disconnected reason on post-accept media failure so both sides agree on the outcome instead of one side seeing a normal "已结束". - DetailColumn.tsx: invalidate conversation-context alongside blocks on block/unblock, so the composer's blocked-notice banner updates immediately instead of only after a window-focus refetch. - GroupDetailColumn.tsx: add an owner-only group rename control (updateGroup was already implemented but never wired to any UI). - Avatar.tsx: stop re-wrapping an already-absolute avatar_file_id (seen on default-avatar placeholders) into a malformed /v0/file/s/https://... request.
This commit is contained in:
+27
-6
@@ -35,6 +35,13 @@ interface CallState {
|
||||
// dynamic Agora SDK import -> client.join() -> createCameraVideoTrack()) hadn't resolved yet, and
|
||||
// never re-fire once it did, leaving the local preview box permanently blank for the whole call.
|
||||
hasLocalVideo: boolean;
|
||||
// Set once `joinCall()` actually resolves (mic track created and published, or for video, mic+
|
||||
// camera) -- distinct from `phase==='connected'`, which flips the instant the SIGNALING accept
|
||||
// arrives, before any media has actually joined. `CallWindow`'s status line previously showed a
|
||||
// ticking duration + "网络良好" from the moment `phase` became 'connected', which live-confirmed
|
||||
// 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
|
||||
@@ -74,6 +81,7 @@ const IDLE = {
|
||||
connectedAt: null,
|
||||
hasRemoteVideo: false,
|
||||
hasLocalVideo: false,
|
||||
mediaJoined: false,
|
||||
endReason: null,
|
||||
wasCaller: false,
|
||||
mediaError: null,
|
||||
@@ -123,7 +131,7 @@ export const useCallStore = create<CallState>((set, get) => ({
|
||||
const join = await joinRtcCall(topic, callId, seq, callType, 'callee');
|
||||
if (get().callId !== callId) return;
|
||||
await joinCall(join, callType, (v) => get().setHasRemoteVideo(v));
|
||||
if (get().callId === callId) set({ hasLocalVideo: callType === 'video' });
|
||||
if (get().callId === callId) set({ hasLocalVideo: callType === 'video', mediaJoined: true });
|
||||
} catch {
|
||||
// Same guard the structurally-identical caller-side accept handler below already has, for the
|
||||
// identical reason: `callId` here is the ORIGINAL id this whole acceptCall() call was for,
|
||||
@@ -138,7 +146,14 @@ export const useCallStore = create<CallState>((set, get) => ({
|
||||
// just only when THIS call is still the live one.
|
||||
if (get().callId !== callId) return;
|
||||
const { topic: currentTopic, seq: currentSeq } = get();
|
||||
if (currentTopic && currentSeq != null) sendCallHangup(getTinode().getTopic(currentTopic), currentSeq, callId);
|
||||
// `reason: 'disconnected'` (not the default no-reason hang-up) so the OTHER side's own
|
||||
// `info.event==='hang-up'` handler can tell this apart from a normal hang-up too -- without it,
|
||||
// the peer's `connectedAt` is already truthy (set the moment accept/accept-event landed, same
|
||||
// as this side's), so its own reason-mapping fell through to 'finished' and both the live
|
||||
// overlay AND the permanent chat-history record showed a normal "通话时长" for a call where
|
||||
// media never actually worked on either end. Live-confirmed: both sides recorded "通话时长
|
||||
// 00:03" for a call that failed getUserMedia immediately after accept.
|
||||
if (currentTopic && currentSeq != null) sendCallHangup(getTinode().getTopic(currentTopic), currentSeq, callId, 'disconnected');
|
||||
leaveCall();
|
||||
get()._endWithReason('disconnected');
|
||||
}
|
||||
@@ -321,7 +336,7 @@ export function initCallSignaling() {
|
||||
.then(async (join) => {
|
||||
if (useCallStore.getState().callId !== callId) return;
|
||||
await joinCall(join, callType, (v) => useCallStore.getState().setHasRemoteVideo(v));
|
||||
if (useCallStore.getState().callId === callId) useCallStore.setState({ hasLocalVideo: callType === 'video' });
|
||||
if (useCallStore.getState().callId === callId) useCallStore.setState({ hasLocalVideo: callType === 'video', mediaJoined: true });
|
||||
})
|
||||
.catch(() => {
|
||||
// Same reasoning as acceptCall()'s own catch branch: `connectedAt` was already set (right
|
||||
@@ -330,7 +345,9 @@ export function initCallSignaling() {
|
||||
// for a call that never actually connected media at all -- go straight to the honest
|
||||
// 'disconnected' outcome instead, and still tell the peer so their UI doesn't hang.
|
||||
if (useCallStore.getState().callId !== callId) return;
|
||||
if (joinTopic && seq != null) sendCallHangup(getTinode().getTopic(joinTopic), seq, callId);
|
||||
// `reason: 'disconnected'` -- same fix as acceptCall()'s structurally identical catch,
|
||||
// so the callee's own hang-up handler maps this to 'disconnected' instead of 'finished'.
|
||||
if (joinTopic && seq != null) sendCallHangup(getTinode().getTopic(joinTopic), seq, callId, 'disconnected');
|
||||
leaveCall();
|
||||
useCallStore.getState()._endWithReason('disconnected');
|
||||
});
|
||||
@@ -346,8 +363,12 @@ export function initCallSignaling() {
|
||||
// already been sending a real `reason:'declined'` all along, just never read back out here),
|
||||
// and a caller who cancelled before pickup made the CALLEE's overlay wrongly show "通话已结束"
|
||||
// instead of "未接听" -- mirrors the SAME connectedAt-based finished-vs-missed distinction the
|
||||
// local hangUp() above already makes for its own no-reason-given case.
|
||||
useCallStore.getState()._endWithReason(reason === 'busy' ? 'busy' : reason === 'declined' ? 'declined' : connectedAt ? 'finished' : 'missed');
|
||||
// local hangUp() above already makes for its own no-reason-given case. `'disconnected'` is the
|
||||
// same fix, for the media-join-failed case: acceptCall()/the accept-handler's own catch above
|
||||
// now send this explicit reason so the OTHER side shows "连接异常" too, not a fake "通话已结束".
|
||||
useCallStore
|
||||
.getState()
|
||||
._endWithReason(reason === 'busy' ? 'busy' : reason === 'declined' ? 'declined' : reason === 'disconnected' ? 'disconnected' : connectedAt ? 'finished' : 'missed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user