A self hosted chat application with end-to-end encrypted messaging.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

159 lines
6.2 KiB

import { Observer } from "../observe/Observer";
import { TemplateFactory } from "../template/TemplateFactory";
import { ChatModel } from "../model/ChatModel";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import * as log from 'loglevel';
import * as DOMPurify from 'dompurify';
import { MarkDownService } from "../service/MarkDownService";
import { MarkDownItMarkDownService } from "../service/MarkDownItMarkDownService";
import { JsonAPI } from "../singleton/JsonAPI";
import { MessageCipherDTO } from "../dto/MessageCipherDTO";
import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
import { EncryptionService } from "../service/EncryptionService";
import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import { fetchHandler } from "./FetchHandler";
import { ChatViewDeps } from "./ChatViewDeps";
export class ChatView implements Observer {
private readonly _chatModel: ChatModel;
private readonly _messageContainer: HTMLElement;
// private readonly _messageSendTemplate = TemplateFactory.getTemplate('msg_container_send_template');
// private readonly _messageReceiveTemplate = TemplateFactory.getTemplate('msg_container_template');
// private readonly _markdownService: MarkDownService = new MarkDownItMarkDownService();
// private readonly _encryptionService: EncryptionService = new SJCLEncryptionService();
private readonly _messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
private readonly _markdownService: MarkDownService;
private readonly _encryptionService: EncryptionService;
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.addEventListeners();
}
update(data: ChatMessageViewModel[]): void {
log.info('ChatView: updating view');
// let html: string = "";
this._messageContainer.innerHTML = "";
data.forEach((vm: ChatMessageViewModel) => {
const vmTemp = 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);
log.debug()
// html += this._messageSendTemplate(vm);
});
// html = DOMPurify.sanitize(md.render(html));
// this._element.innerHTML = html;
// log.debug(this._element.innerHTML);
}
private addEventListeners(): 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()) {
console.log("error");
chatForm.classList.add('was-validated');
return;
}
chatForm.classList.add('was-validated');
const chatInput = document.getElementById('chatInput') as HTMLInputElement;
const passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
if (chatInput.value == '' || chatInput.value == null) {
log.error("Chat input is null.");
return;
}
if (passphraseInput.value == '' || passphraseInput.value == null) {
log.error("Chat input is null.");
return;
}
// @ts-ignore
const messageContent = chatInput.value;
const context = { fromUser: JsonAPI.principleName, toUser: "", message: this._markdownService.render(messageContent), messageTime: new Date().toLocaleString() };
// @ts-ignore
const msgContainer: string = this._messageSendTemplate(context);
$(this._messageContainer).append(DOMPurify.sanitize(msgContainer));
// scrollChatAreaAnimated(2400);
// let messageCipher = sjcl.encrypt(passphraseInput.value, messageContent, { mode: "gcm", ts: 128, adata: "", iter: iterations });
let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(passphraseInput.value, messageContent)
// let messageCipherJson = JSON.parse(messageCipher);
let chatMessageDTO = {
"fromUser": JsonAPI.principleName,
"toUser": contactName,
"messageCipher": messageCipher,
// "messageTime": null
}
// @ts-ignore
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));
}
}