Browse SDKs · WASM
SDKsWASMEnterprise

Handle call events

Listen for WASM SDK call invitation, participant, and media-stream events in one place.

Copy

Register invitation lifecycle, participant connection, and media-stream listeners in a central call-state layer. Most events expose the room ID as data.invitation.roomID; stream events expose it directly as data.roomID. A participant event can have a null invitation and provide only groupID. In that case, look up the room in the current group-call state and query the room by group ID if the local state cannot resolve it.

Event data

Eventdata typeFields used to locate and process the update
OnReceiveNewInvitationSignalingInvitationEventinvitation.roomID, invitation.groupID, participant, and userID.
OnInviteeAcceptedSignalingActionEventinvitation.roomID, userID, participant, and opUserPlatformID.
OnInviteeRejectedSignalingActionEventinvitation.roomID, userID, and opUserPlatformID.
OnInvitationCancelledSignalingInvitationEventinvitation.roomID and userID.
OnInvitationTimeoutSignalingInvitationEventinvitation.roomID and userID.
OnInviteeAcceptedByOtherDeviceSignalingActionEventinvitation.roomID, userID, and opUserPlatformID.
OnInviteeRejectedByOtherDeviceSignalingActionEventinvitation.roomID, userID, and opUserPlatformID.
OnHangUpSignalingHungUpEventinvitation.roomID and userID.
OnRoomParticipantConnectedSignalingRoomParticipantChangedEventinvitation?.roomID, groupID, and participant.
OnRoomParticipantDisconnectedSignalingRoomParticipantChangedEventinvitation?.roomID, groupID, and participant.
OnStreamChangeSignalingStreamChangeEventDirect roomID, plus streamType and mute.

For invitation events, data is not a SignalingInvitation itself; read the invitation from data.invitation. Identify each user in a participant list by participant.userInfo.userID, combined with roomID for the participant-state key.

import { SdkEvent } from '@openim/wasm-client-sdk';

function getCallRoomID(data) {
  return (
    data.roomID ??
    data.invitation?.roomID ??
    (data.groupID ? getActiveRoomIDByGroupID(data.groupID) : undefined)
  );
}

function mergeParticipantEvent(data, action) {
  const roomID = getCallRoomID(data);

  if (!roomID) {
    refreshGroupCall(data.groupID);
    return;
  }

  action(roomID, data.participant ?? []);
}

const handlers = {
  invitation: ({ data }) =>
    upsertCall({
      roomID: data.invitation.roomID,
      invitation: data.invitation,
      participant: data.participant,
      state: 'ringing',
    }),
  accepted: ({ data }) =>
    mergeCallEvent(data.invitation.roomID, 'accepted', data),
  rejected: ({ data }) =>
    mergeCallEvent(data.invitation.roomID, 'rejected', data),
  cancelled: ({ data }) =>
    mergeCallEvent(data.invitation.roomID, 'cancelled', data),
  timeout: ({ data }) =>
    mergeCallEvent(data.invitation.roomID, 'timeout', data),
  acceptedByOtherDevice: ({ data }) =>
    mergeCallEvent(data.invitation.roomID, 'accepted-by-other-device', data),
  rejectedByOtherDevice: ({ data }) =>
    mergeCallEvent(data.invitation.roomID, 'rejected-by-other-device', data),
  hangUp: ({ data }) => finishCall(data.invitation.roomID, data.userID),
  participantConnected: ({ data }) =>
    mergeParticipantEvent(data, addParticipants),
  participantDisconnected: ({ data }) =>
    mergeParticipantEvent(data, removeParticipants),
  streamChanged: ({ data }) => updateRoomStream(data.roomID, data),
};

openimsdk.on(SdkEvent.OnReceiveNewInvitation, handlers.invitation);
openimsdk.on(SdkEvent.OnInviteeAccepted, handlers.accepted);
openimsdk.on(SdkEvent.OnInviteeRejected, handlers.rejected);
openimsdk.on(SdkEvent.OnInvitationCancelled, handlers.cancelled);
openimsdk.on(SdkEvent.OnInvitationTimeout, handlers.timeout);
openimsdk.on(SdkEvent.OnInviteeAcceptedByOtherDevice, handlers.acceptedByOtherDevice);
openimsdk.on(SdkEvent.OnInviteeRejectedByOtherDevice, handlers.rejectedByOtherDevice);
openimsdk.on(SdkEvent.OnHangUp, handlers.hangUp);
openimsdk.on(SdkEvent.OnRoomParticipantConnected, handlers.participantConnected);
openimsdk.on(SdkEvent.OnRoomParticipantDisconnected, handlers.participantDisconnected);
openimsdk.on(SdkEvent.OnStreamChange, handlers.streamChanged);

function removeCallListeners() {
  openimsdk.off(SdkEvent.OnReceiveNewInvitation, handlers.invitation);
  openimsdk.off(SdkEvent.OnInviteeAccepted, handlers.accepted);
  openimsdk.off(SdkEvent.OnInviteeRejected, handlers.rejected);
  openimsdk.off(SdkEvent.OnInvitationCancelled, handlers.cancelled);
  openimsdk.off(SdkEvent.OnInvitationTimeout, handlers.timeout);
  openimsdk.off(SdkEvent.OnInviteeAcceptedByOtherDevice, handlers.acceptedByOtherDevice);
  openimsdk.off(SdkEvent.OnInviteeRejectedByOtherDevice, handlers.rejectedByOtherDevice);
  openimsdk.off(SdkEvent.OnHangUp, handlers.hangUp);
  openimsdk.off(SdkEvent.OnRoomParticipantConnected, handlers.participantConnected);
  openimsdk.off(SdkEvent.OnRoomParticipantDisconnected, handlers.participantDisconnected);
  openimsdk.off(SdkEvent.OnStreamChange, handlers.streamChanged);
}

This page provides the complete listener examples for the 11 call events above. refreshGroupCall(groupID) should call Get a group call room and refresh the current call state for that group from the query result. Merge participant state by roomID:userID. Do not identify calls or participants by event order, display name, or array index. Call removeCallListeners() when the user logs out, the active account changes, or the call-state layer is destroyed.