Chat area is cleared when changing user

Also frontend code has een refactored significantly
This commit is contained in:
Rohan Sircar 2020-01-27 21:43:38 +05:30
parent 52af8f39ba
commit f34a5524fd
17 changed files with 310 additions and 318 deletions

View File

@ -9,7 +9,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Configuration @Configuration

View File

@ -3,7 +3,6 @@ package org.ros.chatto.service;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;

View File

@ -45,6 +45,6 @@
"alertifyjs": "global:alertify" "alertifyjs": "global:alertify"
}, },
"scripts": { "scripts": {
"watch": "watchify ts/src/main.ts -p [ tsify --target es2017 --noImplicitAny ] --debug -o ../resources/static/js/bundle.js" "watch": "watchify ts/src/main.ts -p [ tsify --target ES6 --noImplicitAny ] --debug -o ../resources/static/js/bundle.js"
} }
} }

View File

@ -1,51 +1,58 @@
import { Controller } from "./controller/AbstractController";
import { UserModel } from "./model/UserModel"
import { Model } from "./model/AbstractModel";
import { View } from "./view/AbstractView";
import { UserView } from "./view/UserView";
import { UserController } from "./controller/UserController";
import { ModelFactory } from "./model/ModelFactory";
import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel";
import { ChatMessageViewModel } from "./viewmodel/ChatMessageViewModel";
import * as Handlebars from "handlebars";
import { ChatModel } from "./model/ChatModel";
import { ChatView } from "./view/ChatView";
import { ChatController } from "./controller/ChatController";
import { JsonAPI } from "./singleton/JsonAPI";
import * as log from 'loglevel';
import { EncryptionService } from "./service/EncryptionService";
import { SJCLEncryptionService } from "./service/SJCLEncryptionService";
import { MessageCipherDTO } from "./dto/MessageCipherDTO";
import { SearchService } from "./service/SearchService";
import { FuseSearchService } from "./service/FuseSearchService";
import { ChatMessageDTO } from "./dto/ChatMessageDTO";
import { NotificationService } from "./service/NotificationService";
import { AlertifyNotificationService } from "./service/AlertifyNotificationService";
import { Builder } from "builder-pattern"; import { Builder } from "builder-pattern";
import * as Handlebars from "handlebars";
// import "./SprintfTest.d.ts" import * as log from 'loglevel';
// import { sprintf } from "sprintf-js"; import { ChatController } from "./controller/ChatController";
// import sprintf = require('sprintf-js'); import { UserController } from "./controller/UserController";
import { TemplateFactory } from "./template/TemplateFactory"; import { ChatModel } from "./model/ChatModel";
import { UserViewDeps } from "./view/UserViewDeps"; import { ChatModelHelper } from "./model/ChatModelHelper";
import { ChatViewDeps } from "./view/ChatViewDeps"; import { UserModel } from "./model/UserModel";
import { MarkDownItMarkDownService } from "./service/MarkDownItMarkDownService"; import { AlertifyNotificationService } from "./service/AlertifyNotificationService";
import { Sprintf } from "./singleton/Sprintf";
import { EncryptionServiceFactory } from "./service/EncryptionServiceFactory"; import { EncryptionServiceFactory } from "./service/EncryptionServiceFactory";
import { FuseSearchService } from "./service/FuseSearchService";
import { MarkDownItMarkDownService } from "./service/MarkDownItMarkDownService";
import { NotificationService } from "./service/NotificationService";
import { SearchService } from "./service/SearchService";
import { TemplateFactory } from "./template/TemplateFactory";
import { ChatView } from "./view/ChatView";
import { ChatViewDeps } from "./view/ChatViewDeps";
import { UserView } from "./view/UserView";
import { UserViewDeps } from "./view/UserViewDeps";
import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel";
log.setLevel("TRACE");
const usersListElement = document.getElementById('contacts-box'); const usersListElement = document.getElementById('contacts-box');
const userSearchButton = document.getElementById('user-search'); const userSearchButton = document.getElementById('user-search');
const userSearchInputElement = document.getElementById('user-search-term') as HTMLInputElement; const userSearchInputElement = document.getElementById('user-search-term') as HTMLInputElement;
const userSearchCancelButton = document.getElementById('user-search-cancel'); const userSearchCancelButton = document.getElementById('user-search-cancel');
const chatArea = document.getElementById('chat-area-new');
const activeUserSearchService: SearchService<ActiveUserViewModel> = new FuseSearchService(["userName"]); const activeUserSearchService: SearchService<ActiveUserViewModel> = new FuseSearchService(["userName"]);
const ns: NotificationService = new AlertifyNotificationService();
const encryptionService = EncryptionServiceFactory.getEncryptionService();
log.setLevel("TRACE")
const chatModel = new ChatModel();
const userModel = new UserModel(); const chatModelHelper = new ChatModelHelper(encryptionService, ns);
// const userModel = ModelFactory.createModel("USER"); const chatModel = new ChatModel(chatModelHelper);
const cvDeps: ChatViewDeps = {
chatModel: chatModel,
// @ts-ignore: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.
messageContainer: chatArea,
messageSendTemplate: TemplateFactory.getTemplate('msg_container_send_template'),
messageReceiveTemplate: TemplateFactory.getTemplate('msg_container_template'),
markdownService: new MarkDownItMarkDownService,
encryptionService: encryptionService,
notificationService: ns
}
const chatView = new ChatView(cvDeps);
chatModel.attach(chatView);
const chatController = new ChatController(chatModel, chatView);
const userModel = new UserModel(ns);
const uvDeps: UserViewDeps = { const uvDeps: UserViewDeps = {
model: userModel, model: userModel,
chatModel: chatModel, chatModel: chatModel,
@ -61,82 +68,21 @@ const uvDeps: UserViewDeps = {
userContactOfflineTemplate: TemplateFactory.getTemplate('user-contact-offline-template') userContactOfflineTemplate: TemplateFactory.getTemplate('user-contact-offline-template')
} }
const userView = new UserView(uvDeps); const userView = new UserView(uvDeps);
// console.log(userBox);
userModel.attach(userView); userModel.attach(userView);
// userView.model
const userController = new UserController(userModel, userView); const userController = new UserController(userModel, userView);
// userController.test();
// userModel.someBusinessMethod(activeUsersMock);
log.info("hello");
const chatArea = document.getElementById('chat-area-new');
// @ts-ignore: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.
const cvDeps: ChatViewDeps = {
chatModel: chatModel,
// @ts-ignore: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.
messageContainer: chatArea,
messageSendTemplate: TemplateFactory.getTemplate('msg_container_send_template'),
messageReceiveTemplate: TemplateFactory.getTemplate('msg_container_template'),
markdownService: new MarkDownItMarkDownService,
encryptionService: EncryptionServiceFactory.getEncryptionService()
}
const chatView = new ChatView(cvDeps);
chatModel.attach(chatView);
const chatController = new ChatController(chatModel, chatView);
function someFunc(vm: ActiveUserViewModel): void {
// log.info(vm);
// logger.info(vm)
}
userController.getActiveUsers(); userController.getActiveUsers();
log.info("test");
// someFunc(activeUserViewModelMock);
// @ts-ignore: Object is possibly 'null'.
var source = document.getElementById("msg_container_template").innerHTML;
var msgContainerTemplate = Handlebars.compile(source);
JsonAPI.ACTIVE_USERS_GET + 'aef';
const encryptionService: EncryptionService = EncryptionServiceFactory.getEncryptionService();
let messageCipherDTO: MessageCipherDTO = encryptionService.encrypt("password", "data");
console.log(encryptionService.decrypt("password", messageCipherDTO));
async function func(): Promise<void> {
const text = await encryptionService.decryptAsPromise("password", messageCipherDTO)
log.debug(text);
}
func();
Handlebars.registerHelper('avatar', function () { Handlebars.registerHelper('avatar', function () {
return '<div class="img_cont_msg"> <img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg"> </div>'; return '<div class="img_cont_msg"> <img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg"> </div>';
}); });
const testList: ChatMessageDTO[] = [];
// @ts-ignore
console.log()
log.info(Sprintf("test sprintf"))
// log.info(sprintf2.sprintf("test sprintf"))
const ns: NotificationService = new AlertifyNotificationService();
ns.success("Welcome"); ns.success("Welcome");
// ns.errorWithDelay("Hmm very long error notif", 10); // ns.errorWithDelay("Hmm very long error notif", 10);
const ss = FuseSearchService.getInstance<ActiveUserViewModel>([""]);
const test = Builder<UserViewDeps>().build(); const test = Builder<UserViewDeps>().build();

View File

@ -1,7 +1,7 @@
import { Subject } from "../observe/Observable"; import { Subject } from "../observe/Observable";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
export interface Model extends Subject{ export interface Model extends Subject<any> {
someBusinessMethod(data: Object): void; someBusinessMethod(data: Object): void;
} }

View File

@ -1,114 +1,126 @@
import { Subject } from "../observe/Observable"; import { Subject } from "../observe/Observable";
import { Model } from "./AbstractModel";
import { Observer } from "../observe/Observer"; import { Observer } from "../observe/Observer";
import { fetchErrorHandler } from "./FetchErrorHandler";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { JsonAPI } from "../singleton/JsonAPI"; import { JsonAPI } from "../singleton/JsonAPI";
import log = require('loglevel'); import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { EncryptionService } from "../service/EncryptionService";
import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import { ChatModelHelper } from "./ChatModelHelper"; import { ChatModelHelper } from "./ChatModelHelper";
import log = require('loglevel');
import { ObserverData } from "../observe/ObserverData";
export class ChatModel implements Subject { interface Params {
userName: string,
data: ChatMessageViewModel[],
op: string
}
export class ChatModel implements Subject<ChatMessageViewModel> {
/** /**
* @type {Observer[]} List of subscribers. In real life, the list of * @type {Observer[]} List of subscribers. In real life, the list of
* subscribers can be stored more comprehensively (categorized by event * subscribers can be stored more comprehensively (categorized by event
* type, etc.). * type, etc.).
*/ */
private readonly _observers: Observer[] = []; private readonly _observers: Observer<ChatMessageViewModel>[] = [];
private state: ChatMessageViewModel[] | null;
private readonly _messagePageMap: Map<string, number>; private readonly _messagePageMap: Map<string, number>;
private readonly _messagesMap: Map<string, ChatMessageViewModel[]>; private readonly _messagesMap: Map<string, ChatMessageViewModel[]>;
private readonly _chatModelHelper: ChatModelHelper;
constructor() { constructor(chatModelHelper: ChatModelHelper) {
this.state = null;
this._messagePageMap = new Map(); this._messagePageMap = new Map();
this._messagesMap = new Map(); this._messagesMap = new Map();
this._chatModelHelper = chatModelHelper;
} }
/** /**
* The subscription management methods. * The subscription management methods.
*/ */
public attach(observer: Observer): void { public attach(observer: Observer<ChatMessageViewModel>): void {
console.log('Subject: Attached an observer.'); log.info('Subject: Attached an observer.');
this._observers.push(observer); this._observers.push(observer);
} }
public detach(observer: Observer): void { public detach(observer: Observer<ChatMessageViewModel>): void {
const observerIndex = this._observers.indexOf(observer); const observerIndex = this._observers.indexOf(observer);
this._observers.splice(observerIndex, 1); this._observers.splice(observerIndex, 1);
console.log('Subject: Detached an observer.'); log.info('Subject: Detached an observer.');
}
private storeUserMessages(username: string, messages: ChatMessageViewModel[], op: string) {
switch (op) {
case "clear": this._messagesMap.set(username, []);
case "page": this._messagesMap.set(username, messages.concat(this.getStoredUserMessages(username))); break;
// case "page": this._messagesMap.set(username, messages);
case "new": this._messagesMap.set(username, this.getStoredUserMessages(username).concat(messages)); break;
default: new Error("Invalid option");
} }
private storeUserMessages(username: string, messages: ChatMessageViewModel[]) {
this._messagesMap.set(username, messages);
} }
private getStoredUserMessages(username: string): ChatMessageViewModel[] { private getStoredUserMessages(username: string): ChatMessageViewModel[] {
return this._messagesMap.get(username)!; let temp = this._messagesMap.get(username);
if (temp == null)
return [];
else {
return temp;
}
} }
/** /**
* Trigger an update in each subscriber. * Trigger an update in each subscriber.
*/ */
public notify(userName: string): void { public notify(p: Params): void {
console.log('Subject: Notifying observers...'); log.info('Subject: Notifying observers...');
switch (p.op) {
case "clear": {
const od: ObserverData<ChatMessageViewModel> = { data: [], op: "clear" }
for (const observer of this._observers) { for (const observer of this._observers) {
observer.update(this._messagesMap.get(userName)); observer.update(od);
}
} break;
case "new": {
const od: ObserverData<ChatMessageViewModel> = { data: p.data, op: p.op }
for (const observer of this._observers) {
observer.update(od);
}
} break;
case "page": {
const od: ObserverData<ChatMessageViewModel> = { data: p.data, op: p.op }
for (const observer of this._observers) {
observer.update(od);
}
} break;
default: { log.error("error") }
} }
} }
public someBusinessMethod(chatMessageList: ChatMessageViewModel[]): void { public someBusinessMethod(chatMessageList: ChatMessageViewModel[]): void {
this.state = chatMessageList;
console.log(`Subject: My state has just changed`);
console.log(chatMessageList);
this.notify("some user");
} }
public async getMessages(contactName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> { public clear(): void {
this._messagePageMap.set(JsonAPI.contactName!, 0);
this.storeUserMessages(JsonAPI.contactName!, [], "clear");
this.notify({ userName: "", data: [], op: "clear" })
}
public async getMessages(contactName: string, passphrase: string, lastMessageTime: string | null, op: string): Promise<ChatMessageViewModel[]> {
if (this._messagePageMap.get(contactName) == null) if (this._messagePageMap.get(contactName) == null)
this._messagePageMap.set(contactName, 0); this._messagePageMap.set(contactName, 0);
// else {
// log.debug('page number before = ' + this._messagePageMap.get(contactName)!)
// this._messagePageMap.set(contactName, this._messagePageMap.get(contactName)! + 1);
// log.debug('page number after = ' + this._messagePageMap.get(contactName)!)
// }
const pageNumber = this._messagePageMap.get(contactName) const pageNumber = this._messagePageMap.get(contactName)
const cVMs = await ChatModelHelper.getMessages(contactName, passphrase, pageNumber!, lastMessageTime); const cVMs = await this._chatModelHelper.getMessages(contactName, passphrase, pageNumber!, lastMessageTime, op);
if (cVMs != null) { if (cVMs != null) {
log.info('Subject: My state has just changed') log.info('Subject: My state has just changed')
// this._messagesMap.set(userName, cVMs);
const existingMessages = this.getStoredUserMessages(contactName); const existingMessages = this.getStoredUserMessages(contactName);
log.debug('existing message:')
log.debug(existingMessages); log.debug(existingMessages);
log.debug('new messages:')
log.debug(cVMs); log.debug(cVMs);
if (existingMessages != null) { this.storeUserMessages(contactName, cVMs, op);
// existingMessages.forEach(function (elem) { this.notify({ userName: contactName, data: cVMs, op: op });
// cVMs.push(elem);
// })
const newArr = cVMs.concat(existingMessages)
// log.debug(newArr);
this.storeUserMessages(contactName, cVMs);
// this.storeUserMessages(contactName, cVMs);
}
else {
this.storeUserMessages(contactName, cVMs);
}
JsonAPI.contactName = contactName;
this.notify(contactName);
} }
else { else {
log.error('Messages were null'); log.error('Messages were null');
} }
if(this._messagePageMap.get(contactName) == 0)
{
log.info(cVMs[cVMs.length - 1].messageTime)
}
if (cVMs.length != 0) { if (cVMs.length != 0) {
// log.debug('page number before = ' + this._messagePageMap.get(contactName)!)
this._messagePageMap.set(contactName, this._messagePageMap.get(contactName)! + 1); this._messagePageMap.set(contactName, this._messagePageMap.get(contactName)! + 1);
// log.debug('page number after = ' + this._messagePageMap.get(contactName)!)
} }
return cVMs; return cVMs;

View File

@ -1,30 +1,38 @@
import * as log from "loglevel"; import * as log from "loglevel";
import { ChatMessageDTO } from "../dto/ChatMessageDTO"; import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import { EncryptionService } from "../service/EncryptionService"; import { EncryptionService } from "../service/EncryptionService";
import { EncryptionServiceFactory } from "../service/EncryptionServiceFactory"; import { NotificationService } from "../service/NotificationService";
import { JsonAPI } from "../singleton/JsonAPI"; import { JsonAPI } from "../singleton/JsonAPI";
import { Sprintf } from "../singleton/Sprintf"; import { Sprintf } from "../singleton/Sprintf";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { fetchErrorHandler } from "./FetchErrorHandler"; import { fetchErrorHandler } from "./FetchErrorHandler";
export class ChatModelHelper { export class ChatModelHelper {
private static readonly _encryptionService: EncryptionService = EncryptionServiceFactory.getEncryptionService(); private readonly _encryptionService: EncryptionService;
private readonly _notificationService: NotificationService;
public static async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
constructor(encryptionService: EncryptionService, notificationService: NotificationService) {
this._encryptionService = encryptionService;
this._notificationService = notificationService;
}
public async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: string | null, op: string): Promise<ChatMessageViewModel[]> {
switch (lastMessageTime) { switch (lastMessageTime) {
case null: { case null: {
const data: ChatMessageDTO[] = await this.getPaginatedMessagesAjax(userName, page!); const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(userName, page!);
const cVMs = Promise.all(data.map(vm => this.toChatMessageVMAsync(vm, passphrase)).reverse()); const cVMs = Promise.all(data.map(vm => this._toChatMessageVMAsync(vm, passphrase)).reverse());
return cVMs; return cVMs;
} }
default: { default: {
const data: ChatMessageDTO[] = await this.getNewMessagesAjax(userName, lastMessageTime); const data: ChatMessageDTO[] = await this._getNewMessagesAjax(userName, lastMessageTime);
return data.map(vm => this.toChatMessageVM(vm, passphrase)); return data.map(vm => this._toChatMessageVM(vm, passphrase));
} }
} }
} }
private static async toChatMessageVMAsync(chatMessageDTO: ChatMessageDTO, passphrase: string): Promise<ChatMessageViewModel> { private async _toChatMessageVMAsync(chatMessageDTO: ChatMessageDTO, passphrase: string): Promise<ChatMessageViewModel> {
const vm = new ChatMessageViewModel(); const vm = new ChatMessageViewModel();
vm.fromUser = chatMessageDTO.fromUser; vm.fromUser = chatMessageDTO.fromUser;
vm.toUser = chatMessageDTO.toUser; vm.toUser = chatMessageDTO.toUser;
@ -34,7 +42,7 @@ export class ChatModelHelper {
return vm; return vm;
} }
private static toChatMessageVM(chatMessageDTO: ChatMessageDTO, passphrase: string): ChatMessageViewModel { private _toChatMessageVM(chatMessageDTO: ChatMessageDTO, passphrase: string): ChatMessageViewModel {
const vm = new ChatMessageViewModel(); const vm = new ChatMessageViewModel();
vm.fromUser = chatMessageDTO.fromUser; vm.fromUser = chatMessageDTO.fromUser;
vm.toUser = chatMessageDTO.toUser; vm.toUser = chatMessageDTO.toUser;
@ -44,7 +52,7 @@ export class ChatModelHelper {
return vm; return vm;
} }
private static async getAllMessagesAjax(toUser: string): Promise<any> { private async _getAllMessagesAjax(toUser: string): Promise<any> {
const headers = new Headers(); const headers = new Headers();
if (JsonAPI.authToken == null) { if (JsonAPI.authToken == null) {
log.error("authToken null"); log.error("authToken null");
@ -52,51 +60,41 @@ export class ChatModelHelper {
}; };
headers.append('X-AUTH-TOKEN', JsonAPI.authToken); headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser); const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
// const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser,1,5);
log.debug(url) log.debug(url)
// const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
// method: 'GET',
// headers: headers
// });
const response = await fetch(url, { const response = await fetch(url, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
console.log(response.clone()); log.debug(response.clone());
if (fetchErrorHandler(response.clone())) { if (fetchErrorHandler(response.clone(), this._notificationService)) {
return null; return null;
} }
const data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
return data; return data;
} }
private static async getPaginatedMessagesAjax(toUser: string, page: number): Promise<any> { private async _getPaginatedMessagesAjax(toUser: string, page: number): Promise<any> {
const headers = new Headers(); const headers = new Headers();
if (JsonAPI.authToken == null) { if (JsonAPI.authToken == null) {
log.error("authToken null"); log.error("authToken null");
return; return;
}; };
headers.append('X-AUTH-TOKEN', JsonAPI.authToken); headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
// const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, 5); const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, 5);
log.debug(url) log.debug(url)
// const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
// method: 'GET',
// headers: headers
// });
const response = await fetch(url, { const response = await fetch(url, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
console.log(response.clone()); log.debug(response.clone());
if (fetchErrorHandler(response.clone())) { if (fetchErrorHandler(response.clone(), this._notificationService)) {
return null; return null;
} }
const data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
return data; return data;
} }
private static async getNewMessagesAjax(toUser: string, lastMessageTimeStamp: string): Promise<any> { private async _getNewMessagesAjax(toUser: string, lastMessageTimeStamp: string): Promise<any> {
const headers = new Headers(); const headers = new Headers();
if (JsonAPI.authToken == null) { if (JsonAPI.authToken == null) {
log.error("authToken null"); log.error("authToken null");
@ -107,8 +105,8 @@ export class ChatModelHelper {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
console.log(response.clone()); log.debug(response.clone());
if (fetchErrorHandler(response.clone())) { if (fetchErrorHandler(response.clone(), this._notificationService)) {
return null; return null;
} }
const data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();

View File

@ -1,17 +1,19 @@
import log = require("loglevel"); import log = require("loglevel");
import { NotificationService } from "../service/NotificationService";
import { Sprintf } from "../singleton/Sprintf";
// import { sprintf } from "sprintf-js"; // import { sprintf } from "sprintf-js";
///<reference path="../SprintfTest.d.ts" /> ///<reference path="../SprintfTest.d.ts" />
// import sprintf = require('sprintf-js').sprintf; // import sprintf = require('sprintf-js').sprintf;
export function fetchErrorHandler(response: Response) { export function fetchErrorHandler(response: Response, ns: NotificationService) {
// alertify.success('Current position : ' + alertify.get('notifier', 'position')); // alertify.success('Current position : ' + alertify.get('notifier', 'position'));
if (!response.ok) { if (!response.ok) {
return response.text().catch(err => { return response.text().catch(err => {
// the status was not ok and there is no json body // the status was not ok and there is no json body
// throw new Error(response.statusText); // throw new Error(response.statusText);
// window.alert(sprintf('Some error occured. Http code is %s', response.status)); // window.alert(sprintf('Some error occured. Http code is %s', response.status));
// alertify.error(sprintf('Some error occured. Http code is %s', response.status)); ns.error(Sprintf('Some error occured. Http code is %s', response.status));
// @ts-ignore // @ts-ignore
log.error(sprintf('Some error occured. Http code is %s', response.status)); log.error(sprintf('Some error occured. Http code is %s', response.status));
log.error(); log.error();
@ -20,7 +22,7 @@ export function fetchErrorHandler(response: Response) {
// the status was not ok but there is a json body // the status was not ok but there is a json body
// throw new Error(json.error.message); // example error message returned by a REST API // throw new Error(json.error.message); // example error message returned by a REST API
// window.alert(sprintf('Error: %s (Http code %s)', json, response.status)); // window.alert(sprintf('Error: %s (Http code %s)', json, response.status));
// alertify.error(sprintf('Some error occured. Http code is %s', response.status)); ns.error(Sprintf('Some error occured. Http code is %s', response.status));
// @ts-ignore // @ts-ignore
log.error(sprintf('Some error occured. Http code is %s', response.status)); log.error(sprintf('Some error occured. Http code is %s', response.status));
log.error(json); log.error(json);

View File

@ -1,31 +1,33 @@
import { Subject } from "../observe/Observable";
import { Model } from "./AbstractModel";
import { Observer } from "../observe/Observer";
import { fetchErrorHandler } from "./FetchErrorHandler";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { JsonAPI } from "../singleton/JsonAPI";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import * as log from "loglevel"; import * as log from "loglevel";
import { Subject } from "../observe/Observable";
import { Observer } from "../observe/Observer";
import { JsonAPI } from "../singleton/JsonAPI";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { fetchErrorHandler } from "./FetchErrorHandler";
import { NotificationService } from "../service/NotificationService";
export class UserModel implements Subject { export class UserModel implements Subject<ActiveUserViewModel> {
/** /**
* @type {Observer[]} List of subscribers. In real life, the list of * @type {Observer[]} List of subscribers. In real life, the list of
* subscribers can be stored more comprehensively (categorized by event * subscribers can be stored more comprehensively (categorized by event
* type, etc.). * type, etc.).
*/ */
private readonly observers: Observer[] = []; private readonly observers: Observer<ActiveUserViewModel>[] = [];
private _activeUsersList: ActiveUserViewModel[] | undefined; private _activeUsersList: ActiveUserViewModel[];
// @ts-ignore: Cannot find name 'hostAddress'. private readonly _notificationService: NotificationService;
constructor() { } constructor(notificationService: NotificationService) {
this._activeUsersList = [];
this._notificationService = notificationService;
}
/** /**
* Getter activeUsersList * Getter activeUsersList
* @return {ActiveUserViewModel[] } * @return {ActiveUserViewModel[] }
*/ */
public get activeUsersList(): ActiveUserViewModel[] | undefined { public get activeUsersList(): ActiveUserViewModel[] {
return this._activeUsersList; return this._activeUsersList;
} }
@ -33,32 +35,32 @@ export class UserModel implements Subject {
/** /**
* The subscription management methods. * The subscription management methods.
*/ */
public attach(observer: Observer): void { public attach(observer: Observer<ActiveUserViewModel>): void {
console.log('Subject: Attached an observer.'); log.info('Subject: Attached an observer.');
this.observers.push(observer); this.observers.push(observer);
} }
public detach(observer: Observer): void { public detach(observer: Observer<ActiveUserViewModel>): void {
const observerIndex = this.observers.indexOf(observer); const observerIndex = this.observers.indexOf(observer);
this.observers.splice(observerIndex, 1); this.observers.splice(observerIndex, 1);
console.log('Subject: Detached an observer.'); log.info('Subject: Detached an observer.');
} }
/** /**
* Trigger an update in each subscriber. * Trigger an update in each subscriber.
*/ */
public notify(): void { public notify(): void {
console.log('Subject: Notifying observers...'); log.info('Subject: Notifying observers...');
for (const observer of this.observers) { for (const observer of this.observers) {
observer.update(this._activeUsersList); observer.update({ data: this._activeUsersList!, op: "" });
} }
} }
public someBusinessMethod(activeuserList: ActiveUserViewModel[]): void { public someBusinessMethod(activeuserList: ActiveUserViewModel[]): void {
this._activeUsersList = activeuserList; this._activeUsersList = activeuserList;
this.helperMethod(); this.helperMethod();
console.log(`Subject: My state has just changed`); log.info(`Subject: My state has just changed`);
console.log(activeuserList); log.trace(activeuserList);
this.notify(); this.notify();
} }
@ -67,12 +69,12 @@ export class UserModel implements Subject {
*/ */
public getActiveUsers(): void { public getActiveUsers(): void {
if (JsonAPI.authToken != null) { if (JsonAPI.authToken != null) {
this.getActiveUsersAjax(JsonAPI.authToken) this._getActiveUsersAjax(JsonAPI.authToken)
.then(data => { .then(data => {
// // activeUsers = data; // // activeUsers = data;
// sessionStorage.setItem('activeUsers', JSON.stringify(data)); // sessionStorage.setItem('activeUsers', JSON.stringify(data));
// console.log(sessionStorage.getItem('activeUsers')); // log.trace(sessionStorage.getItem('activeUsers'));
console.log(`Subject: received ajax active users`); log.info(`Subject: received ajax active users`);
this._activeUsersList = data; this._activeUsersList = data;
this.notify(); this.notify();
}) })
@ -82,15 +84,15 @@ export class UserModel implements Subject {
} }
} }
async getActiveUsersAjax(authToken: string): Promise<any> { private async _getActiveUsersAjax(authToken: string): Promise<any> {
let headers = new Headers(); let headers = new Headers();
headers.append('X-AUTH-TOKEN', authToken); headers.append('X-AUTH-TOKEN', authToken);
let response = await fetch(JsonAPI.ACTIVE_USERS_GET, { let response = await fetch(JsonAPI.ACTIVE_USERS_GET, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
console.log(response.clone()); log.debug(response.clone());
if (fetchErrorHandler(response.clone())) { if (fetchErrorHandler(response.clone(), this._notificationService)) {
return null; return null;
} }
let data = await response.json(); let data = await response.json();

View File

@ -1,17 +1,18 @@
import { Observer } from "./Observer"; import { Observer } from "./Observer";
import { ObserverData } from "./ObserverData";
/** /**
* The Subject interface declares a set of methods for managing subscribers. * The Subject interface declares a set of methods for managing subscribers.
*/ */
export interface Subject { export interface Subject<T> {
// Attach an observer to the subject. // Attach an observer to the subject.
attach(observer: Observer): void; attach(observer: Observer<T>): void;
// Detach an observer from the subject. // Detach an observer from the subject.
detach(observer: Observer): void; detach(observer: Observer<T>): void;
// Notify all observers about an event. // Notify all observers about an event.
notify(param: any | null): void; notify(param: any): void;
} }
/** /**

View File

@ -1,8 +1,9 @@
import { Subject } from "./Observable"; import { Subject } from "./Observable";
import { ObserverData } from "./ObserverData";
export interface Observer { export interface Observer<T> {
// Receive update from subject. // Receive update from subject.
update(data: any): void; update(data: ObserverData<T>): void;
} }
// /** // /**

View File

@ -0,0 +1,4 @@
export interface ObserverData<T> {
data: Array<T>
op: string
}

View File

@ -2,7 +2,7 @@
import { Model } from "../model/AbstractModel"; import { Model } from "../model/AbstractModel";
import { Controller } from "../controller/AbstractController"; import { Controller } from "../controller/AbstractController";
import { Observer } from "../observe/Observer"; import { Observer } from "../observe/Observer";
export interface View extends Observer { export interface View extends Observer<any> {
readonly model: Model, readonly model: Model,
readonly element: any readonly element: any
} }

View File

@ -10,14 +10,17 @@ import { JsonAPI } from "../singleton/JsonAPI";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { ChatViewDeps } from "./ChatViewDeps"; import { ChatViewDeps } from "./ChatViewDeps";
import { fetchHandler } from "./FetchHandler"; import { fetchHandler } from "./FetchHandler";
import { ObserverData } from '../observe/ObserverData';
import { NotificationService } from '../service/NotificationService';
export class ChatView implements Observer { export class ChatView implements Observer<ChatMessageViewModel> {
private readonly _chatModel: ChatModel; private readonly _chatModel: ChatModel;
private readonly _messageContainer: HTMLElement; private readonly _messageContainer: HTMLElement;
private readonly _messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>; private readonly _messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>; private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
private readonly _markdownService: MarkDownService; private readonly _markdownService: MarkDownService;
private readonly _encryptionService: EncryptionService; private readonly _encryptionService: EncryptionService;
private readonly _notificationService: NotificationService;
constructor(deps: ChatViewDeps) { constructor(deps: ChatViewDeps) {
@ -27,7 +30,8 @@ export class ChatView implements Observer {
this._messageReceiveTemplate = deps.messageReceiveTemplate; this._messageReceiveTemplate = deps.messageReceiveTemplate;
this._markdownService = deps.markdownService; this._markdownService = deps.markdownService;
this._encryptionService = deps.encryptionService; this._encryptionService = deps.encryptionService;
this.addEventListeners(); this._notificationService = deps.notificationService;
this._initEventListeners();
$(document).ready(function () { $(document).ready(function () {
$('#action_menu_btn').click(function () { $('#action_menu_btn').click(function () {
@ -35,15 +39,18 @@ export class ChatView implements Observer {
}); });
}); });
this.chatMessagePageLoadAjax(); this._chatMessagePageLoadAjax();
} }
update(data: ChatMessageViewModel[]): void { update(cd: ObserverData<ChatMessageViewModel>): void {
log.info('ChatView: updating view'); log.info('ChatView: updating view');
// let html: string = "";
// let currentMsg = $('.msg:first'); switch (cd.op) {
// this._messageContainer.innerHTML = ""; case "clear": {
const rev: ChatMessageViewModel[] = Object.create(data) $(this._messageContainer).html("");
} break;
case "new": {
const rev: ChatMessageViewModel[] = Object.create(cd.data)
rev.reverse(); rev.reverse();
let arr: string[] = []; let arr: string[] = [];
rev.forEach((vm: ChatMessageViewModel) => { rev.forEach((vm: ChatMessageViewModel) => {
@ -61,41 +68,54 @@ export class ChatView implements Observer {
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp)); rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
} }
$(this._messageContainer).prepend(rendered); $(this._messageContainer).prepend(rendered);
// if (currentMsg.position() != null) {
// $(this._messageContainer).scrollTop(currentMsg.position().top)
// }
// log.debug(vm)
// log.debug(vmTemp)
// html += this._messageSendTemplate(vm);
}); });
// if (currentMsg.position() != null) { $(this._messageContainer).stop().animate({
// $(this._messageContainer).scrollTop(currentMsg.position().top) scrollTop: $(this._messageContainer)[0].scrollHeight
// } }, 1500);
} 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));
// html = DOMPurify.sanitize(md.render(html)); }
// this._element.innerHTML = html; else {
// log.debug(this._element.innerHTML); rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
}
$(this._messageContainer).prepend(rendered);
});
}
} }
private addEventListeners(): void {
this.addChatFormEL();
} }
private addChatFormEL() { private _initEventListeners(): void {
this._addChatFormEL();
}
private _addChatFormEL() {
const chatForm = document.getElementById('chatMessageForm') as HTMLSelectElement; const chatForm = document.getElementById('chatMessageForm') as HTMLSelectElement;
if (chatForm == null) { if (chatForm == null) {
log.error("Chat form is null"); log.error("Chat form is null");
} }
else { else {
chatForm.addEventListener('submit', (e) => this.createChatMessageDTO(e, chatForm)) chatForm.addEventListener('submit', (e) => this._createChatMessageDTO(e, chatForm))
} }
} }
private createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void { private _createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void {
e.preventDefault(); e.preventDefault();
let contactName = JsonAPI.contactName; let contactName = JsonAPI.contactName;
@ -106,7 +126,7 @@ export class ChatView implements Observer {
} }
if (!chatForm.checkValidity()) { if (!chatForm.checkValidity()) {
console.log("error"); log.error("Form is not valid");
chatForm.classList.add('was-validated'); chatForm.classList.add('was-validated');
return; return;
} }
@ -117,18 +137,24 @@ export class ChatView implements Observer {
const passphraseInput = document.getElementById('passphrase') as HTMLInputElement; const passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
if (chatInput.value == '' || chatInput.value == null) { if (chatInput.value == '' || chatInput.value == null) {
this._notificationService.error("Please enter a message");
log.error("Chat input is null."); log.error("Chat input is null.");
return; return;
} }
if (passphraseInput.value == '' || passphraseInput.value == null) { if (passphraseInput.value == '' || passphraseInput.value == null) {
log.error("Chat input is null."); this._notificationService.error("Please enter a passphrase");
log.error("Passphrase is null.");
return; return;
} }
// @ts-ignore // @ts-ignore
const messageContent = chatInput.value; const messageContent = chatInput.value;
const context = { fromUser: JsonAPI.principleName, toUser: "", message: this._markdownService.render(messageContent), messageTime: new Date().toLocaleString() }; const context = {
fromUser: JsonAPI.principleName, toUser: "",
message: this._markdownService.render(messageContent),
messageTime: new Date().toLocaleString()
};
// @ts-ignore // @ts-ignore
const msgContainer: string = this._messageSendTemplate(context); const msgContainer: string = this._messageSendTemplate(context);
$(this._messageContainer).append(DOMPurify.sanitize(msgContainer)); $(this._messageContainer).append(DOMPurify.sanitize(msgContainer));
@ -143,10 +169,10 @@ export class ChatView implements Observer {
// "messageTime": null // "messageTime": null
} }
// @ts-ignore // @ts-ignore
this.sendMessageAJAX(chatMessageDTO); this._sendMessageAJAX(chatMessageDTO);
} }
private sendMessageAJAX(chatMessageDTO: ChatMessageDTO): void { private _sendMessageAJAX(chatMessageDTO: ChatMessageDTO): void {
let headers = new Headers(); let headers = new Headers();
// console.log("Token = " + btoa("hmm" + ":" + "hmm")) // console.log("Token = " + btoa("hmm" + ":" + "hmm"))
@ -164,16 +190,15 @@ export class ChatView implements Observer {
log.debug(response); log.debug(response);
return response.clone(); return response.clone();
}) })
.then(response => fetchHandler(response)); .then(response => fetchHandler(response, this._notificationService));
} }
chatMessagePageLoadAjax() { private _chatMessagePageLoadAjax() {
this._messageContainer.addEventListener('scroll', (e) => { this._messageContainer.addEventListener('scroll', (e) => {
if ($(this._messageContainer).scrollTop() == 0) { if ($(this._messageContainer).scrollTop() == 0 && $(this._messageContainer).html() != "") {
let currentMsg = $('.msg:first'); let currentMsg = $('.msg:first');
log.debug('Reached top') log.debug('Reached top')
// @ts-ignore
let passphrase: string; let passphrase: string;
let passphraseInput = document.getElementById('passphrase') as HTMLInputElement; let passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
@ -189,9 +214,11 @@ export class ChatView implements Observer {
return; return;
} }
if (JsonAPI.contactName != null) if (JsonAPI.contactName != null)
this._chatModel.getMessages(JsonAPI.contactName, passphrase, null).then(() => { this._chatModel.getMessages(JsonAPI.contactName, passphrase, null, "page").then(() => {
log.debug(currentMsg.offset()!.top) if (currentMsg != null) {
$(this._messageContainer).scrollTop(currentMsg.position()!.top - $('.msg').position()!.top) // log.debug(currentMsg.offset()!.top)
$(this._messageContainer).scrollTop(currentMsg.position().top - $('.msg').position()!.top)
}
}); });
} }
}) })

View File

@ -2,6 +2,7 @@ import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { ChatModel } from "../model/ChatModel"; import { ChatModel } from "../model/ChatModel";
import { MarkDownService } from "../service/MarkDownService"; import { MarkDownService } from "../service/MarkDownService";
import { EncryptionService } from "../service/EncryptionService"; import { EncryptionService } from "../service/EncryptionService";
import { NotificationService } from "../service/NotificationService";
export interface ChatViewDeps { export interface ChatViewDeps {
chatModel: ChatModel; chatModel: ChatModel;
@ -10,5 +11,5 @@ export interface ChatViewDeps {
messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>; messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
markdownService: MarkDownService; markdownService: MarkDownService;
encryptionService: EncryptionService; encryptionService: EncryptionService;
notificationService: NotificationService
} }

View File

@ -1,9 +1,7 @@
import { NotificationService } from "../service/NotificationService"; import { NotificationService } from "../service/NotificationService";
import { AlertifyNotificationService } from "../service/AlertifyNotificationService";
import { Sprintf } from "../singleton/Sprintf"; import { Sprintf } from "../singleton/Sprintf";
export function fetchHandler(response: any) { export function fetchHandler(response: Response, ns: NotificationService) {
const ns: NotificationService = new AlertifyNotificationService();
if (response.ok) { if (response.ok) {
return response.json().then((json: any) => { return response.json().then((json: any) => {
// the status was ok and there is a json body // the status was ok and there is a json body

View File

@ -6,8 +6,10 @@ import * as DOMPurify from "dompurify";
import { SearchService } from "../service/SearchService"; import { SearchService } from "../service/SearchService";
import { UserModel } from "../model/UserModel"; import { UserModel } from "../model/UserModel";
import { UserViewDeps } from "./UserViewDeps"; import { UserViewDeps } from "./UserViewDeps";
import { ObserverData } from "../observe/ObserverData";
import { JsonAPI } from "../singleton/JsonAPI";
export class UserView implements Observer { export class UserView implements Observer<ActiveUserViewModel> {
private readonly _model: UserModel; private readonly _model: UserModel;
@ -31,35 +33,35 @@ export class UserView implements Observer {
this._userContactOnlineTemplate = deps.userContactOnlineTemplate; this._userContactOnlineTemplate = deps.userContactOnlineTemplate;
this._userContactOfflineTemplate = deps.userContactOfflineTemplate; this._userContactOfflineTemplate = deps.userContactOfflineTemplate;
this.addSearchEventListeners(); this._addSearchEventListeners();
} }
update(data: ActiveUserViewModel[]): void { update(d: ObserverData<ActiveUserViewModel>): void {
let html: string = ""; let html: string = "";
data.forEach((element: ActiveUserViewModel) => { d.data.forEach((element: ActiveUserViewModel) => {
element.online ? html += this._userContactOnlineTemplate(element) : html += this._userContactOfflineTemplate(element); element.online ? html += this._userContactOnlineTemplate(element) : html += this._userContactOfflineTemplate(element);
}); });
$(this._usersListElement).html(DOMPurify.sanitize(html)); $(this._usersListElement).html(DOMPurify.sanitize(html));
this.addUserCallBacks(); this._addUserCallBacks();
console.log(this._usersListElement.innerHTML);
} }
private addSearchEventListeners(): void { private _addSearchEventListeners(): void {
this.addSearchButtonEL(); this._addSearchButtonEL();
this.addSearchCancelEL(); this._addSearchCancelEL();
this.addSearchInputEL(); this._addSearchInputEL();
} }
private addUserCallBacks(): void { private _addUserCallBacks(): void {
let userBoxes = document.getElementsByClassName('user-box'); let userBoxes = document.getElementsByClassName('user-box');
for (let i = 0; i < userBoxes.length; i++) { for (let i = 0; i < userBoxes.length; i++) {
let userBox = userBoxes[i]; let userBox = userBoxes[i];
userBoxes[i].addEventListener('click', this.userCallBack.bind(this, userBox)); userBoxes[i].addEventListener('click', this._userCallBack.bind(this, userBox));
} }
} }
private userCallBack(el: Element): void { private _userCallBack(el: Element): void {
this._chatModel.clear();
let current = document.getElementsByClassName('user-box active'); let current = document.getElementsByClassName('user-box active');
let passphrase: string = ''; let passphrase: string = '';
@ -104,13 +106,14 @@ export class UserView implements Observer {
// console.log(this.getElementsByClassName('to-user-span')); // console.log(this.getElementsByClassName('to-user-span'));
let elem = el.getElementsByClassName('to-user-span')[0] as HTMLElement; let elem = el.getElementsByClassName('to-user-span')[0] as HTMLElement;
let userName = elem.innerText; let userName = elem.innerText;
JsonAPI.contactName = userName;
// @ts-ignore: Object is possibly 'null'. // @ts-ignore: Object is possibly 'null'.
document.getElementById('user-name-span').innerText = userName; document.getElementById('user-name-span').innerText = userName;
this._chatModel.getMessages(userName, passphrase, null); this._chatModel.getMessages(userName, passphrase, null, "new");
el.className += " active"; el.className += " active";
} }
private addSearchButtonEL() { private _addSearchButtonEL() {
this._userSearchButton.addEventListener('submit', (e) => { this._userSearchButton.addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
// log.trace(temp); // log.trace(temp);
@ -124,12 +127,12 @@ export class UserView implements Observer {
return; return;
} }
let searchResult = this._searchService.search(list, searchTerm); let searchResult = this._searchService.search(list, searchTerm);
this.update(searchResult); this.update({data: searchResult, op: ""});
log.debug(searchResult); log.debug(searchResult);
}) })
} }
private addSearchInputEL() { private _addSearchInputEL() {
this._userSearchInputElement.addEventListener('input', (e) => { this._userSearchInputElement.addEventListener('input', (e) => {
e.preventDefault(); e.preventDefault();
if (this._userSearchInputElement.value.length < 2) { if (this._userSearchInputElement.value.length < 2) {
@ -139,14 +142,13 @@ export class UserView implements Observer {
}) })
} }
private addSearchCancelEL() { private _addSearchCancelEL() {
this._userSearchCancelButton.addEventListener('click', (e) => { this._userSearchCancelButton.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
this._userSearchInputElement.value = ""; this._userSearchInputElement.value = "";
this._userSearchCancelButton.hidden = true; this._userSearchCancelButton.hidden = true;
let list = this._model.activeUsersList; let list = this._model.activeUsersList;
// @ts-ignore this.update({data: list, op: ""})
this.update(list)
}) })
} }