portal.mkgtu.ru/clientSide/components/chat/chat.js

360 lines
11 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {bindFileInputEvents, eraseFileInput, fileStructuredClone} from './chat-file-uploader'
import './chat-search'
import {
clearPeriodicUpdateChatPeopleList,
initPeriodicUpdateChatPeopleList,
isEmptyValue,
updateChatPeopleList
} from "./common";
export default class Chat {
static INCOMING_TEMPLATE = "incoming-template";
constructor(chatId) {
this.chatId = chatId;
this.filesToSend = "";
this.messageToSend = "";
}
init() {
this.cacheDOM();
this.bindEvents();
this.renderChatHistory();
}
cacheDOM() {
this.$chatHeader = $(".chat-header");
this.$button = $("#send-btn-message");
this.$chatHistory = $(".chat-history");
this.$textarea = $("#message-to-send");
this.$chatSelectBtn = $(".chat-select-btn");
this.$chatPeopleList = $(".people-list");
this.$fileUploader = $(".chat-file-uploader");
this.$chatHistoryList = this.$chatHistory.find("ul");
}
bindEvents() {
this.$button.off();
this.$button.on("click", this.addMessage.bind(this));
this.$textarea.off();
this.$textarea.on("keyup", this.addMessageByEnterBtn.bind(this));
}
/**
* Функция включения анимации загрузки
*
* @returns {void}
*/
startLoading() {
let emptyChat = $(".empty-chat-history");
if (emptyChat.length < 1) {
this.$chatHistoryList?.empty();
this.$chatHistoryList?.append("<li class='empty-chat-history d-flex justify-content-center align-items-center'></li>");
emptyChat = $(".empty-chat-history");
}
emptyChat.empty();
emptyChat.addClass("loading");
this.renderChatHeader();
}
/**
* Отрисовка заголовка чата
*
* @param {object|null} response - ответ сервера в формате JSON
*
* @returns {void}
*/
renderChatHeader(response = null) {
this.$chatHeader?.empty();
if (response) {
this.$chatHeader?.append(response);
}
}
templateCompile(template, data) {
const pattern = /{{\s*(\w+?)\s*}}/g; // {{property}}
return template.replace(pattern, (_, token) => data[token] || "");
}
renderChatHistory(senderType) {
if (
(this.messageToSend.trim() !== "" || this.filesToSend.length > 0) &&
!isEmptyValue(senderType)
) {
let messageContext = {};
if (this.messageToSend.trim() !== "") {
messageContext = this.renderChatHistoryMessage(senderType);
}
let fileContexts = [];
if (this.filesToSend.length > 0) {
fileContexts = this.renderChatHistoryFile(senderType);
}
this.scrollToBottom();
if (senderType === Chat.INCOMING_TEMPLATE) {
eraseFileInput();
this.$textarea.val("");
this.sendMessage(messageContext, fileContexts);
}
}
}
renderChatHistoryMessage(senderType) {
let template = $(`#${senderType}-message`).html();
let context = {
messageOutput: this.messageToSend.trim(),
time: this.getCurrentDateTime(),
messageUid: this.getRandomUid(),
};
this.$chatHistoryList.append(this.templateCompile(template, context));
return context;
}
renderChatHistoryFile(senderType) {
let contexts = [];
if (this.filesToSend.length > 0) {
let template = $(`#${senderType}-file`).html();
for (let I = 0; I < this.filesToSend.length; I++) {
const file = this.filesToSend[I];
const context = {
time: this.getCurrentDateTime(),
fileUid: this.getRandomUid(),
fileName: file.name,
};
this.$chatHistoryList.append(this.templateCompile(template, context));
contexts.push(context);
}
}
return contexts;
}
/**
* Функция генерации случайного идентификатора
*
* @returns {string}
*/
getRandomUid() {
return `${Math.random().toString(36).substring(2, 15)}-${Date.now()}-${Math.random()
.toString(36)
.substring(2, 15)}`;
}
/**
* Инкрементами счётчика сообщений
*
* @param {int} delta
*
* @returns {void}
*/
incrementMessageCounter(delta = 1) {
let counter = $(".chat-num-messages span");
if (isEmptyValue(counter?.text())) {
counter?.text("1");
}
counter?.html(parseInt(counter?.text()) + delta);
}
/**
* Функция отправки сообщения
*
* @param {object} messageContext - контекст сообщения
* @param {array} fileContexts - контексты файлов
*
* @returns {void}
*/
sendMessage(messageContext, fileContexts) {
let chat = this;
let formData = new FormData();
if (chat.filesToSend.length > 0) {
for (let I = 0; I < chat.filesToSend.length; I++) {
const file = chat.filesToSend[I];
formData.append(`file[${I}]`, file, file.name);
}
}
formData.append("chatId", this.chatId);
formData.append("usersIds", JSON.stringify(this.getUsersIds(), null, 2));
formData.append("fileContexts", JSON.stringify(fileContexts, null, 2));
formData.append("messageContext", JSON.stringify(messageContext, null, 2));
let totalSendMessageCount = chat.filesToSend.length;
if (!isEmptyValue(messageContext?.messageOutput)) {
totalSendMessageCount++;
}
$.ajax({
url: sendMessageUrl,
type: "POST",
data: formData,
processData: false,
contentType: false,
beforeSend: function () {
clearPeriodicUpdateChatPeopleList();
},
})
.done(function (response) {
chat.chatId = response.chatId;
if (response.blobsUid.length > 0) {
chat.changeBlobStatus(response.blobsUid, "sending", "fa fa-check success");
}
bindFileInputEvents();
chat.incrementMessageCounter(totalSendMessageCount);
updateChatPeopleList();
})
.fail(function (response) {
// TODO: добавить эвент превращающий клик в повторную отправку
if (response.responseJSON.blobsUid.length > 0) {
chat.changeBlobStatus(
response?.responseJSON?.blobsUid,
"sending",
"fa fa-exclamation-triangle error"
);
}
})
.always(function () {
initPeriodicUpdateChatPeopleList();
});
}
changeBlobStatus(blobsUid, removeClass, addClass) {
for (let I = 0; I < blobsUid.length; I++) {
const blobUid = blobsUid[I];
let blobStatus = $(`#${blobUid}`)?.find(".message-status");
if (blobStatus) {
blobStatus.removeClass(removeClass);
blobStatus.addClass(addClass);
}
}
}
/**
* Функция получения идентификаторов пользователей
*
* @returns {array} - массив идентификаторов пользователей
*/
getUsersIds() {
let destinationUserId = this.$chatHeader?.find(".chat-about")?.data("destination_user_id");
return [destinationUserId, currentUserId];
}
addMessage() {
this.filesToSend = "";
const filesTemp = $(".chat-file-uploader #file-upload")[0]?.files;
if (!isEmptyValue(filesTemp)) {
this.filesToSend = filesTemp;
fileStructuredClone();
}
this.messageToSend = this.$textarea.val();
this.renderChatHistory(Chat.INCOMING_TEMPLATE);
}
addMessageByEnterBtn(event) {
// enter was pressed
if (event.keyCode === 13) {
this.addMessage();
}
}
scrollToBottom() {
this.$chatHistory.scrollTop(this.$chatHistory[0]?.scrollHeight);
}
getCurrentTime() {
return new Date().toLocaleTimeString().replace(/([\d]+:[\d]{2})(:[\d]{2})(.*)/, "$1$3");
}
getCurrentDateTime() {
let time = this.getCurrentTime();
let date = this.formatDate(new Date());
return `${date} ${time}`;
}
getRandomItem(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
/**
* Функция перерисовки истории чата
*
* @param {string} refreshedHistory
*
* @returns {void}
*/
refreshChatHistory(refreshedHistory) {
this.$chatHistoryList.empty();
this.$chatHistoryList.html(refreshedHistory);
}
/**
* Дополняет входящий номер до 2х символов
* заполняя пустоту нулями (`0`)
*
* @param {string} num
*
* @returns {string}
*/
padTo2Digits(num) {
return this.padStart(num.toString(), 2, "0");
}
/**
* В IE11 нет функции `String.prototype.padStart`
* Придётся заменять её в ручную
*
* @param {string} originString
* @param {int} targetLength
* @param {string} padString
*
* @returns {string}
*/
padStart(originString, targetLength, padString) {
targetLength = targetLength >> 0;
padString = String(typeof padString !== "undefined" ? padString : " ");
if (originString.length > targetLength) {
return String(originString);
} else {
targetLength = targetLength - originString.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length);
}
return padString.slice(0, targetLength) + String(originString);
}
}
/**
* Генерирует строку даты формата 'd.m.Y'
*
* @param {Date} date
*
* @returns {string}
*/
formatDate(date) {
return [
this.padTo2Digits(date.getDate()),
this.padTo2Digits(date.getMonth() + 1),
date.getFullYear(),
].join(".");
}
}