chat.js

import Message from './message'
import Visitor from './visitor'
import Connection from "./connection";
import axios from "axios";

// const API_URL = process.env.API_URL + ':' + process.env.API_PORT + '/' + process.env.API_PATH;
const API_URL = 'https://beta.chatix.io:5022/v1';

/**
 * Chat class represents conversation between visitor and managers. This is main private class for all actions inside
 * library.
 * @property {Manager[]} managers people who are connected to current conversation in dashboard
 * @property {Visitor} visitor user who visits current page
 * @property {Connection} connection class provides protocol implementation
 */
class Chat {

   /**
    * Chat constructor
    * @param {string} websiteId Website ID from Chatix dashboard
    * @param {string} visitrorId Visitor ID can be passed from backend. If null, SDK will try to find it in 
    * local storage and request new if it wasn't found in storage.
    */
   constructor(websiteId, visitrorId = null) {
      this.websiteId = websiteId;
      this.messages = [];
      this.managers = [];

      this.onConnected = function(){};
		this.onDisconnected = function(){};
		this.onManagerConnectedToConversation = function(){};
		this.onManagerDisconnectedFromConversation = function(){};
		this.onConversationMessageReceived = function(){};
		this.onConversationMessageUpdated = function(){};
      this.onConversationMessageDeleted = function(){};
      this.onChatroomCreated = function(){};
      this.onChatroomUpdated = function(){};
      this.onChatroomDeleted = function(){};
      this.onChatroomRestored = function(){};
		this.onChatroomMessageReceived = function(){};
		this.onChatroomMessageUpdated = function(){};
		this.onChatroomMessageDeleted = function(){};
		this.onMemberConnectedToChatroom = function(){};
		this.onManagerConnectedToChatroom = function(){};
		this.onVisitorDisconnectedFromChatroom = function(){};
		this.onManagerDisconnectedFromChatroom = function(){};
		this.onScreencastPermissionRequested = function(){};
		this.onManagerConnectedToScreencast = function(){};
      this.onManagerDisconnectedFromScreencast = function(){};
      
      this.connection = new Connection();
      this.connection.onConnected = this.onConnected;
      this.connection.onDisconnected = this.onDisconnected;
      this.connection.onApplyErrorMessage = (message, method) => {
         console.error("Chatix error occured in method " + method + ", desciption: " + message);
      };
      this.connection.onApplyDialogMessage = this._onConversationMessageReceivedCallback;
      this.connection.onConnectManagerToConversation = this.onManagerConnectedCallback;
      this.connection.onDisconnectManagerFromConversation = this.onManagerDisconnectedFromConversationCallback;
      this.connection.onScreencastRequested = this.onScreencastPermissionRequestedCallback;
      this.connection.onDisconnectManagerFromScreencast = this.onManagerDisconnectedFromScreencastCallback;
      this.connection.onApplyChatroomMessage = this.onChatroomMessageReceivedCallback;
      this.connection.onConnectManagerToChatroom = this.onManagerConnectedToChatroomCallback;
      this.connection.onDisonnectManagerFromChatroom = this.onManagerDisconnectedFromChatroomCallback;
      this.connection.onConnectVisitorToChatroom = this.onMemberConnectedToChatroomCallback;
      this.connection.onDisconnectVisitorFromChatroom = this.onMemberDisconnectedFromChatroomCallback;
      this.connection.onCreateChatroom = this.onChatroomCreatedCallback;
      this.connection.onUpdateChatroom = this.onChatroomUpdatedCallback;
      this.connection.onDeleteChatroom = this.onChatroomDeletedCallback;
      this.connection.onRestoreChatroom = this.onChatroomRestoredCallback;
      this.connection.onUpdateConversationMessage = this.onConversationMesssageUpdatedCallback;
      this.connection.onDeleteConversationMessage = this.onConversationMessageDeletedCallback;
      this.connection.onScreenDataRequested = this.onScreenDataRequestedCallback;
      this.connection.onManagerSentScreenEvent = this.onManagerSentScreenEventCallback;

      this.connection.onInvalidVisitorIdConnected = () => {console.error("Invalid Visitor Id Connection")};
      
      this.visitor = new Visitor();
      this.visitor.setUuid(visitrorId);
   }


   /**
    * Starts SignalR connection to server
    */
   async startConnection() {
      if (this.visitor.getUuid === null) {
         this.visitor.setUuid(await this.connection.getNewVisitorId(this.websiteId));
      }
      try {
         await this.connection.start(this.websiteId, this.visitor.getUuid());
         let visitor = await this.connection.getVisitorInfo();
         this.visitor = visitor;
         this.onConnected();
         return true;
      } catch(e) {
         console.error(e);
         return false;
      }
   }


   // applyVisitor() {
   // if (localStorage.getItem('chatix__visitorId') != null) {
   // localStorage.removeItem('chatix__visitorId')
   // }
   // if (!this.visitor) {
   // let storageValue = localStorage.getItem('chatix__visitorId_v1');
   // if (storageValue) {
   //       this.visitor = new Visitor(this);
   //       this.visitor.uuid = storageValue;
   //       return Promise.resolve();
   // } else {
   //       return axios.get(API_URL + '/visitor/get-id?websiteId=' + this.websiteId)
   //          .then((res) => {
   //             if (localStorage.getItem('chatix__visitorId_v0')) {
   //                localStorage.removeItem('chatix__visitorId_v0')
   //             }
   //             localStorage.setItem('chatix__visitorId_v1', res.data);
   //             this.visitor = new Visitor();
   //             this.visitor.uuid = res.data;
   //          }).catch(function(data){
   //             console.log(data.response);
   //          });
   // }
   // } else {
   // return Promise.resolve();
   // }
   // }

   /**
    * Stops network connection
    */
   async stopConnection() {
      try {
         await this.connection.stop();
         return true;
      } catch (e) {
         return false;
      }
   }

   async getWebchat() {
      return this.connection.getWebChatInfo();
   }

   /**
    * Getter for current conversation visitor
    *
    * @return {Visitor|null}
    */
   getVisitor() {
      return this.visitor;
   }

   /**
    * Setter for current conversation visitor
    * @param {Visitor} visitor
    */
   async setVisitor(visitor) {
      let responseVisitor = await this.connection.sendVisitorInfo(visitor);
      this.visitor = responseVisitor;
   }

   /**
    * Getter for all website managers
    *
    * @return {Manager[]|*}
    */
   async getManagers() {
      return await this.connection.getWebChatManagers();
   }

   /**
    * Getter for current conversation managers
    *
    * @return {Manager[]|*}
    */
   async getConnectedManagers() {
      return await this.connection.getConnectedToConversationManagers();
   }

   async sendBroadcastData(innerHTML, broadcastData) {
      await this.connection.sendBroadcastData(innerHTML, broadcastData);
   }

   async sendBroadcastEvent(broadcastEvent) {
      await this.connection.sendBroadcastEvent(broadcastEvent);
   }

   /**
    * Sends data to server about user navigation
    * @param {string} url Current page URL
    * @param {string} title Current page title
    */
   async sendPage(url, title) {
      await this.connection.sendPage(url, title);
   }

   /**
    * Sends current visitor typing
    * @param {string} text current visitor input
    */
   async visitorTypes(text) {
      await this.connection.sendVisitorTypedText(text);
   }

   /**
    * Sends request for current conversation messages. Messages returns ordered by sent time ascending.
    * 
    * @param {string|null} lastKnownMessageId ID of the last message in dialog you have rendered.
    * @param {number} count Number of messages to return.
    * @return {Promise.<Message[]>}
    */
   async getConversationHistory(lastKnownMessageId = null, count = 50) {
      return this.connection.getConversationHistory(lastKnownMessageId, count);
   }

   /**
    * Gets information about manager from API
    * @param {string} managerId ID of manager you are interested in
    */
   async getManagerInfo(managerId) {
      return await this.connection.getManagerInfo(managerId);
   }

	/**
	* Sends text message to current conversation
   *
	* @param {text} text new message text
	*/
   async sendTextMessage(text) {
      let message = Message.buildText(text);
      return await this.connection.sendMessage(message);
   }

   /**
    * @private
    * Uploads conversation file to Chatix storage
    * @param {File} file File to send.
    * @see https://developer.mozilla.org/en-US/docs/Web/API/File to get more details
    */
   async _uploadConversationFile(file) {
      const data = new FormData();
      data.append('formFile', file);
      let response = await axios.post(API_URL + '/file/save?visitorId=' + this.visitor.getUuid() + '&websiteId=' + this.websiteId, data);
      if (file.type == 'image/jpeg' || file.type == 'image/png') {
         let message = Message.buildImg(response.data);
         return await this.connection.sendMessage(message);
      }
      else {
         let message = Message.buildFile(response.data);
         return await this.connection.sendMessage(message);
      }
   }

   /**
    * Sends file or image message to Chatix
    * @param {File|File[]} files File to send or array of files
    * @see https://developer.mozilla.org/en-US/docs/Web/API/File to get more details
    */
   async sendFileMessage(files) {
      let response = [];
      if (files.length) {
         for (let file of files) {
            response.push(await this._uploadConversationFile(file));
         }
      }
      else {
         response.push(await this._uploadConversationFile(files));
      }
      return response;
   }


   /**
    * @private
    * Uploads chatroom file to Chatix storage
    * @param {File} file File to send.
    * @see https://developer.mozilla.org/en-US/docs/Web/API/File to get more details
    */
   async _uploadChatroomFile(file, chatroomId) {
      const data = new FormData();
      data.append('formFile', file);
      let response = await axios.post(API_URL + '/file/save?visitorId=' + this.visitor.getUuid() + '&websiteId=' + this.websiteId + '&chatroomId=' + chatroomId, data);
      if (file.type == 'image/jpeg' || file.type == 'image/png') {
         let message = Message.buildImg(response.data);
         return await this.connection.sendChatroomMessage(message);
      }
      else {
         let message = Message.buildFile(response.data);
         return await this.connection.sendChatroomMessage(message);
      }
   }

   async sendChatroomTextMessage(messageText, chatroomId) {
      return await this.connection.sendChatroomTextMessage(messageText, chatroomId);
   }

   /**
    * Sends files or images to chatroom
    * @param {File|File[]} files File or files to send
    * @param {string} chatroomId Chatroom ID where to send messages
    */
   async sendChatroomFileMessage(files, chatroomId) {
      let response = [];
      if (files.length) {
         for (let file of files) {
            response.push(await this._uploadChatroomFile(file, chatroomId));
         }
      }
      else {
         response.push(await this._uploadChatroomFile(files, chatroomId));
      }
      return response;
   }

   /**
    * Sorts messages by sent_at ascending
    * @param {Message[]} messages Messages array to sort
    */
   sortMessages(messages) {
      return messages.sort(function (a, b) {
         if (a.sent_at < b.sent_at) {
            return -1;
         }
         else if (a.sent_at > b.sent_at) {
            return 1;
         }
         else {
            return 0;
         }
      });
   }

   async getChatroom(chatroomId) {
      return await this.connection.getChatroom(chatroomId);
   }

   async getAllChatrooms(page, perPage) {
      return await this.connection.getAllChatrooms(page, perPage);
   }

   async getMyChatrooms(page, perPage) {
      return await this.connection.getMyChatrooms(page, perPage);
   }

   async connectToChatroom(chatroomId) {
      return await this.connection.connectToChatroom(chatroomId);
   }

   async disconnectFromChatroom(chatroomId) {
      return await this.connection.disconnectFromChatroom(chatroomId);
   }

   async getChatroomMembers(chatroomId, page = 1, perPage = 100) {
      return await this.connection.getChatroomMembers(chatroomId, perPage, page);
   }

   async getChatroomHistory(chatroomId, lastKnownMessageId = null, count = 50) {
      return await this.connection.getChatroomHistory(chatroomId, lastKnownMessageId, count);
   }

   async sendScreencastPermission(flag) {
      return await this.connection.allowScreenCast(flag);
   }

   async interruptScreencast() {
      return await this.connection.interruptScreenCast();
   }

   /**
    * Receiving new message in conversation
    *
    * @param {Message} message new message
    * @todo обновить на получение одного сообщения
    */
   _onConversationMessageReceivedCallback(message) { 

      var allData = [];
      allData = allData.concat(this.messages, message);

      var sortMess = this.sortMessages(allData);

      this.messages = sortMess;

      this.onConversationMessageReceived(message)
   }

   /**
    * @todo Зачем?
    * @param {} chatInfo 
    */
   receivedChatInfo(chatInfo) {
      this.chatInfo = chatInfo;
      this.onChatInfo(chatInfo);
   }

   onConnectedCallback(visitor) {
      this.onConnected(visitor);
   }

   /**
    * 
    * @param {Manager} manager new connected manager
    */
   _onManagerConnectedCallback(manager) {
      this.managers.push(manager);
      this.onManagerConnectedToConversation(manager);
   }

   _onScreencastPermissionRequestedCallback() {
      this.onScreencastPermissionRequested();
   }

   _onManagerDisconnectedFromScreencastCallback(manager) {
      this.onManagerDisconnectedFromScreencast(manager);
   }

   _onManagerDisconnectedFromConversationCallback(manager) {
      this.onManagerDisconnectedFromConversation(manager);
   }

   _onChatroomMessageReceivedCallback(message, chatroomId) {
      this.onChatroomMessageReceived(message, chatroomId)
   }

   _onManagerConnectedToChatroomCallback(manager, chatroomId) {
      this.onManagerConnectedToChatroom(manager, chatroomId);
   }

   _onMemberConnectedToChatroomCallback(visitor, chatroomId) {
      this.onMemberConnectedToChatroom(visitor, chatroomId);
   }

   _onManagerDisconnectedFromChatroomCallback(manager, chatroomId) {
      this.onManagerDisconnectedFromChatroom(manager, chatroomId);
   }

   _onMemberDisconnectedFromChatroomCallback(visitor, chatroomId) {
      this.onMemberDisconnectedFromChatroom(visitor, chatroomId)
   }

   _onChatroomCreatedCallback(chatroom) {
      this.onChatroomCreated(chatroom);
   }

   _onChatroomUpdatedCallback(chatroom) {
      this.onChatroomUpdated(chatroom);
   }

   _onChatroomDeletedCallback(chatroomId) {
      this.onChatroomDeleted(chatroomId)
   }

   _onChatroomRestoredCallback(chatroom) {
      this.onChatroomRestored(chatroom);
   }

   _onConversationMesssageUpdatedCallback(message) {
      let messageIndex = this.messages.find(mess => mess.uuid === message.uuid);
      messageIndex = this.messages.indexOf(messageIndex);

      if (this.messages[messageIndex] !== undefined) {
         this.messages[messageIndex] = message;
      }
      this.onConversationMessageUpdated(message);
   }

   _onConversationMessageDeletedCallback(messageId) {
      let messageIndex = this.messages.find(mess => mess.uuid === messageId);
      messageIndex = this.messages.indexOf(messageIndex);

      if (this.messages[messageIndex] !== undefined) {
         delete this.messages[messageIndex];
      }
      this.onConversationMessageDeleted(messageId)
   }

   /**
    * @todo перенести логику отправки данных экрана в отдельный компонент
    */
   _onScreenDataRequestedCallback(){
      
   }

   /**
    * @todo перенести логику отрисовки ивентов в отдельный компонент
    */
   _onManagerSentScreenEventCallback(){

   }

}


export default Chat;