diff --git a/chatto/src/main/javascript/ts/src/model/UserModel.ts b/chatto/src/main/javascript/ts/src/model/UserModel.ts index 964e9d3..8d6403f 100644 --- a/chatto/src/main/javascript/ts/src/model/UserModel.ts +++ b/chatto/src/main/javascript/ts/src/model/UserModel.ts @@ -7,112 +7,104 @@ import { fetchErrorHandler } from "./FetchErrorHandler"; import { NotificationService } from "../service/NotificationService"; export class UserModel implements Subject { - /** - * @type {Observer[]} List of subscribers. In real life, the list of - * subscribers can be stored more comprehensively (categorized by event - * type, etc.). - */ - private readonly observers: Observer[] = []; + /** + * @type {Observer[]} List of subscribers. In real life, the list of + * subscribers can be stored more comprehensively (categorized by event + * type, etc.). + */ + private readonly observers: Observer[] = []; + private _activeUsersList: ActiveUserViewModel[] = Array(); + private readonly _notificationService: NotificationService; - private _activeUsersList: ActiveUserViewModel[]; - private readonly _notificationService: NotificationService; + constructor(notificationService: NotificationService) { + this._activeUsersList = []; + this._notificationService = notificationService; + } - constructor(notificationService: NotificationService) { - this._activeUsersList = []; - this._notificationService = notificationService; - } + /** + * Getter activeUsersList + * @return {ActiveUserViewModel[] } + */ + public get activeUsersList(): ActiveUserViewModel[] { + return this._activeUsersList; + } - /** - * Getter activeUsersList - * @return {ActiveUserViewModel[] } - */ - public get activeUsersList(): ActiveUserViewModel[] { - return this._activeUsersList; - } + /** + * The subscription management methods. + */ + public attach(observer: Observer): void { + log.info("Subject: Attached an observer."); + this.observers.push(observer); + } + public detach(observer: Observer): void { + const observerIndex = this.observers.indexOf(observer); + this.observers.splice(observerIndex, 1); + log.info("Subject: Detached an observer."); + } - /** - * The subscription management methods. - */ - public attach(observer: Observer): void { - log.info('Subject: Attached an observer.'); - this.observers.push(observer); + /** + * Trigger an update in each subscriber. + */ + public notify(): void { + log.info("Subject: Notifying observers..."); + for (const observer of this.observers) { + observer.update({ data: this._activeUsersList!, op: "" }); } + } - public detach(observer: Observer): void { - const observerIndex = this.observers.indexOf(observer); - this.observers.splice(observerIndex, 1); - log.info('Subject: Detached an observer.'); - } + public someBusinessMethod(activeuserList: ActiveUserViewModel[]): void { + this._activeUsersList = activeuserList; + this.helperMethod(); + log.info(`Subject: My state has just changed`); + log.trace(activeuserList); + this.notify(); + } - /** - * Trigger an update in each subscriber. - */ - public notify(): void { - log.info('Subject: Notifying observers...'); - for (const observer of this.observers) { - observer.update({ data: this._activeUsersList!, op: "" }); - } - } + /** + * getActiveUsers + */ + public getActiveUsers(): void { + if (JsonAPI.authToken != null) { + this._getActiveUsersAjax(JsonAPI.authToken).then((data) => { + // // activeUsers = data; + // sessionStorage.setItem('activeUsers', JSON.stringify(data)); + // log.trace(sessionStorage.getItem('activeUsers')); + log.info(`Subject: received ajax active users`); + data.map((d: any) => { + if (d.lastActive == null) return null; - public someBusinessMethod(activeuserList: ActiveUserViewModel[]): void { - this._activeUsersList = activeuserList; - this.helperMethod(); - log.info(`Subject: My state has just changed`); - log.trace(activeuserList); + d.lastActive = new Date(d.lastActive); + return d; + }); + this._activeUsersList = data; this.notify(); + }); + } else { + log.error("Auth token is null"); } + } - /** - * getActiveUsers - */ - public getActiveUsers(): void { - if (JsonAPI.authToken != null) { - this._getActiveUsersAjax(JsonAPI.authToken) - .then(data => { - // // activeUsers = data; - // sessionStorage.setItem('activeUsers', JSON.stringify(data)); - // log.trace(sessionStorage.getItem('activeUsers')); - log.info(`Subject: received ajax active users`); - data.map((d: any) => { - if( d.lastActive == null ) - return null; - - d.lastActive = new Date(d.lastActive) - return d; - }); - this._activeUsersList = data; - this.notify(); - }) - } - else { - log.error('Auth token is null'); - } + private async _getActiveUsersAjax(authToken: string): Promise { + let headers = new Headers(); + headers.append("X-AUTH-TOKEN", authToken); + let response = await fetch(JsonAPI.ACTIVE_USERS_GET, { + method: "GET", + headers: headers, + }); + log.debug(response.clone()); + if (fetchErrorHandler(response.clone(), this._notificationService)) { + return null; } + let data = await response.json(); + // return data; + return new Promise((resolve, reject) => { + if (data != null) { + resolve(data); + } else reject("Response data null"); + }); + } - private async _getActiveUsersAjax(authToken: string): Promise { - let headers = new Headers(); - headers.append('X-AUTH-TOKEN', authToken); - let response = await fetch(JsonAPI.ACTIVE_USERS_GET, { - method: 'GET', - headers: headers - }); - log.debug(response.clone()); - if (fetchErrorHandler(response.clone(), this._notificationService)) { - return null; - } - let data = await response.json(); - // return data; - return new Promise((resolve, reject) => { - if (data != null) { - resolve(data) - } - else - reject('Response data null') - }) - } - - private helperMethod() { } - -} \ No newline at end of file + private helperMethod() {} +} diff --git a/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts b/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts index 278852d..6c7189f 100644 --- a/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts +++ b/chatto/src/main/javascript/ts/src/service/AlertifyNotificationService.ts @@ -3,128 +3,83 @@ import { NotificationService } from "./NotificationService"; import * as alertify from "alertifyjs"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import log = require("loglevel"); -import bootbox = require("bootbox") +import bootbox = require("bootbox"); import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; export class AlertifyNotificationService implements NotificationService { + private readonly _alertify = alertify; + constructor() { + this._alertify.set("notifier", "position", "top-center"); + } + success(message: string): void { + this._alertify.success(message); + } + error(message: string): void { + this._alertify.error(message); + } + errorWithDelay(message: string, delay: number): void { + const _delay = this._alertify.get("notifier", "delay"); + this._alertify.set("notifier", "delay", delay); + this._alertify.error(message); + this._alertify.set("notifier", "delay", _delay); + } + warning(message: string): void { + this._alertify.warning(message); + } + message(message: string): void { + this._alertify.message(message); + } + passphrasePrompt( + vm: ActiveUserViewModel, + vms: ActiveUserViewModel[], + cb1: (v: ActiveUserViewModel, op: string) => ChatMessageViewModel[], + cb2: (...x: any) => any, + cb3: (...x: any) => any + ): void { + if (!vm.passphrase) { + bootbox.prompt({ + title: "Please enter the passphrase", + inputType: "password", + callback: async function (result) { + if (result) { + const valid = await cb3(result, vm.userName); + if (!valid) { + bootbox.alert("Some error occured. Please check your password"); + log.error("invalid password"); + return; + } + vm.unlocked = true; + vm.passphrase = result; + const chatMessages: ChatMessageViewModel[] = await cb1(vm, "new"); - private readonly _alertify = alertify; - constructor() { - this._alertify.set('notifier', 'position', 'top-center'); - } - success(message: string): void { - this._alertify.success(message) - } - error(message: string): void { - this._alertify.error(message); - } - errorWithDelay(message: string, delay: number): void { - const _delay = this._alertify.get('notifier', 'delay'); - this._alertify.set('notifier', 'delay', delay); - this._alertify.error(message); - this._alertify.set('notifier', 'delay', _delay); - } - warning(message: string): void { - this._alertify.warning(message); - } - message(message: string): void { - this._alertify.message(message); - } - passphrasePrompt(vm: ActiveUserViewModel, vms: ActiveUserViewModel[], cb1: (v: ActiveUserViewModel, op: string) => ChatMessageViewModel[], - cb2: (...x: any) => any, cb3: (...x: any) => any): void { - - // alertify.myprompt || alertify.dialog('myprompt', function () { - - // // document.getElementById('passphraseFormNew')!.addEventListener('submit', function (e: Event) { - // // e.preventDefault(); - // // log.debug(this.querySelectorAll('input')) - // // Array.from(this.querySelectorAll('input')).map((el) => { - // // const el2 = el as HTMLInputElement; - // // vm.passphrase = el2.value; - // // vm.unlocked = true; - // // cb1(vm.userName!, vm.passphrase, null, "new"); - // // cb2(); - // // alertify.myprompt().close(); - // // }) - // // log.debug(vm) - - // // }) - - // return { - // main: function (content: HTMLElement) { - // log.debug(vm) - - // // @ts-ignore - // this.setContent(content); - // }, - - // setup: function () { - // return { - // focus: { - // // @ts-ignore - // element: function () { - // // @ts-ignore - // return this.elements.body.querySelector(this.get('selector')); - // }, - // select: true - // }, - // options: { - // title: 'Enter Passphrase', - // basic: true, - // maximizable: false, - // resizable: false, - // padding: false - // } - // }; - // }, - // settings: { - // // @ts-ignore - // selector: undefined - // }, - // } - - - // }) - - // alertify.myprompt(document.getElementById('passphraseFormNew')).set('selector', 'input[type="password"]'); - - if (vm.passphrase == null) { - bootbox.prompt({ - title: "Please enter the passphrase", - inputType: 'password', - callback: async function (result) { - if (result) { - log.debug(result); - const valid = await cb3(result, vm.userName); - if (!valid) { - bootbox.alert("Some error occured. Please check your password"); - log.error("invalid password"); - return; - } - vm.unlocked = true - vm.passphrase = result; - const chatMessages: ChatMessageViewModel[] = await cb1(vm, "new"); - - vms.filter(v => v.userName == vm.userName).map(v => { - v.passphrase = result; - v.unlocked = true; - if (chatMessages.length != 0) { - v.lastMessageTime = new Date(chatMessages[chatMessages.length - 1].messageTime); - const lastMessageText = v.lastMessageText = chatMessages[chatMessages.length - 1].message; - if (lastMessageText.length > 15) { - v.lastMessageText = chatMessages[chatMessages.length - 1].message.slice(0, 15) + "..." - } - } - }) - // vm.lastMessageTime = new Date(chatMessages[chatMessages.length - 1].messageTime); - cb2(vm, vms); - log.debug(vm) - log.debug(vms) - } + vms + .filter((v) => v.userName == vm.userName) + .map((v) => { + v.passphrase = result; + v.unlocked = true; + if (chatMessages.length > 0) { + v.lastMessageTime = new Date( + chatMessages[chatMessages.length - 1].messageTime + ); + const lastMessageText = (v.lastMessageText = + chatMessages[chatMessages.length - 1].message); + if (lastMessageText.length > 15) { + v.lastMessageText = + chatMessages[chatMessages.length - 1].message.slice( + 0, + 15 + ) + "..."; + } } - }); - } - else { - cb1(vm, "new"); - } + }); + // vm.lastMessageTime = new Date(chatMessages[chatMessages.length - 1].messageTime); + cb2(vm, vms); + // log.debug(vm); + // log.debug(vms); + } + }, + }); + } else { + cb1(vm, "new"); } + } } diff --git a/chatto/src/main/javascript/ts/src/view/ChatView.ts b/chatto/src/main/javascript/ts/src/view/ChatView.ts index 184490b..1521490 100644 --- a/chatto/src/main/javascript/ts/src/view/ChatView.ts +++ b/chatto/src/main/javascript/ts/src/view/ChatView.ts @@ -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 { - private readonly _chatModel: ChatModel; - private readonly _messageContainer: HTMLElement; - private readonly _messageSendTemplate: Handlebars.TemplateDelegate; - private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate; - 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): 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): 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; } -} \ No newline at end of file + + // 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 + ); + } + }); + } + }); + } +} diff --git a/chatto/src/main/javascript/ts/src/view/UserView.ts b/chatto/src/main/javascript/ts/src/view/UserView.ts index 0b5995a..0aa6f02 100644 --- a/chatto/src/main/javascript/ts/src/view/UserView.ts +++ b/chatto/src/main/javascript/ts/src/view/UserView.ts @@ -11,174 +11,184 @@ import { JsonAPI } from "../singleton/JsonAPI"; import { NotificationService } from "../service/NotificationService"; export class UserView implements Observer { - - - private readonly _model: UserModel; - private readonly _chatModel: ChatModel; - private readonly _usersListElement: HTMLElement; - private readonly _userSearchInputElement: HTMLInputElement; - private readonly _userSearchButton: HTMLElement; - private readonly _userSearchCancelButton: HTMLElement; - private readonly _searchService: SearchService; - private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate; - private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate; - private readonly _notificationService: NotificationService; - private _newMessagesLoop: any; - - constructor(deps: UserViewDeps) { - this._model = deps.model; - this._chatModel = deps.chatModel; - this._usersListElement = deps.usersListElement; - this._userSearchInputElement = deps.userSearchInputElement; - this._userSearchButton = deps.userSearchButton; - this._userSearchCancelButton = deps.userSearchCancelButton; - this._searchService = deps.searchService; - this._userContactOnlineTemplate = deps.userContactOnlineTemplate; - this._userContactOfflineTemplate = deps.userContactOfflineTemplate; - this._notificationService = deps.notificationService; - - this._addSearchEventListeners(); + private readonly _model: UserModel; + private readonly _chatModel: ChatModel; + private readonly _usersListElement: HTMLElement; + private readonly _userSearchInputElement: HTMLInputElement; + private readonly _userSearchButton: HTMLElement; + private readonly _userSearchCancelButton: HTMLElement; + private readonly _searchService: SearchService; + private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate< + ActiveUserViewModel + >; + private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate< + ActiveUserViewModel + >; + private readonly _notificationService: NotificationService; + private _newMessagesLoop: any; + + constructor(deps: UserViewDeps) { + this._model = deps.model; + this._chatModel = deps.chatModel; + this._usersListElement = deps.usersListElement; + this._userSearchInputElement = deps.userSearchInputElement; + this._userSearchButton = deps.userSearchButton; + this._userSearchCancelButton = deps.userSearchCancelButton; + this._searchService = deps.searchService; + this._userContactOnlineTemplate = deps.userContactOnlineTemplate; + this._userContactOfflineTemplate = deps.userContactOfflineTemplate; + this._notificationService = deps.notificationService; + + this._addSearchEventListeners(); + } + + update(d: ObserverData): void { + let html: string = ""; + d.data.forEach((element: ActiveUserViewModel) => { + element.online + ? (html += this._userContactOnlineTemplate(element)) + : (html += this._userContactOfflineTemplate(element)); + }); + $(this._usersListElement).html(DOMPurify.sanitize(html)); + this._addUserCallBacks(); + } + + private _addSearchEventListeners(): void { + this._addSearchButtonEL(); + this._addSearchCancelEL(); + this._addSearchInputEL(); + } + + private _addUserCallBacks(): void { + let userBoxes = document.getElementsByClassName("user-box"); + + Array.from(userBoxes).forEach((ub: Element) => + ub.addEventListener("click", this._userCallBack.bind(this, ub)) + ); + } + + private _userCallBack(el: Element): void { + this._chatModel.clear(); + clearInterval(this._newMessagesLoop); + let current = document.getElementsByClassName("user-box active"); + + let passphrase: string = ""; + if (current.length > 0) { + // let passphraseInput = document.getElementById('passphrase') as any; + + // if (passphraseInput == null) { + // log.error('passphraseInput element reference is null'); + // return; + // } + // passphrase = passphraseInput.value + // if (passphrase == '' || passphrase == null) { + // // alert('Please input passphrase') + // // alertify.error('Please enter a passphrase'); + // log.error('passphrase is empty or null'); + // return; + // } + current[0].className = current[0].className.replace(" active", ""); } - - update(d: ObserverData): void { - let html: string = ""; - d.data.forEach((element: ActiveUserViewModel) => { - element.online ? html += this._userContactOnlineTemplate(element) : - html += this._userContactOfflineTemplate(element); - }); - $(this._usersListElement).html(DOMPurify.sanitize(html)); - this._addUserCallBacks(); + // Add the active class to the current/clicked button + else if (current.length == 0) { + let elem = document.getElementById("passphrase-initial") as any; + if (elem == null) { + log.error("passphraseInput element reference is null"); + return; + } + // passphrase = elem.value; + // if (passphrase == '' || passphrase == null) { + // // // alert('Please input passphrase') + // // // alertify.error('Please enter a passphrase'); + // log.error('passphrase is empty or null'); + // return; + // } + // @ts-ignore: Object is possibly 'null'. + document.getElementById("no-user-selected").hidden = true; + // @ts-ignore: Object is possibly 'null'. + document.getElementById("chat-card").hidden = false; + // @ts-ignore: Object is possibly 'null'. + elem.hidden = true; } - - private _addSearchEventListeners(): void { - this._addSearchButtonEL(); - this._addSearchCancelEL(); - this._addSearchInputEL(); + // console.log(this.getElementsByClassName('to-user-span')); + let elem = el.getElementsByClassName("to-user-span")[0] as HTMLElement; + let userName = elem.innerText; + JsonAPI.contactName = userName; + // @ts-ignore: Object is possibly 'null'. + document.getElementById("user-name-span").innerText = userName; + let vm = this._model.activeUsersList.find((vm) => vm.userName === userName); + if (!vm) { + vm = new ActiveUserViewModel(); + vm.userName = userName; } - - private _addUserCallBacks(): void { - let userBoxes = document.getElementsByClassName('user-box'); - - Array.from(userBoxes).forEach((ub: Element) => - ub.addEventListener('click', this._userCallBack.bind(this, ub))) + this._notificationService.passphrasePrompt( + vm, + this._model.activeUsersList, + this._chatModel.getMessages.bind(this._chatModel), + this._promptHandler.bind(this), + this._chatModel.isPassphraseValid.bind(this._chatModel) + ); + // this._chatModel.getMessages(userName, vm.passphrase, null, "new"); + el.className += " active"; + log.debug("loop", this._newMessagesLoop); + if (vm.unlocked && vm.lastMessageTime) { + this._newMessagesLoop = setInterval( + this._chatModel.getMessages.bind(this._chatModel, vm, "update"), + 10_000 + ); + this._model.notify(); } - - - private _userCallBack(el: Element): void { - this._chatModel.clear(); - clearInterval(this._newMessagesLoop); - let current = document.getElementsByClassName('user-box active'); - - let passphrase: string = ''; - if (current.length > 0) { - // let passphraseInput = document.getElementById('passphrase') as any; - - // if (passphraseInput == null) { - // log.error('passphraseInput element reference is null'); - // return; - // } - // passphrase = passphraseInput.value - // if (passphrase == '' || passphrase == null) { - // // alert('Please input passphrase') - // // alertify.error('Please enter a passphrase'); - // log.error('passphrase is empty or null'); - // return; - // } - current[0].className = current[0].className.replace(" active", ""); - - } - // Add the active class to the current/clicked button - else if (current.length == 0) { - let elem = document.getElementById('passphrase-initial') as any; - if (elem == null) { - log.error('passphraseInput element reference is null'); - return; - } - // passphrase = elem.value; - // if (passphrase == '' || passphrase == null) { - // // // alert('Please input passphrase') - // // // alertify.error('Please enter a passphrase'); - // log.error('passphrase is empty or null'); - // return; - // } - // @ts-ignore: Object is possibly 'null'. - document.getElementById('no-user-selected').hidden = true; - // @ts-ignore: Object is possibly 'null'. - document.getElementById('chat-card').hidden = false; - // @ts-ignore: Object is possibly 'null'. - elem.hidden = true; - } - // console.log(this.getElementsByClassName('to-user-span')); - let elem = el.getElementsByClassName('to-user-span')[0] as HTMLElement; - let userName = elem.innerText; - JsonAPI.contactName = userName; - // @ts-ignore: Object is possibly 'null'. - document.getElementById('user-name-span').innerText = userName; - let vm = this._model.activeUsersList.find(vm => vm.userName === userName); - if (!vm) { - vm = new ActiveUserViewModel(); - vm.userName = userName; - } - this._notificationService.passphrasePrompt(vm, this._model.activeUsersList, - this._chatModel.getMessages.bind(this._chatModel), - this._promptHandler.bind(this), this._chatModel.isPassphraseValid.bind(this._chatModel)); - // this._chatModel.getMessages(userName, vm.passphrase, null, "new"); - el.className += " active"; - log.debug("loop", this._newMessagesLoop) - if (vm.unlocked && vm.lastMessageTime != null) { - this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel, - vm, "update"), 10_000); - } - } - - private _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) { - // vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName) - log.debug(vms) - - if (vm.lastMessageTime != null) { - this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel, - vm, "update"), 10_000); - } - this._model.notify(); - } - - private _addSearchButtonEL() { - this._userSearchButton.addEventListener('submit', (e) => { - e.preventDefault(); - // log.trace(temp); - const searchTerm = this._userSearchInputElement.value; - log.debug("search term value = " + searchTerm); - const list = this._model.activeUsersList; - log.debug("active users"); - log.debug(list); - if (list == null) { - log.error("Users list is null"); - return; - } - let searchResult = this._searchService.search(list, searchTerm); - this.update({ data: searchResult, op: "" }); - log.debug(searchResult); - }) + } + + private _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) { + // vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName) + // log.debug(vms); + + if (vm.lastMessageTime) { + this._newMessagesLoop = setInterval( + this._chatModel.getMessages.bind(this._chatModel, vm, "update"), + 10_000 + ); + this._model.notify(); } - - private _addSearchInputEL() { - this._userSearchInputElement.addEventListener('input', (e) => { - e.preventDefault(); - if (this._userSearchInputElement.value.length < 2) { - log.debug("inputted") - this._userSearchCancelButton.hidden = false; - } - }) - } - - private _addSearchCancelEL() { - this._userSearchCancelButton.addEventListener('click', (e) => { - e.preventDefault(); - this._userSearchInputElement.value = ""; - this._userSearchCancelButton.hidden = true; - let list = this._model.activeUsersList; - this.update({ data: list, op: "" }) - }) - } - -} \ No newline at end of file + } + + private _addSearchButtonEL() { + this._userSearchButton.addEventListener("submit", (e) => { + e.preventDefault(); + // log.trace(temp); + const searchTerm = this._userSearchInputElement.value; + log.debug("search term value = " + searchTerm); + const list = this._model.activeUsersList; + log.debug("active users"); + log.debug(list); + if (list == null) { + log.error("Users list is null"); + return; + } + let searchResult = this._searchService.search(list, searchTerm); + this.update({ data: searchResult, op: "" }); + log.debug(searchResult); + }); + } + + private _addSearchInputEL() { + this._userSearchInputElement.addEventListener("input", (e) => { + e.preventDefault(); + if (this._userSearchInputElement.value.length < 2) { + log.debug("inputted"); + this._userSearchCancelButton.hidden = false; + } + }); + } + + private _addSearchCancelEL() { + this._userSearchCancelButton.addEventListener("click", (e) => { + e.preventDefault(); + this._userSearchInputElement.value = ""; + this._userSearchCancelButton.hidden = true; + let list = this._model.activeUsersList; + this.update({ data: list, op: "" }); + }); + } +}