requestCallToUser method

RequestCallToUserInstance requestCallToUser({
  1. required dynamic localStream,
  2. required dynamic remoteUserId,
  3. Map<String, dynamic>? additionalData,
})

Implementation

RequestCallToUserInstance requestCallToUser ({
  required localStream,
  required remoteUserId,
  Map<String,dynamic>? additionalData,
}) {
  assert(!_functionCalled, "Please, create another instance of this class, this one is already in use");
  assert(!_callServiceExpired, "Please, create another instance of this class and use it right away, because of getWebRTCParams");
  _callServiceExpired = _functionCalled = true;
  resultCompleterForRequestingCall = Completer();

  this.remoteUserId = remoteUserId;
  this.localStream = localStream;
  this.additionalData = additionalData ?? <String, dynamic>{};

  _listenToCallToClose(
    onCallAnswered: (event) async {
      logger("Uhull! Call has been answered! Accepted: ${event["callAccepted"]}");

      if (disposed) {
        logger("success, but it was canceled");
        return;
      }
      if (event["callAccepted"] != true) {
        logger("callAccepted field is ${(event["callAccepted"] ?? 'null')}");
        doDisposeCallback(result: RequestCallResult(callAccepted: false, additionalData: remoteUserAdditionalData));
        return;
      }
      assert(event["sdp"]["type"] == "answer");

      logger (">> 2 - Setting remote description from answer! ${event["sdp"]["type"]} and ${event["sdp"]["sdp"]}");

      // call accepted..
      await rtcPeerConnection!.setRemoteDescription(
          RTCSessionDescription(
              event["sdp"]["sdp"],
              event["sdp"]["type"]
          )
      );

      logger("--->");
      logger(Map.from(candidatesByPort).toString());

      final iceCandidateListWebsocketResponse = await AsklessClient.instance.create(
          route: "askless-internal/call/ice-candidate-list",
          body: {
            "remoteUserId": remoteUserId,
            "iceCandidateList": rtcIceCandidateList.map((iceCandidate) {
              return {
                "candidate": iceCandidate.candidate,
                "id": iceCandidate.sdpMid,
                "label": iceCandidate.sdpMLineIndex,
              };
            }).toList()
          }
      );
      if (!iceCandidateListWebsocketResponse.success) {
        final errorMessage = "could not inform the iceCandidateList: \"${iceCandidateListWebsocketResponse.error!.code}: ${iceCandidateListWebsocketResponse.error!.description}\"";
        logger(errorMessage, level: Level.error);
        doDisposeCallback(error: errorMessage);
        return;
      }
    }
  );

  _setup(
    impl: () async {
      if (disposed) {
        doDisposeCallback(error: 'disposing...');
        return;
      }
      final sdpOffer = await rtcPeerConnection!.createOffer({'offerToReceiveVideo': 1, 'offerToReceiveAudio': 1});
      await rtcPeerConnection!.setLocalDescription(sdpOffer);

      logger(">> 1 - SDP OFFER GENERATED AND SET LOCAL DESCRIPTION LOCALLY sdpOffer");

      rtcPeerConnection!.onIceCandidate = (RTCIceCandidate candidate) {
        logger("rtcPeerConnection -> onIceCandidate #1");
        rtcIceCandidateList.add(candidate);
        logger(json.encode({
          'candidate': candidate.candidate,
          'sdpMid': candidate.sdpMid,
          'sdpMlineIndex': candidate.sdpMLineIndex
        }));

        if (candidate.candidate != null && candidate.candidate!.contains('srflx')) {
          final cand = parseCandidate(candidate.candidate!);
          if (candidatesByPort[cand["relatedPort"]] == null) {
            candidatesByPort[cand["relatedPort"]] = [];
          }
          logger('adding --> '+Map.from(cand).toString());
          candidatesByPort[cand["relatedPort"]].add(cand["port"]);
        } else if (candidate.candidate == null) {
          if (candidatesByPort.keys.length == 1) {
            final ports = candidatesByPort[candidatesByPort.keys.first];
            logger(ports.length == 1 ? 'cool nat' : 'symmetric nat');
          }
        }

      };

      logger("askless-internal/call/request: requesting call...");
      final res = await AsklessClient.instance.create(
          route: "askless-internal/call/request",
          neverTimeout: false,
          /** TODO: add controller to let the user be able to cancel the request */
          /** TODO: custom timeout? */
          body: {
            "remoteUserId": remoteUserId,
            "sdp": sdpOffer.toMap(),
            "additionalData": additionalData,
          }
      );
      logger("askless-internal/call/request: requesting call: Call request response is ${res.success} ${res.error?.code ?? ''} ${res.error?.description ?? ''} ");
      if (!res.success) {
        doDisposeCallback(error: res.error!.description);
      }
    }
  );

  return requestCallInstance = RequestCallToUserInstance(
    resultCompleter: resultCompleterForRequestingCall!,
    dispose: doDisposeCallback,
  );
}