peer-connection/helpers/data-channel/sendMessageToDataChannel.js

import logger from '../../../logger';
import Skylink from '../../../index';
import { onDataChannelStateChanged } from '../../../skylink-events';
import { dispatchEvent } from '../../../utils/skylinkEventManager';
import PeerConnection from '../..';

import {
  PEER_CONNECTION_STATE,
  DC_PROTOCOL_TYPE,
  DATA_CHANNEL_MESSAGE_ERROR,
  DATA_CHANNEL_STATE,
} from '../../../constants';

/**
 * Function that sends data over the DataChannel connection.
 * @private
 * @memberOf PeerConnection
 * @since 2.0.0
 * @fires onDataChannelStateChanged
 */
const sendMessageToDataChannel = (roomState, peerId, data, channelProperty, doNotConvert) => {
  const state = Skylink.getSkylinkState(roomState.room.id);
  const peerConnection = state.peerConnections[peerId];
  const dataChannel = state.dataChannels[peerId];
  let channelProp = channelProperty;

  if (!channelProp || channelProp === peerId) {
    channelProp = 'main';
  }

  // TODO: What happens when we want to send binary data over or ArrayBuffers?
  if (!(typeof data === 'object' && data) && !(data && typeof data === 'string')) {
    logger.log.WARN([peerId, 'RTCDataChannel', channelProp, 'Dropping invalid data ->'], data);
    return null;
  }

  if (!(peerConnection && peerConnection.signalingState !== PEER_CONNECTION_STATE.CLOSED)) {
    logger.log.WARN([peerId, 'RTCDataChannel', channelProp, 'Dropping for sending message as Peer connection does not exists or is closed ->'], data);
    return null;
  }

  if (!(dataChannel && dataChannel[channelProp])) {
    logger.log.WARN([peerId, 'RTCDataChannel', channelProp, 'Dropping for sending message as Datachannel connection does not exists ->'], data);
    return null;
  }

  /* eslint-disable prefer-destructuring */
  const channelName = dataChannel[channelProp].channelName;
  const channelType = dataChannel[channelProp].channelType;
  const readyState = dataChannel[channelProp].channel.readyState;
  const messageType = typeof data === 'object' && data.type === DC_PROTOCOL_TYPE.MESSAGE ? DATA_CHANNEL_MESSAGE_ERROR.MESSAGE : DATA_CHANNEL_MESSAGE_ERROR.TRANSFER;

  if (readyState !== DATA_CHANNEL_STATE.OPEN) {
    const notOpenError = new Error(`Failed sending message as Datachannel connection state is not opened. Current readyState is ${readyState}`);
    logger.log.ERROR([peerId, 'RTCDataChannel', channelProp, notOpenError], data);
    dispatchEvent(onDataChannelStateChanged({
      peerId,
      channelName,
      channelType,
      messageType,
      error: notOpenError,
      state: DATA_CHANNEL_STATE.SEND_MESSAGE_ERROR,
      bufferAmount: PeerConnection.getDataChannelBuffer(dataChannel[channelProp].channel),
    }));
    throw notOpenError;
  }

  try {
    if (!doNotConvert && typeof data === 'object') {
      logger.log.DEBUG([peerId, 'RTCDataChannel', channelProp, `Sending ${data.type} protocol message ->`], data);
      dataChannel[channelProp].channel.send(JSON.stringify(data));
    } else {
      logger.log.DEBUG([peerId, 'RTCDataChannel', channelProp, 'Sending data with size ->'], data.size || data.length || data.byteLength);
      dataChannel[channelProp].channel.send(data);
    }
  } catch (error) {
    logger.log.ERROR([peerId, 'RTCDataChannel', channelProp, 'Failed sending data)'], { error, data });
    dispatchEvent(onDataChannelStateChanged({
      peerId,
      channelName,
      channelType,
      messageType,
      error,
      state: DATA_CHANNEL_STATE.SEND_MESSAGE_ERROR,
      bufferAmount: PeerConnection.getDataChannelBuffer(dataChannel[channelProp].channel),
    }));
    throw error;
  }
  return null;
};

export default sendMessageToDataChannel;