features/messaging/async-messaging/index.js

import { TAGS } from '../../../constants';
import logger from '../../../logger';
import MESSAGES from '../../../messages';
import SkylinkSignalingServer from '../../../server-communication/signaling-server';
import {
  getParamValidity, getRoomStateByName, isABoolean,
} from '../../../utils/helpers';
import SkylinkError from '../../../utils/skylinkError';
import Skylink from '../../../index';
import EncryptedMessaging from '../encrypted-messaging';
import { dispatchEvent } from '../../../utils/skylinkEventManager';
import { storedMessages, persistentMessageState } from '../../../skylink-events';
import PeerData from '../../../peer-data';
import asyncHelpers from './helpers/index';
import getUserInfo from '../../../peer-data/helpers/getUserInfo';

const instance = {};

/**
 * @classdesc Class used for handling the asynchronous messaging feature
 * @class
 * @private
 */
class AsyncMessaging {
  constructor(roomState) {
    const { user, room, hasPersistentMessage } = roomState;

    if (!instance[room.id]) {
      instance[room.id] = this;
    }

    this.room = room;
    this.peerId = user.sid;
    this.isPersistent = hasPersistentMessage; // Value defaults to hasPersistentMessage
    this.hasPersistentMessage = hasPersistentMessage;

    return instance[room.id];
  }

  setMessagePersistence(isPersistent) {
    if (!isABoolean(isPersistent)) {
      throw SkylinkError.throwError(MESSAGES.MESSAGING.PERSISTENCE.ERRORS.FAILED_SETTING_PERSISTENCE, MESSAGES.MESSAGING.PERSISTENCE.ERRORS.INVALID_TYPE);
    } else if (!this.hasPersistentMessage) {
      throw SkylinkError.throwError(MESSAGES.MESSAGING.PERSISTENCE.ERRORS.FAILED_SETTING_PERSISTENCE, MESSAGES.MESSAGING.PERSISTENCE.ERRORS.PERSISTENT_MESSAGE_FEATURE_NOT_ENABLED);
    }

    this.isPersistent = isPersistent;

    dispatchEvent(persistentMessageState({
      room: this.room,
      isPersistent: this.isPersistent,
      peerInfo: getUserInfo(this.room),
      peerId: this.peerId,
    }));
  }

  getMessagePersistence() {
    return this.isPersistent;
  }

  sendMessage(roomName, message, targetPeerId) {
    const roomState = getRoomStateByName(roomName);
    if (getParamValidity(message, 'message', 'sendMessage') && roomState) {
      try {
        logger.log.DEBUG([null, TAGS.ASYNC_MESSAGING, null, MESSAGES.MESSAGING.PERSISTENCE.SEND_MESSAGE]);
        const encryptedMessaging = new EncryptedMessaging(roomState);
        if (encryptedMessaging.canEncrypt(true)) {
          encryptedMessaging.sendMessage(roomName, message, targetPeerId, this.isPersistent);
        }
      } catch (error) {
        SkylinkError.throwError(MESSAGES.MESSAGING.ERRORS.DROPPING_MESSAGE, error.message);
      }
    }
  }

  getStoredMessages() {
    const roomState = Skylink.getSkylinkState(this.room.id);
    if (!this.hasPersistentMessage) {
      logger.log.WARN([this.peerId, TAGS.ASYNC_MESSAGING, null, `${MESSAGES.MESSAGING.PERSISTENCE.ERRORS.PERSISTENT_MESSAGE_FEATURE_NOT_ENABLED}`]);
      return;
    }

    new SkylinkSignalingServer().getStoredMessages(roomState);
  }

  canPersist() {
    if (this.hasPersistentMessage) {
      if (this.isPersistent) {
        return true;
      }

      logger.log.DEBUG([null, TAGS.ASYNC_MESSAGING, null, MESSAGES.MESSAGING.PERSISTENCE.NOT_PERSISTED]);
      return false;
    }

    if (this.isPersistent) {
      logger.log.DEBUG([null, TAGS.ASYNC_MESSAGING, null, `${MESSAGES.MESSAGING.PERSISTENCE.IS_PERSISTENT_CONFIG} ${this.isPersistent}`]);
      throw new Error(MESSAGES.MESSAGING.PERSISTENCE.ERRORS.PERSISTENT_MESSAGE_FEATURE_NOT_ENABLED);
    }

    return false;
  }

  static processStoredMessages(message) {
    const roomState = Skylink.getSkylinkState(message.rid);
    const { room } = roomState;
    const targetMid = message.mid;
    const messageData = JSON.parse(message.data);
    const encryptedMessaging = new EncryptedMessaging(roomState);
    const messages = [];

    logger.log.DEBUG([targetMid, TAGS.ASYNC_MESSAGING, null, MESSAGES.MESSAGING.PERSISTENCE.STORED_MESSAGES], messageData);

    try {
      for (let i = 0; i < messageData.length; i += 1) {
        messageData[i].data = encryptedMessaging.decryptStoredMessages(messageData[i].data, messageData[i].secretId);
        messages.push(asyncHelpers.parseDecryptedMessageData(messageData[i], targetMid));
      }
    } catch (error) {
      throw SkylinkError.throwError(MESSAGES.MESSAGING.ENCRYPTION.ERRORS.FAILED_DECRYPTING_MESSAGE, error.message);
    }

    dispatchEvent(storedMessages({
      room,
      storedMessages: messages,
      isSelf: false,
      peerId: targetMid,
      peerInfo: PeerData.getPeerInfo(targetMid, room),
    }));
  }
}

export default AsyncMessaging;