chatixcore.js

import Chat from './chat'
// eslint-disable-next-line no-unused-vars
import Connection from "./connection";
// eslint-disable-next-line no-unused-vars
import Message from "./message";
// eslint-disable-next-line no-unused-vars
import Manager from "./manager";
// eslint-disable-next-line no-unused-vars
import Chatroom from "./chatroom";


/**
 * Base ChatixCore class.
 * Provides public interface to Chatix API. All you need is here
 *
 *
 * @param {Chat} chat Internal class that does all dirty work. **Please ignore this class because all Chatix public
 * methods are in this class**. We can't guarantee that structure and behavior will not change in future. Use stable
 * public API instead. Thank you!
 * @property {onConnectedCallback} onConnected
 * @property {onDisconnectedCallback} onDisconnected
 * @property {onManagerConnectedToConversationCallback} onManagerConnectedToConversation
 * @property {onManagerDisconnectedFromConversationCallback} onManagerDisconnectedFromConversation
 * @property {onConversationMessageReceivedCallback} onConversationMessageReceived
 * @property {onConversationMessageUpdatedCallback} onConversationMessageUpdated
 * @property {onConversationMessageDeletedCallback} onConversationMessageDeleted
 * @property {onChatroomMessageReceivedCallback} onChatroomMessageReceived
 * @property {onChatroomMessageUpdatedCallback} onChatroomMessageUpdated
 * @property {onChatroomMessageDeletedCallback} onChatroomMessageDeleted
 * @property {onVisitorConnectedToChatroomCallback} onVisitorConnectedToChatroom
 * @property {onManagerConnectedToChatroomCallback} onManagerConnectedToChatroom
 * @property {onVisitorDisconnectedFromChatroomCallback} onVisitorDisconnectedFromChatroom
 * @property {onManagerDisconnectedFromChatroomCallback} onManagerDisconnectedFromChatroom
 * @property {onScreencastPermissionRequestedCallback} onScreencastPermissionRequested
 * @property {onManagerConnectedToScreencastCallback} onManagerConnectedToScreencast
 * @property {onManagerDisconnectedFromScreencastCallback} onManagerDisconnectedFromScreencast
 */
class ChatixCore {

    /**
	 * @constructor
     * @param {string} websiteId Website Unique identifier. You can see it in the dashboard.
	 * @param {string?} visitorId Visitor ID that you can store in your database. If NULL, SDK will check localStorage and request new Visitor ID from Chatix
     */
	constructor(websiteId, visitorId = null) {
		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.onMemberDisconnectedFromChatroom = function(){};
		this.onManagerDisconnectedFromChatroom = function(){};
		this.onScreencastPermissionRequested = function(){};
		this.onManagerConnectedToScreencast = function(){};
		this.onManagerDisconnectedFromScreencast = function(){};

		this.chat = new Chat(websiteId, visitorId);
		this.chat.onConnected = () => {
			if (typeof(this.onConnected) === 'function') {
				this.onConnected();
			}
		};
		this.chat.onDisconnected = () => {
			if (typeof(this.onDisconnected) === 'function') {
				this.onDisconnected();
			}
		}
		this.chat.onManagerConnectedToConversation = (manager) => {
			if (typeof(this.onManagerConnectedToConversation) === 'function') {
				this.onManagerConnectedToConversation(manager);
			}
		}
		this.chat.onManagerDisconnectedFromConversation = (manager) => {
			if (typeof(this.onManagerDisconnectedFromConversation) === 'function') {
				this.onManagerDisconnectedFromConversation(manager);
			}
		};

		this.chat.onConversationMessageReceived = (message) => {
			if (typeof(this.onConversationMessageReceived) === 'function') {
				this.onConversationMessageReceived(message);
			}
		}

		this.chat.onConversationMessageUpdated = (message) => {
			if (typeof(this.onConversationMessageUpdated) === 'function') {
				this.onConversationMessageUpdated(message);
			}
		}

		this.chat.onConversationMessageDeleted = (message) => {
			if (typeof(this.onConversationMessageDeleted) === 'function') {
				this.onConversationMessageDeleted(message);
			}
		}

		this.chat.onChatroomCreated = (chatroom) => {
			if (typeof(this.onChatroomCreated) === 'function') {
				this.onChatroomCreated(chatroom);
			}
		}

		this.chat.onChatroomUpdated = (chatroom) => {
			if (typeof(this.onChatroomUpdated) === 'function') {
				this.onChatroomUpdated(chatroom);
			}
		}

		this.chat.onChatroomDeleted = (chatroom) => {
			if (typeof(this.onChatroomDeleted) === 'function') {
				this.onChatroomDeleted(chatroom);
			}
		}

		this.chat.onChatroomRestored = (chatroom) => {
			if (typeof(this.onChatroomRestored) === 'function') {
				this.onChatroomRestored(chatroom);
			}
		}

		this.chat.onCatroomMessageReceived = (chatroom, message) => {
			if (typeof(this.onCatroomMessageReceived) === 'function') {
				this.onCatroomMessageReceived(chatroom, message);
			}
		}

		this.chat.onCatroomMessageUpdated = (chatroom, message) => {
			if (typeof(this.onCatroomMessageUpdated) === 'function') {
				this.onCatroomMessageUpdated(chatroom, message);
			}
		}

		this.chat.onCatroomMessageDeleted = (chatroom, message) => {
			if (typeof(this.onCatroomMessageDeleted) === 'function') {
				this.onCatroomMessageDeleted(chatroom, message);
			}
		}

		this.chat.onMemberConnectedToChatroom = (chatroom, member) => {
			if (typeof(this.onMemberConnectedToChatroom) === 'function') {
				this.onMemberConnectedToChatroom(chatroom, member);
			}
		}

		this.chat.onManagerConnectedToChatroom = (chatroom, manager) => {
			if (typeof(this.onManagerConnectedToChatroom) === 'function') {
				this.onManagerConnectedToChatroom(chatroom, manager);
			}
		}

		this.chat.onMemberDisconnectedFromChatroom = (chatroom, member) => {
			if (typeof(this.onMemberDisconnectedFromChatroom) === 'function') {
				this.onMemberDisconnectedFromChatroom(chatroom, member);
			}
		}
		
		this.chat.onManagerDisconnectedFromChatroom = (chatroom, manager) => {
			if (typeof(this.onManagerDisconnectedFromChatroom) === 'function') {
				this.onManagerDisconnectedFromChatroom(chatroom, manager);
			}
		}

		this.chat.onScreencastPermissionRequested = () => {
			if (typeof(this.onScreencastPermissionRequested) === 'function') {
				this.onScreencastPermissionRequested();
			}
		}

		this.chat.onManagerConnectedToScreencast = (manager) => {
			if (typeof(this.onManagerConnectedToScreencast) === 'function') {
				this.onManagerConnectedToScreencast(manager);
			}
		}

		this.chat.onManagerDisconnectedFromScreencast = (manager) => {
			if (typeof(this.onManagerDisconnectedFromScreencast) === 'function') {
				this.onManagerDisconnectedFromScreencast(manager);
			}
		}
	}

	
	/**
	 * @todo перекинуть из конструктора
	 */
	// function getUtm(name){
	// 	let utm = decodeURIComponent(location.search);
	// 	utm = utm.substr(1).split('&');
	// 	let objUtm = {};
	// 	utm.forEach(function(item){
	// 		let arrItem = item.split('=');
	// 		objUtm[arrItem[0]] = arrItem[1];
	// 	});

	// 	return objUtm[name];
	// }


	// перекинуть куда-нибудь где есть подключение
	// let date = new Date();
	// let time_zone_offset = date.getTimezoneOffset();
	// let visitorUpdate = {
	// 	browser_language: window.navigator.languages[0],
	// 	time_zone_offset: time_zone_offset
	// };

	// if (getUtm('utm_campaign') || getUtm('utm_content') || getUtm('utm_medium') || getUtm('utm_source') || getUtm('utm_term')) {
	// 	visitorUpdate.utm_campaign = getUtm('utm_campaign');
	// 	visitorUpdate.utm_content = getUtm('utm_content');
	// 	visitorUpdate.utm_medium = getUtm('utm_medium');
	// 	visitorUpdate.utm_source = getUtm('utm_source');
	// 	visitorUpdate.utm_term = getUtm('utm_term');
	// }

	// this.setVisitor(visitorUpdate);

	/**
	 * Starts connection.
	 * This method is **required** for launch connection.
	 */
	async start() {
		if (await this.chat.startConnection()) {
			return true;
		}
		return false;
	}

	/**
	 * Manually stops connection
	 */
	async stop() {
		if (await this.chat.stopConnection()) {
			this.onDisconnected();
			return true;
		}
		return false;
	}

	/**
	* Send text message in conversation
	* @param {string} messageText text to send in conversation
	*/
	async sendConversationTextMessage(messageText) {
		return await this.chat.sendTextMessage(messageText);
	}

	/**
	 * Send file message in conversation
	 * @param {File} file File to send. If file is JPEG/PNG file, it will be saved
	 * as image message, otherwise - file message. File can be up to 20Mb.
	 */
	async sendConversationFileMessage(file) {
		return await this.chat.sendFileMessage(file);
	}

	/**
	* Send text message in chatroom
	* @param {string} messageText text to send in chatroom
	* @param {string} chatroomId Chatroom ID where to send message
	*/
	async sendChatroomTextMessage(messageText, chatroomId) {
		return await this.chat.sendChatroomTextMessage(messageText, chatroomId);
	}

	/**
	 * Send file message in chatroom
	 * @param {File} file File to send. If file is JPEG/PNG file, it will be saved
	 * as image message, otherwise - file message. File can be up to 20Mb.
	 * @param {string} chatroomId Chatroom ID where to send message
	 */
	async sendChatroomFileMessage(file, chatroomId) {
		return await this.chat.sendChatroomFileMessage(file, chatroomId);
	}

	/**
	 * Connects visitor to chatrooms
	 * @param {string} chatroomId Chatroom's ID that visitor should connect to
	 */
	async connectToChatroom(chatroomId) {
		return await this.chat.connectToChatroom(chatroomId);
	}

	/**
	 * Disconnects visitor from chatroom
	 * @param {string} chatroomId Chatroom's ID that should disconnect from
	 */
	async disconnectFromChatroom(chatroomId) {
		return await this.chat.disconnectFromChatroom(chatroomId);
	}

	/**
	 * Gets all (avaliable for visitor) chatrooms. There will be public chatrooms and private, where visitor is connected.
	 * @param {number} page Current page number (starting at 1)
	 * @param {number} perPage Number of items per one page
	 */
	async getAllChatrooms(page = 1, perPage = 100) {
		return await this.chat.getAllChatrooms(page, perPage);
	}

	/**
	 * Gets all chatrooms where visitor is connected.
	 * @param {number} page Current page number (starting at 1)
	 * @param {number} perPage Number of items per one page
	 */
	async getMyChatrooms(page = 1, perPage = 100) {
		return await this.chat.getMyChatrooms(page, perPage);
	}

	/**
	 * Gets members of chatroom
	 * @param {string} chatroomId Chatroom ID
	 * @param {*} page number of page
	 * @param {*} perPage number of items per page
	 */
	async getChatroomMembers(chatroomId, page = 1, perPage = 50) {
		return this.chat.getChatroomMembers(chatroomId, perPage, page);
	}

	/**
	 * Gets chatroom from API
	 * @param {string} chatroomId Chatroom ID
	 */
	async getChatroom(chatroomId) {
		return await this.chat.getChatroom(chatroomId);
	}

	/**
	 * Sends visitor's answer for requesting screencast permission
	 * @param {boolean} flag User's decision. TRUE - allow screencast, FALSE = disallow.
	 */
	async sendScreencastPermission(flag) {
		await this.chat.sendScreencastPermission(flag);
	}

	/**
	 * Manually stops screencast
	 */
	async interruptScreencast() {
		return await this.chat.interruptScreencast();
	}

	/**
	 * Gets webchat information such as schedule 
	 */
	async getWebChat() {
		return this.chat.getWebchat();
	}

	/**
	 * Calculates number of minututes to next workink day
	 * @returns {number} X minutes to next workday. NULL if no working days specified in schedule, 
	 * 0 - if it is working hour now
	 * @todo Обработать правильную отдачу -1 и 0. 
	 */
	getMinutesToWork() {
		let date = new Date();

		let day = date.getDay() - 1;
		if (day === -1) {
			day = 6;
		}

		let minutesToWork;
		if (this.chat.chatInfo.schedule[day].is_work) {
			let timeDiff = (date.getTimezoneOffset() / 60) * -1 - (this.chat.chatInfo.time_zone_utc_offset);
			let workStart = this.chat.chatInfo.schedule[day].start + timeDiff;
			let workEnd = this.chat.chatInfo.schedule[day].end + timeDiff;
			let dateStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), workStart, 0, 0);
			let dateEnd = new Date(date.getFullYear(), date.getMonth(), date.getDate(), workEnd, 0, 0);

			if (dateStart > date && dateEnd < date) {
				// console.log('Рабочее время');
			}
			else {
				let comperableHour;
				let dayOfset = 0;
				if (dateEnd < date) {
					dayOfset = 1;
					if (this.chat.chatInfo.schedule[day + dayOfset]) {
						comperableHour = this.chat.chatInfo.schedule[day + dayOfset].start + timeDiff;
					}
					else {
						comperableHour = this.chat.chatInfo.schedule[0].start + timeDiff;
					}
				}
				else if (dateStart > date) {
					let dateManager = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours() - timeDiff, 0, 0);
					if (date.getDate() > dateManager.getDate()) {
						dayOfset = -1;
					}

					if (this.chat.chatInfo.schedule[day + dayOfset]) {
						comperableHour = this.chat.chatInfo.schedule[day + dayOfset].start + timeDiff;
					}
					else {
						comperableHour = this.chat.chatInfo.schedule[6].start + timeDiff;
					}
				}

				let comperableHourDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() + dayOfset, comperableHour, 0, 0);
				minutesToWork = Math.round((comperableHourDate - date) / 1000 / 60);
			}
		}
		else {
			minutesToWork = 0;
		}


		return minutesToWork;
	}

	/**
	 * Gets all managers of webchat
	 */
	async getAllManagers() {
		return await this.chat.connection.getWebChatManagers();
	}

	/**
	 * Gets all **connected** managers to current user conversation
	 */
	async getConnectedManagers() {
		return await this.chat.getConnectedManagers();
	}

	/**
	 * Sends typed, but have not sent yet text message.
	 * @param {string} text Text content of visitor's message field
	 */
	async visitorType(text) {
		await this.chat.visitorTypes(text);
	}

	/**
	 * Gets messages of visitor's conversation before `lastMess` (or last if it is NULL) in number of `amount`
	 * @param {string} lastMess last known message id
	 * @param {number} count number of messages to receive 
	 */
	async getConversationMessages(lastMess = null, count = 50) {
		return await this.chat.getConversationHistory(lastMess, count);
	}

	/**
	 * Gets messages of chatroom with ID `chatroomId` before `lastMess` (or last if it is NULL) in number of `amount`
	 * @param {string} chatroomId chatroom ID
	 * @param {string} lastMess last known message id
	 * @param {number} count number of messages to receive 
	 */
	async getChatroomMessages(chatroomId, lastMess = null, count = 50) {
		return await this.chat.getChatroomHistory(chatroomId, lastMess, count);
	}

	/**
	 * Sets current visitor's name
	 * @param {string} name Visitor's name to set
	 */
	async setName(name) {
		let visitor = this.chat.visitor;
		visitor.name = name;
		await this.chat.setVisitor(visitor);
	}

	/**
	 * Sets current visitor's email
	 * @param {string} email Visitor's email to set
	 */
	async setEmail(email) {
		let visitor = this.chat.visitor;
		visitor.email = email;
		await this.chat.setVisitor(visitor);
	}

	/**
	 * Sets visitor's field by key and value
	 * @param {string} key field key
	 * @param {string|number|boolean} value 
	 */
	async setField(key, value) {
		if (typeof(key) !== "string") {
			throw new Error("Field key has to be string");
		}
		if (typeof(value) !== "string" 
			&& typeof(value) !== "number"
			&& typeof(value) !== "boolean"
			&& value !== null) {
			throw new Error("Field value can be string, number, boolean or null");
		}
		let visitor = this.chat.visitor;
		visitor.fields[key] = value;
		await this.chat.setVisitor(visitor);
	}


	/**
	 * Sets current visitor object
	 * @param {Visitor} visitor 
	 */
	async setVisitor(visitor) {
		if (this.chat.visitor && visitor) {
			if (this.chat.visitor.name !== visitor.name || this.chat.visitor.email !== visitor.email || this.chat.visitor.fields !== visitor.fields) {
				this.chat.visitor.name = visitor.name || this.chat.visitor.name;
				this.chat.visitor.email = visitor.email || this.chat.visitor.email;
				this.chat.visitor.fields = visitor.fields || this.chat.visitor.fields;
				this.chat.setVisitor(visitor);
			}
		}
	}

	/**
	  * Getting current visitor details
	  * @return {Visitor}
	  */
	getVisitor() {
		return this.chat.visitor;
	}
}

export default ChatixCore;

/**
 * @callback onConnectedCallback callback called after successfully established connection to chatix server
 */

/**
 * @callback onDisconnectedCallback callback called when connection to chatix server was interrupted.
 */

/**
 * @callback onManagerConnectedToConversationCallback calls after manager connects to conversation with current
 * visitor. It is useful to display currently connected managers in widget
 * @param {Manager} manager who connected to conversation
 */

/**
 * @callback onManagerDisconnectedFromConversationCallback calls after manager disconnects from conversation 
 * with current visitor. It is useful to display currently connected managers in widget
 * @param {Manager} manager who disconnected from conversation
 */

/**
 * @callback onConversationMessageReceivedCallback calls after someone sent new message to conversation with 
 * current visitor. Message can be sent by manager or by visitor. It also calls after {@link ChatixCore#sendMessage} method
 * @param {Message} message new message in conversation
 */

/**
 * @callback onConversationMessageUpdatedCallback calls after manager updated message in conversation with 
 * current visitor. Manager can update all text messages in conversation.
 * @param {Message} message updated instance of message. Use Message.uuid to define which one was updated 
 */

/**
 * @callback onConversationMessageDeletedCallback calls after manager deleted message in conversation with 
 * current visitor. Manager can delete all messages in conversation.
 * @param {Message} message Deleted instance of message. Use Message.uuid to define which one was deleted 
 */

/**
 * @callback onChatroomMessageReceivedCallback calls after someone sent new message to chatroom.
 * It also calls after {@link ChatixCore#sendChatroomMessage} method
 * @param {Message} message new message in conversation
 * @param {Chatroom} chatroom chatroom where message received
 */

/**
 * @callback onChatroomMessageUpdatedCallback calls after manager updated message in chatroom. 
 * Manager can update all text messages in chatroom.
 * @param {Message} message updated instance of message. Use Message.uuid to define which one was updated 
 * @param {Chatroom} chatroom chatroom where message was updated
 */

/**
 * @callback onChatroomMessageDeletedCallback calls after manager deleted message in chatroom. 
 * Manager can delete all messages in chatroom.
 * @param {Message} message Deleted instance of message. Use Message.uuid to define which one was deleted 
 * @param {Chatroom} chatroom chatroom where message was deleted
 */

 /**
  * @callback onVisitorConnectedToChatroomCallback calls after new member was connected to chatroom
  * @param {ChatroomMember} member who was connected
  * @param {Chatroom} chatroom where member was connected
  */

/**
 * @callback onManagerConnectedToChatroomCallback calls after new manager was connected to chatroom
 * @param {Manager} manager who was connected
 * @param {Chatroom} chatroom where manager was connected
 */

 /**
  * @callback onVisitorDisconnectedFromChatroomCallback calls after new member was disconnected from chatroom
  * @param {ChatroomMember} member who was disconnected
  * @param {Chatroom} chatroom where member was disconnected
  */

/**
 * @callback onManagerDisconnectedFromChatroomCallback calls after new manager was disconnected from chatroom
 * @param {Manager} manager who was disconnected
 * @param {Chatroom} chatroom where manager was disconnected
 */

/**
 * @callback onScreencastPermissionRequestedCallback calls after someone of manager requested permissions to watch visitor's screen.
 * NOTE: Permission will be asked only for first manager. If visitor allows screencast, the second manager, 
 * who will try to connect to screencast will connect without requesting permission.
 * Screencast permission will be requested by first manager and will be resetted when all managers disconnected 
 * from allowed screencast.
 * Visitor's permission has to be sent via {@link ChatixCore#sendScreencastPermission}
 */

/**
 * @callback onManagerConnectedToScreencastCallback calls after each manager gets and access to visitor's screen
 * @param {Manager} manager who connected to screencast
 */

/**
 * @callback onManagerDisconnectedFromScreencastCallback calls after each manager exits screencast
 * @param {Manager} manager who disconnected from screencast
 */