|
|
@ -1,5 +1,5 @@ |
|
|
|
import * as DOMPurify from 'dompurify'; |
|
|
|
import * as log from 'loglevel'; |
|
|
|
import * as DOMPurify from "dompurify"; |
|
|
|
import * as log from "loglevel"; |
|
|
|
import { ChatMessageDTO } from "../dto/ChatMessageDTO"; |
|
|
|
import { MessageCipherDTO } from "../dto/MessageCipherDTO"; |
|
|
|
import { ChatModel } from "../model/ChatModel"; |
|
|
@ -10,238 +10,225 @@ import { JsonAPI } from "../singleton/JsonAPI"; |
|
|
|
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
|
|
|
import { ChatViewDeps } from "./ChatViewDeps"; |
|
|
|
import { fetchHandler } from "./FetchHandler"; |
|
|
|
import { ObserverData } from '../observe/ObserverData'; |
|
|
|
import { NotificationService } from '../service/NotificationService'; |
|
|
|
import { UserModel } from '../model/UserModel'; |
|
|
|
import { ObserverData } from "../observe/ObserverData"; |
|
|
|
import { NotificationService } from "../service/NotificationService"; |
|
|
|
import { UserModel } from "../model/UserModel"; |
|
|
|
|
|
|
|
export class ChatView implements Observer<ChatMessageViewModel> { |
|
|
|
private readonly _chatModel: ChatModel; |
|
|
|
private readonly _messageContainer: HTMLElement; |
|
|
|
private readonly _messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>; |
|
|
|
private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>; |
|
|
|
private readonly _markdownService: MarkDownService; |
|
|
|
private readonly _encryptionService: EncryptionService; |
|
|
|
private readonly _notificationService: NotificationService; |
|
|
|
private readonly _userModel: UserModel; |
|
|
|
|
|
|
|
|
|
|
|
constructor(deps: ChatViewDeps) { |
|
|
|
this._messageContainer = deps.messageContainer; |
|
|
|
this._chatModel = deps.chatModel; |
|
|
|
this._messageSendTemplate = deps.messageSendTemplate; |
|
|
|
this._messageReceiveTemplate = deps.messageReceiveTemplate; |
|
|
|
this._markdownService = deps.markdownService; |
|
|
|
this._encryptionService = deps.encryptionService; |
|
|
|
this._notificationService = deps.notificationService; |
|
|
|
this._userModel = deps.userModel; |
|
|
|
this._initEventListeners(); |
|
|
|
|
|
|
|
$(document).ready(function () { |
|
|
|
$('#action_menu_btn').click(function () { |
|
|
|
$('.action_menu').toggle(); |
|
|
|
private readonly _chatModel: ChatModel; |
|
|
|
private readonly _messageContainer: HTMLElement; |
|
|
|
private readonly _messageSendTemplate: Handlebars.TemplateDelegate< |
|
|
|
ChatMessageViewModel |
|
|
|
>; |
|
|
|
private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate< |
|
|
|
ChatMessageViewModel |
|
|
|
>; |
|
|
|
private readonly _markdownService: MarkDownService; |
|
|
|
private readonly _encryptionService: EncryptionService; |
|
|
|
private readonly _notificationService: NotificationService; |
|
|
|
private readonly _userModel: UserModel; |
|
|
|
|
|
|
|
constructor(deps: ChatViewDeps) { |
|
|
|
this._messageContainer = deps.messageContainer; |
|
|
|
this._chatModel = deps.chatModel; |
|
|
|
this._messageSendTemplate = deps.messageSendTemplate; |
|
|
|
this._messageReceiveTemplate = deps.messageReceiveTemplate; |
|
|
|
this._markdownService = deps.markdownService; |
|
|
|
this._encryptionService = deps.encryptionService; |
|
|
|
this._notificationService = deps.notificationService; |
|
|
|
this._userModel = deps.userModel; |
|
|
|
this._initEventListeners(); |
|
|
|
|
|
|
|
$(document).ready(function () { |
|
|
|
$("#action_menu_btn").click(function () { |
|
|
|
$(".action_menu").toggle(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
this._chatMessagePageLoadAjax(); |
|
|
|
} |
|
|
|
|
|
|
|
update(cd: ObserverData<ChatMessageViewModel>): void { |
|
|
|
log.info("ChatView: updating view"); |
|
|
|
|
|
|
|
switch (cd.op) { |
|
|
|
case "clear": |
|
|
|
{ |
|
|
|
$(this._messageContainer).html(""); |
|
|
|
} |
|
|
|
break; |
|
|
|
case "new": |
|
|
|
{ |
|
|
|
cd.data.forEach((vm: ChatMessageViewModel) => { |
|
|
|
let rendered = this.renderMessage(vm); |
|
|
|
$(this._messageContainer).append(rendered); |
|
|
|
}); |
|
|
|
$(this._messageContainer) |
|
|
|
.stop() |
|
|
|
.animate( |
|
|
|
{ |
|
|
|
scrollTop: $(this._messageContainer)[0].scrollHeight, |
|
|
|
}, |
|
|
|
1500 |
|
|
|
); |
|
|
|
} |
|
|
|
break; |
|
|
|
case "update": |
|
|
|
{ |
|
|
|
cd.data.forEach((vm: ChatMessageViewModel) => { |
|
|
|
let rendered = this.renderMessage(vm); |
|
|
|
$(this._messageContainer).append(rendered); |
|
|
|
}); |
|
|
|
// if (cd.data.length > 0) {
|
|
|
|
// this._userModel.notify();
|
|
|
|
// }
|
|
|
|
} |
|
|
|
break; |
|
|
|
case "page": |
|
|
|
{ |
|
|
|
{ |
|
|
|
const rev: ChatMessageViewModel[] = Object.create(cd.data); |
|
|
|
rev.reverse(); |
|
|
|
rev.forEach((vm: ChatMessageViewModel) => { |
|
|
|
let rendered = this.renderMessage(vm); |
|
|
|
$(this._messageContainer).prepend(rendered); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
this._chatMessagePageLoadAjax(); |
|
|
|
} |
|
|
|
|
|
|
|
update(cd: ObserverData<ChatMessageViewModel>): void { |
|
|
|
log.info('ChatView: updating view'); |
|
|
|
|
|
|
|
switch (cd.op) { |
|
|
|
case "clear": { |
|
|
|
$(this._messageContainer).html(""); |
|
|
|
} break; |
|
|
|
case "new": { |
|
|
|
const rev: ChatMessageViewModel[] = Object.create(cd.data) |
|
|
|
// rev.reverse();
|
|
|
|
let arr: string[] = []; |
|
|
|
rev.forEach((vm: ChatMessageViewModel) => { |
|
|
|
const vmTemp: ChatMessageViewModel = { ...vm }; |
|
|
|
vmTemp.message = this._markdownService.render(vm.message); |
|
|
|
/** Very Important!!! |
|
|
|
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!! |
|
|
|
*/ |
|
|
|
let rendered; |
|
|
|
if (vmTemp.fromUser == JsonAPI.principleName) { |
|
|
|
rendered = DOMPurify.sanitize(this._messageSendTemplate(vmTemp)); |
|
|
|
|
|
|
|
} |
|
|
|
else { |
|
|
|
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp)); |
|
|
|
} |
|
|
|
$(this._messageContainer).append(rendered); |
|
|
|
}); |
|
|
|
$(this._messageContainer).stop().animate({ |
|
|
|
scrollTop: $(this._messageContainer)[0].scrollHeight |
|
|
|
}, 1500); |
|
|
|
} break; |
|
|
|
case "update": { |
|
|
|
const rev: ChatMessageViewModel[] = Object.create(cd.data) |
|
|
|
// rev.reverse();
|
|
|
|
let arr: string[] = []; |
|
|
|
rev.forEach((vm: ChatMessageViewModel) => { |
|
|
|
const vmTemp: ChatMessageViewModel = { ...vm }; |
|
|
|
vmTemp.message = this._markdownService.render(vm.message); |
|
|
|
/** Very Important!!! |
|
|
|
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!! |
|
|
|
*/ |
|
|
|
let rendered; |
|
|
|
if (vmTemp.fromUser == JsonAPI.principleName) { |
|
|
|
rendered = DOMPurify.sanitize(this._messageSendTemplate(vmTemp)); |
|
|
|
|
|
|
|
} |
|
|
|
else { |
|
|
|
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp)); |
|
|
|
} |
|
|
|
$(this._messageContainer).append(rendered); |
|
|
|
}); |
|
|
|
if(rev.length > 0) { |
|
|
|
// $(this._messageContainer).stop().animate({
|
|
|
|
// scrollTop: $(this._messageContainer)[0].scrollHeight
|
|
|
|
// }, 1500);
|
|
|
|
this._userModel.notify(); |
|
|
|
} |
|
|
|
} break; |
|
|
|
default: { |
|
|
|
const rev: ChatMessageViewModel[] = Object.create(cd.data) |
|
|
|
rev.reverse(); |
|
|
|
let arr: string[] = []; |
|
|
|
rev.forEach((vm: ChatMessageViewModel) => { |
|
|
|
const vmTemp: ChatMessageViewModel = { ...vm }; |
|
|
|
vmTemp.message = this._markdownService.render(vm.message); |
|
|
|
/** Very Important!!! |
|
|
|
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!! |
|
|
|
*/ |
|
|
|
let rendered; |
|
|
|
if (vmTemp.fromUser == JsonAPI.principleName) { |
|
|
|
rendered = DOMPurify.sanitize(this._messageSendTemplate(vmTemp)); |
|
|
|
|
|
|
|
} |
|
|
|
else { |
|
|
|
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp)); |
|
|
|
} |
|
|
|
$(this._messageContainer).prepend(rendered); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
default: |
|
|
|
new Error("Invalid option"); |
|
|
|
} |
|
|
|
|
|
|
|
private _initEventListeners(): void { |
|
|
|
this._addChatFormEL(); |
|
|
|
} |
|
|
|
|
|
|
|
private renderMessage(vm: ChatMessageViewModel): string { |
|
|
|
const vmTemp: ChatMessageViewModel = { ...vm }; |
|
|
|
vmTemp.message = this._markdownService.render(vm.message); |
|
|
|
switch (vmTemp.fromUser) { |
|
|
|
case JsonAPI.principleName: |
|
|
|
return DOMPurify.sanitize(this._messageSendTemplate(vmTemp)); |
|
|
|
default: |
|
|
|
return DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp)); |
|
|
|
} |
|
|
|
|
|
|
|
private _addChatFormEL() { |
|
|
|
const chatForm = document.getElementById('chatMessageForm') as HTMLSelectElement; |
|
|
|
|
|
|
|
if (chatForm == null) { |
|
|
|
log.error("Chat form is null"); |
|
|
|
} |
|
|
|
else { |
|
|
|
chatForm.addEventListener('submit', (e) => this._createChatMessageDTO(e, chatForm)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private _initEventListeners(): void { |
|
|
|
this._addChatFormEL(); |
|
|
|
} |
|
|
|
|
|
|
|
private _addChatFormEL() { |
|
|
|
const chatForm = document.getElementById( |
|
|
|
"chatMessageForm" |
|
|
|
) as HTMLSelectElement; |
|
|
|
|
|
|
|
if (chatForm == null) { |
|
|
|
log.error("Chat form is null"); |
|
|
|
} else { |
|
|
|
chatForm.addEventListener("submit", (e) => |
|
|
|
this._createChatMessageDTO(e, chatForm) |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private _createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void { |
|
|
|
e.preventDefault(); |
|
|
|
|
|
|
|
let contactName = JsonAPI.contactName; |
|
|
|
|
|
|
|
if (contactName == null) { |
|
|
|
log.error("Contact name is null"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!chatForm.checkValidity()) { |
|
|
|
log.error("Form is not valid"); |
|
|
|
chatForm.classList.add('was-validated'); |
|
|
|
return; |
|
|
|
} |
|
|
|
chatForm.classList.add('was-validated'); |
|
|
|
|
|
|
|
private _createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void { |
|
|
|
e.preventDefault(); |
|
|
|
|
|
|
|
const chatInput = document.getElementById('chatInput') as HTMLInputElement; |
|
|
|
const vm = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName); |
|
|
|
// new Date().
|
|
|
|
vm!.lastMessageTime = new Date(); |
|
|
|
|
|
|
|
const passphrase = vm?.passphrase |
|
|
|
let contactName = JsonAPI.contactName; |
|
|
|
|
|
|
|
if (chatInput.value == '' || chatInput.value == null) { |
|
|
|
this._notificationService.error("Please enter a message"); |
|
|
|
log.error("Chat input is null."); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// if (passphraseInput.value == '' || passphraseInput.value == null) {
|
|
|
|
// this._notificationService.error("Please enter a passphrase");
|
|
|
|
// log.error("Passphrase is null.");
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
const messageContent = chatInput.value; |
|
|
|
vm!.lastMessageText = messageContent; |
|
|
|
const msgTime = new Date(); |
|
|
|
const context: ChatMessageViewModel = { |
|
|
|
fromUser: JsonAPI.principleName || "error", |
|
|
|
toUser: "", |
|
|
|
message: messageContent, |
|
|
|
messageTime: msgTime |
|
|
|
}; |
|
|
|
|
|
|
|
this.update({ data: new Array(context), op: "new" }) |
|
|
|
this._userModel.notify(); |
|
|
|
|
|
|
|
let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(passphrase!, messageContent) |
|
|
|
let chatMessageDTO = { |
|
|
|
"fromUser": JsonAPI.principleName || "", |
|
|
|
"toUser": contactName, |
|
|
|
"messageCipher": messageCipher, |
|
|
|
messageTime: msgTime |
|
|
|
} |
|
|
|
this._sendMessageAJAX(chatMessageDTO); |
|
|
|
if (!chatForm.checkValidity()) { |
|
|
|
log.error("Form is not valid"); |
|
|
|
chatForm.classList.add("was-validated"); |
|
|
|
return; |
|
|
|
} |
|
|
|
chatForm.classList.add("was-validated"); |
|
|
|
|
|
|
|
private _sendMessageAJAX(chatMessageDTO: ChatMessageDTO): void { |
|
|
|
let headers = new Headers(); |
|
|
|
// console.log("Token = " + btoa("hmm" + ":" + "hmm"))
|
|
|
|
|
|
|
|
// headers.append('Accept','application/json')
|
|
|
|
headers.append('Content-Type', 'application/json'); |
|
|
|
// headers.append('Authorization', basicAuthToken);
|
|
|
|
// @ts-ignore
|
|
|
|
headers.append('X-AUTH-TOKEN', JsonAPI.authToken); |
|
|
|
fetch(JsonAPI.MESSAGE_POST, { |
|
|
|
method: 'POST', |
|
|
|
headers: headers, |
|
|
|
body: JSON.stringify(chatMessageDTO) |
|
|
|
}) |
|
|
|
.then(response => { |
|
|
|
log.debug(response); |
|
|
|
return response.clone(); |
|
|
|
}) |
|
|
|
.then(response => fetchHandler(response, this._notificationService)); |
|
|
|
const chatInput = document.getElementById("chatInput") as HTMLInputElement; |
|
|
|
const vm = this._userModel.activeUsersList.find( |
|
|
|
(u) => u.userName == JsonAPI.contactName |
|
|
|
); |
|
|
|
// new Date().
|
|
|
|
vm!.lastMessageTime = new Date(); |
|
|
|
|
|
|
|
} |
|
|
|
const passphrase = vm?.passphrase; |
|
|
|
|
|
|
|
private _chatMessagePageLoadAjax() { |
|
|
|
this._messageContainer.addEventListener('scroll', (e) => { |
|
|
|
if ($(this._messageContainer).scrollTop() == 0 && $(this._messageContainer).html() != "") { |
|
|
|
let currentMsg = $('.msg:first'); |
|
|
|
log.debug('Reached top') |
|
|
|
const vm = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName) |
|
|
|
if (JsonAPI.contactName != null) |
|
|
|
|
|
|
|
this._chatModel.getMessages(vm!, "page").then(() => { |
|
|
|
if (currentMsg != null) { |
|
|
|
// log.debug(currentMsg.offset()!.top)
|
|
|
|
$(this._messageContainer).scrollTop(currentMsg.position().top - $('.msg').position()!.top) |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}) |
|
|
|
if (chatInput.value == "") { |
|
|
|
this._notificationService.error("Please enter a message"); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// if (passphraseInput.value == '' || passphraseInput.value == null) {
|
|
|
|
// this._notificationService.error("Please enter a passphrase");
|
|
|
|
// log.error("Passphrase is null.");
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
const messageContent = chatInput.value; |
|
|
|
vm!.lastMessageText = messageContent.slice(0, 15) + "..."; |
|
|
|
const msgTime = new Date(); |
|
|
|
const context: ChatMessageViewModel = { |
|
|
|
fromUser: JsonAPI.principleName, |
|
|
|
toUser: "", |
|
|
|
message: messageContent, |
|
|
|
messageTime: msgTime, |
|
|
|
}; |
|
|
|
|
|
|
|
this.update({ data: new Array(context), op: "new" }); |
|
|
|
this._userModel.notify(); |
|
|
|
|
|
|
|
let messageCipher: MessageCipherDTO = this._encryptionService.encrypt( |
|
|
|
passphrase!, |
|
|
|
messageContent |
|
|
|
); |
|
|
|
let chatMessageDTO = { |
|
|
|
fromUser: JsonAPI.principleName, |
|
|
|
toUser: contactName, |
|
|
|
messageCipher: messageCipher, |
|
|
|
messageTime: msgTime, |
|
|
|
}; |
|
|
|
this._sendMessageAJAX(chatMessageDTO); |
|
|
|
} |
|
|
|
|
|
|
|
private _sendMessageAJAX(chatMessageDTO: ChatMessageDTO): void { |
|
|
|
let headers = new Headers(); |
|
|
|
// console.log("Token = " + btoa("hmm" + ":" + "hmm"))
|
|
|
|
|
|
|
|
// headers.append('Accept','application/json')
|
|
|
|
headers.append("Content-Type", "application/json"); |
|
|
|
// headers.append('Authorization', basicAuthToken);
|
|
|
|
// @ts-ignore
|
|
|
|
headers.append("X-AUTH-TOKEN", JsonAPI.authToken); |
|
|
|
fetch(JsonAPI.MESSAGE_POST, { |
|
|
|
method: "POST", |
|
|
|
headers: headers, |
|
|
|
body: JSON.stringify(chatMessageDTO), |
|
|
|
}) |
|
|
|
.then((response) => { |
|
|
|
log.debug(response); |
|
|
|
return response.clone(); |
|
|
|
}) |
|
|
|
.then((response) => fetchHandler(response, this._notificationService)); |
|
|
|
} |
|
|
|
|
|
|
|
private _chatMessagePageLoadAjax() { |
|
|
|
this._messageContainer.addEventListener("scroll", (e) => { |
|
|
|
if ( |
|
|
|
$(this._messageContainer).scrollTop() == 0 && |
|
|
|
$(this._messageContainer).html() != "" |
|
|
|
) { |
|
|
|
let currentMsg = $(".msg:first"); |
|
|
|
log.debug("Reached top"); |
|
|
|
const vm = this._userModel.activeUsersList.find( |
|
|
|
(u) => u.userName == JsonAPI.contactName |
|
|
|
); |
|
|
|
this._chatModel.getMessages(vm!, "page").then(() => { |
|
|
|
if (currentMsg != null) { |
|
|
|
// log.debug(currentMsg.offset()!.top)
|
|
|
|
$(this._messageContainer).scrollTop( |
|
|
|
currentMsg.position().top - $(".msg").position()!.top |
|
|
|
); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |