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.

158 lines
6.2 KiB

5 years ago
5 years ago
5 years ago
  1. import { Observer } from "../observe/Observer";
  2. import { TemplateFactory } from "../template/TemplateFactory";
  3. import { ChatModel } from "../model/ChatModel";
  4. import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
  5. import * as log from 'loglevel';
  6. import * as DOMPurify from 'dompurify';
  7. import { MarkDownService } from "../service/MarkDownService";
  8. import { MarkDownItMarkDownService } from "../service/MarkDownItMarkDownService";
  9. import { JsonAPI } from "../singleton/JsonAPI";
  10. import { MessageCipherDTO } from "../dto/MessageCipherDTO";
  11. import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
  12. import { EncryptionService } from "../service/EncryptionService";
  13. import { ChatMessageDTO } from "../dto/ChatMessageDTO";
  14. import { fetchHandler } from "./FetchHandler";
  15. import { ChatViewDeps } from "./ChatViewDeps";
  16. export class ChatView implements Observer {
  17. private readonly _chatModel: ChatModel;
  18. private readonly _messageContainer: HTMLElement;
  19. // private readonly _messageSendTemplate = TemplateFactory.getTemplate('msg_container_send_template');
  20. // private readonly _messageReceiveTemplate = TemplateFactory.getTemplate('msg_container_template');
  21. // private readonly _markdownService: MarkDownService = new MarkDownItMarkDownService();
  22. // private readonly _encryptionService: EncryptionService = new SJCLEncryptionService();
  23. private readonly _messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
  24. private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
  25. private readonly _markdownService: MarkDownService;
  26. private readonly _encryptionService: EncryptionService;
  27. constructor(deps: ChatViewDeps) {
  28. this._messageContainer = deps.messageContainer;
  29. this._chatModel = deps.chatModel;
  30. this._messageSendTemplate = deps.messageSendTemplate;
  31. this._messageReceiveTemplate = deps.messageReceiveTemplate;
  32. this._markdownService = deps.markdownService;
  33. this._encryptionService = deps.encryptionService;
  34. this.addEventListeners();
  35. }
  36. update(data: ChatMessageViewModel[]): void {
  37. log.info('ChatView: updating view');
  38. // let html: string = "";
  39. this._messageContainer.innerHTML = "";
  40. data.forEach((vm: ChatMessageViewModel) => {
  41. const vmTemp = vm;
  42. vmTemp.message = this._markdownService.render(vm.message);
  43. /** Very Important!!!
  44. * Sanitizing HTML before displaying on webpage to prevent XSS attacks!!
  45. */
  46. let rendered;
  47. if (vmTemp.fromUser == JsonAPI.principleName) {
  48. rendered = DOMPurify.sanitize(this._messageSendTemplate(vmTemp));
  49. }
  50. else {
  51. rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
  52. }
  53. $(this._messageContainer).append(rendered);
  54. log.debug()
  55. // html += this._messageSendTemplate(vm);
  56. });
  57. // html = DOMPurify.sanitize(md.render(html));
  58. // this._element.innerHTML = html;
  59. // log.debug(this._element.innerHTML);
  60. }
  61. private addEventListeners(): void {
  62. this.addChatFormEL();
  63. }
  64. private addChatFormEL() {
  65. const chatForm = document.getElementById('chatMessageForm') as HTMLSelectElement;
  66. if (chatForm == null) {
  67. log.error("Chat form is null");
  68. }
  69. else {
  70. chatForm.addEventListener('submit', (e) => this.createChatMessageDTO(e, chatForm))
  71. }
  72. }
  73. private createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void {
  74. e.preventDefault();
  75. let contactName = JsonAPI.contactName;
  76. if(contactName == null)
  77. {
  78. log.error("Contact name is null");
  79. return;
  80. }
  81. if (!chatForm.checkValidity()) {
  82. console.log("error");
  83. chatForm.classList.add('was-validated');
  84. return;
  85. }
  86. chatForm.classList.add('was-validated');
  87. const chatInput = document.getElementById('chatInput') as HTMLInputElement;
  88. const passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
  89. if (chatInput.value == '' || chatInput.value == null) {
  90. log.error("Chat input is null.");
  91. return;
  92. }
  93. if (passphraseInput.value == '' || passphraseInput.value == null) {
  94. log.error("Chat input is null.");
  95. return;
  96. }
  97. // @ts-ignore
  98. const messageContent = chatInput.value;
  99. const context = { fromUser: JsonAPI.principleName, toUser: "", message: this._markdownService.render(messageContent), messageTime: new Date().toLocaleString() };
  100. // @ts-ignore
  101. const msgContainer: string = this._messageSendTemplate(context);
  102. $(this._messageContainer).append(DOMPurify.sanitize(msgContainer));
  103. // scrollChatAreaAnimated(2400);
  104. // let messageCipher = sjcl.encrypt(passphraseInput.value, messageContent, { mode: "gcm", ts: 128, adata: "", iter: iterations });
  105. let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(passphraseInput.value, messageContent)
  106. // let messageCipherJson = JSON.parse(messageCipher);
  107. let chatMessageDTO = {
  108. "fromUser": JsonAPI.principleName,
  109. "toUser": contactName,
  110. "messageCipher": messageCipher,
  111. // "messageTime": null
  112. }
  113. // @ts-ignore
  114. this.sendMessageAJAX(chatMessageDTO);
  115. }
  116. private sendMessageAJAX(chatMessageDTO: ChatMessageDTO): void {
  117. let headers = new Headers();
  118. // console.log("Token = " + btoa("hmm" + ":" + "hmm"))
  119. // headers.append('Accept','application/json')
  120. headers.append('Content-Type', 'application/json');
  121. // headers.append('Authorization', basicAuthToken);
  122. // @ts-ignore
  123. headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
  124. fetch(JsonAPI.MESSAGE_POST, {
  125. method: 'POST',
  126. headers: headers,
  127. body: JSON.stringify(chatMessageDTO)
  128. })
  129. .then(response => {
  130. log.debug(response);
  131. return response.clone();
  132. })
  133. .then(response => fetchHandler(response));
  134. }
  135. }