onMemberStateChanged method

Future<void> onMemberStateChanged(
  1. SDNEvent event
)

Implementation

Future<void> onMemberStateChanged(SDNEvent event) async {
  // The member events may be received for another room, which we will ignore.
  if (event.roomId != room.id) {
    return;
  }

  final user = await room.requestUser(event.stateKey!);

  if (user == null) {
    return;
  }

  final callsState = IGroupCallRoomMemberState.fromJson(event);

  if (callsState is List) {
    Logs()
        .w('Ignoring member state from ${user.id} member not in any calls.');
    await _removeParticipant(user.id);
    return;
  }

  // Currently we only support a single call per room. So grab the first call.
  IGroupCallRoomMemberCallState? callState;

  if (callsState.calls.isNotEmpty) {
    final index = callsState.calls
        .indexWhere((element) => element.call_id == groupCallId);
    if (index != -1) {
      callState = callsState.calls[index];
    }
  }

  if (callState == null) {
    Logs().w(
        'Room member ${user.id} does not have a valid m.call_id set. Ignoring.');
    await _removeParticipant(user.id);
    return;
  }

  final callId = callState.call_id;
  if (callId != null && callId != groupCallId) {
    Logs().w(
        'Call id $callId does not match group call id $groupCallId, ignoring.');
    await _removeParticipant(user.id);
    return;
  }

  await _addParticipant(user);

  // Don't process your own member.
  final localUserId = client.userID;

  if (user.id == localUserId) {
    return;
  }

  if (state != GroupCallState.Entered) {
    return;
  }

  // Only initiate a call with a user who has a userId that is lexicographically
  // less than your own. Otherwise, that user will call you.
  if (localUserId!.compareTo(user.id) > 0) {
    Logs().i('Waiting for ${user.id} to send call invite.');
    return;
  }

  final existingCall = getCallByUserId(user.id);

  if (existingCall != null) {
    return;
  }

  final opponentDevice = await getDeviceForMember(user.id);

  if (opponentDevice == null) {
    Logs().w('No opponent device found for ${user.id}, ignoring.');
    lastError = GroupCallError(
      '400',
      GroupCallErrorCode.UnknownDevice,
      'Outgoing Call: No opponent device found for ${user.id}, ignoring.',
    );
    onGroupCallEvent.add(GroupCallEvent.Error);
    return;
  }

  final opts = CallOptions()
    ..callId = genCallID()
    ..room = room
    ..voip = voip
    ..dir = CallDirection.kOutgoing
    ..localPartyId = client.deviceID!
    ..groupCallId = groupCallId
    ..type = CallType.kVideo
    ..iceServers = await voip.getIceSevers();

  final newCall = voip.createNewCall(opts);
  newCall.opponentDeviceId = opponentDevice.device_id;
  newCall.opponentSessionId = opponentDevice.session_id;
  newCall.remoteUser = await room.requestUser(user.id, ignoreErrors: true);
  newCall.invitee = user.id;

  final requestScreenshareFeed = opponentDevice.feeds.indexWhere(
          (IGroupCallRoomMemberFeed feed) =>
              feed.purpose == SDPStreamMetadataPurpose.Screenshare) !=
      -1;

  await newCall.placeCallWithStreams(
      getLocalStreams(), requestScreenshareFeed);

  await addCall(newCall);
}