fix: acceptor sets activeDmCall in click handler, not server response
Bug A: handleAccept relied on dm_call_accepted server response to set activeDmCall. But connectFn's async AudioContext resume yields to the event loop, dm_call_accepted arrives during the yield, finds isLiveKitConnected=false (connectFn just reset it), and skips setActiveDmCall. The acceptor connects to LiveKit but the UI never shows the call. Fix: set activeDmCall and clear incomingCall directly in the click handler. Bug B: ready handler no longer sets activeDmCall for active calls. On refresh/restart the client has no LiveKit connection — showing "Connecting..." with no connection is broken. The call exists on the server but this client session is disconnected.
This commit is contained in:
@@ -42,12 +42,19 @@ export function IncomingCallModal() {
|
|||||||
const handleAccept = () => {
|
const handleAccept = () => {
|
||||||
if (timerRef.current) clearTimeout(timerRef.current);
|
if (timerRef.current) clearTimeout(timerRef.current);
|
||||||
const dmChannelId = incomingCall.dmChannelId;
|
const dmChannelId = incomingCall.dmChannelId;
|
||||||
const { callOrigin, federatedCallId } = useVoiceStore.getState();
|
const { callOrigin, federatedCallId, setActiveDmCall, connectFn } = useVoiceStore.getState();
|
||||||
const origin = callOrigin || (dmChannelId ? getChannelOrigin(dmChannelId) : undefined);
|
const origin = callOrigin || (dmChannelId ? getChannelOrigin(dmChannelId) : undefined);
|
||||||
|
const callDmId = dmChannelId || federatedCallId!;
|
||||||
|
|
||||||
|
// Immediately transition to active call state — don't wait for server response.
|
||||||
|
// The dm_call_accepted event races with connectFn's async AudioContext resume,
|
||||||
|
// causing isLiveKitConnected to be false when it arrives → activeDmCall never set.
|
||||||
|
setIncomingCall(null);
|
||||||
|
setActiveDmCall({ dmChannelId: callDmId });
|
||||||
|
|
||||||
wsSend({ type: 'dm_call_accept', dmChannelId, federatedCallId }, origin);
|
wsSend({ type: 'dm_call_accept', dmChannelId, federatedCallId }, origin);
|
||||||
// Connect directly within gesture context (required for iOS audio permission)
|
// Connect directly within gesture context (required for iOS audio permission)
|
||||||
const connectFn = useVoiceStore.getState().connectFn;
|
if (connectFn) connectFn(callDmId, true);
|
||||||
if (connectFn) connectFn(dmChannelId || federatedCallId!, true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDecline = () => {
|
const handleDecline = () => {
|
||||||
|
|||||||
@@ -321,21 +321,12 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
|||||||
// For federated calls, check membership via token presence (participants may be empty)
|
// For federated calls, check membership via token presence (participants may be empty)
|
||||||
const isParticipant = call.participants.includes(myId) || !!call.livekitToken;
|
const isParticipant = call.participants.includes(myId) || !!call.livekitToken;
|
||||||
if (call.state === 'active' && isParticipant) {
|
if (call.state === 'active' && isParticipant) {
|
||||||
const callDmId = call.dmChannelId || call.federatedCallId || '';
|
|
||||||
// Clear any stuck ringing UI from a ringing→active transition during refresh
|
// Clear any stuck ringing UI from a ringing→active transition during refresh
|
||||||
setIncomingCall(null);
|
setIncomingCall(null);
|
||||||
setActiveDmCall({ dmChannelId: callDmId });
|
// Do NOT set activeDmCall or auto-connect. On refresh/restart, the user
|
||||||
// Store federated call data if present (server already filtered to this user's token)
|
// is no longer in LiveKit — showing "Connecting..." with no connection
|
||||||
if (call.livekitUrl && call.livekitToken) {
|
// is broken UX. The call exists on the server but this client session
|
||||||
setFederatedCallData(call.livekitToken, call.livekitUrl);
|
// has no active LiveKit connection. The user can re-initiate if needed.
|
||||||
}
|
|
||||||
if (call.federatedCallId) {
|
|
||||||
setFederatedCallId(call.federatedCallId);
|
|
||||||
}
|
|
||||||
// PASSIVE: do NOT auto-connect to LiveKit on ready.
|
|
||||||
// The user must click "Join" or re-accept. Auto-connecting causes
|
|
||||||
// identity conflicts when the same user has multiple sessions —
|
|
||||||
// both sessions fight for the same LiveKit identity slot.
|
|
||||||
break;
|
break;
|
||||||
} else if (call.state === 'ringing' && call.callerId !== myId) {
|
} else if (call.state === 'ringing' && call.callerId !== myId) {
|
||||||
const dmCh = event.dmChannels?.find((d: any) => d.id === call.dmChannelId);
|
const dmCh = event.dmChannels?.find((d: any) => d.id === call.dmChannelId);
|
||||||
|
|||||||
Reference in New Issue
Block a user