Cleaned up frontend code a bit

This commit is contained in:
Rohan Sircar 2020-05-27 23:03:42 +05:30
parent aff632a091
commit a400e555fa
4 changed files with 548 additions and 604 deletions

View File

@ -7,112 +7,104 @@ import { fetchErrorHandler } from "./FetchErrorHandler";
import { NotificationService } from "../service/NotificationService"; import { NotificationService } from "../service/NotificationService";
export class UserModel implements Subject<ActiveUserViewModel> { 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<ActiveUserViewModel>[] = []; private readonly observers: Observer<ActiveUserViewModel>[] = [];
private _activeUsersList: ActiveUserViewModel[] = Array();
private readonly _notificationService: NotificationService;
private _activeUsersList: ActiveUserViewModel[]; constructor(notificationService: NotificationService) {
private readonly _notificationService: NotificationService; this._activeUsersList = [];
this._notificationService = notificationService;
}
constructor(notificationService: NotificationService) { /**
this._activeUsersList = []; * Getter activeUsersList
this._notificationService = notificationService; * @return {ActiveUserViewModel[] }
*/
public get activeUsersList(): ActiveUserViewModel[] {
return this._activeUsersList;
}
/**
* The subscription management methods.
*/
public attach(observer: Observer<ActiveUserViewModel>): void {
log.info("Subject: Attached an observer.");
this.observers.push(observer);
}
public detach(observer: Observer<ActiveUserViewModel>): void {
const observerIndex = this.observers.indexOf(observer);
this.observers.splice(observerIndex, 1);
log.info("Subject: Detached an 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 someBusinessMethod(activeuserList: ActiveUserViewModel[]): void {
* Getter activeUsersList this._activeUsersList = activeuserList;
* @return {ActiveUserViewModel[] } this.helperMethod();
*/ log.info(`Subject: My state has just changed`);
public get activeUsersList(): ActiveUserViewModel[] { log.trace(activeuserList);
return this._activeUsersList; this.notify();
} }
/**
* 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);
* The subscription management methods. return d;
*/
public attach(observer: Observer<ActiveUserViewModel>): void {
log.info('Subject: Attached an observer.');
this.observers.push(observer);
}
public detach(observer: Observer<ActiveUserViewModel>): void {
const observerIndex = this.observers.indexOf(observer);
this.observers.splice(observerIndex, 1);
log.info('Subject: Detached an 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 someBusinessMethod(activeuserList: ActiveUserViewModel[]): void {
this._activeUsersList = activeuserList;
this.helperMethod();
log.info(`Subject: My state has just changed`);
log.trace(activeuserList);
this.notify();
}
/**
* 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<any> {
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()); this._activeUsersList = data;
if (fetchErrorHandler(response.clone(), this._notificationService)) { this.notify();
return null; });
} } else {
let data = await response.json(); log.error("Auth token is null");
// return data;
return new Promise((resolve, reject) => {
if (data != null) {
resolve(data)
}
else
reject('Response data null')
})
} }
}
private helperMethod() { } private async _getActiveUsersAjax(authToken: string): Promise<any> {
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() {}
} }

View File

@ -3,128 +3,83 @@ import { NotificationService } from "./NotificationService";
import * as alertify from "alertifyjs"; import * as alertify from "alertifyjs";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import log = require("loglevel"); import log = require("loglevel");
import bootbox = require("bootbox") import bootbox = require("bootbox");
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
export class AlertifyNotificationService implements NotificationService { 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; vms
constructor() { .filter((v) => v.userName == vm.userName)
this._alertify.set('notifier', 'position', 'top-center'); .map((v) => {
} v.passphrase = result;
success(message: string): void { v.unlocked = true;
this._alertify.success(message) if (chatMessages.length > 0) {
} v.lastMessageTime = new Date(
error(message: string): void { chatMessages[chatMessages.length - 1].messageTime
this._alertify.error(message); );
} const lastMessageText = (v.lastMessageText =
errorWithDelay(message: string, delay: number): void { chatMessages[chatMessages.length - 1].message);
const _delay = this._alertify.get('notifier', 'delay'); if (lastMessageText.length > 15) {
this._alertify.set('notifier', 'delay', delay); v.lastMessageText =
this._alertify.error(message); chatMessages[chatMessages.length - 1].message.slice(
this._alertify.set('notifier', 'delay', _delay); 0,
} 15
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)
}
} }
}); });
} // vm.lastMessageTime = new Date(chatMessages[chatMessages.length - 1].messageTime);
else { cb2(vm, vms);
cb1(vm, "new"); // log.debug(vm);
} // log.debug(vms);
}
},
});
} else {
cb1(vm, "new");
} }
}
} }

View File

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

View File

@ -11,174 +11,184 @@ import { JsonAPI } from "../singleton/JsonAPI";
import { NotificationService } from "../service/NotificationService"; import { NotificationService } from "../service/NotificationService";
export class UserView implements Observer<ActiveUserViewModel> { export class UserView implements Observer<ActiveUserViewModel> {
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<ActiveUserViewModel>;
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;
private readonly _model: UserModel; this._addSearchEventListeners();
private readonly _chatModel: ChatModel; }
private readonly _usersListElement: HTMLElement;
private readonly _userSearchInputElement: HTMLInputElement;
private readonly _userSearchButton: HTMLElement;
private readonly _userSearchCancelButton: HTMLElement;
private readonly _searchService: SearchService<ActiveUserViewModel>;
private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate<ActiveUserViewModel>;
private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate<ActiveUserViewModel>;
private readonly _notificationService: NotificationService;
private _newMessagesLoop: any;
constructor(deps: UserViewDeps) { update(d: ObserverData<ActiveUserViewModel>): void {
this._model = deps.model; let html: string = "";
this._chatModel = deps.chatModel; d.data.forEach((element: ActiveUserViewModel) => {
this._usersListElement = deps.usersListElement; element.online
this._userSearchInputElement = deps.userSearchInputElement; ? (html += this._userContactOnlineTemplate(element))
this._userSearchButton = deps.userSearchButton; : (html += this._userContactOfflineTemplate(element));
this._userSearchCancelButton = deps.userSearchCancelButton; });
this._searchService = deps.searchService; $(this._usersListElement).html(DOMPurify.sanitize(html));
this._userContactOnlineTemplate = deps.userContactOnlineTemplate; this._addUserCallBacks();
this._userContactOfflineTemplate = deps.userContactOfflineTemplate; }
this._notificationService = deps.notificationService;
this._addSearchEventListeners(); 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", "");
} }
// Add the active class to the current/clicked button
update(d: ObserverData<ActiveUserViewModel>): void { else if (current.length == 0) {
let html: string = ""; let elem = document.getElementById("passphrase-initial") as any;
d.data.forEach((element: ActiveUserViewModel) => { if (elem == null) {
element.online ? html += this._userContactOnlineTemplate(element) : log.error("passphraseInput element reference is null");
html += this._userContactOfflineTemplate(element); return;
}); }
$(this._usersListElement).html(DOMPurify.sanitize(html)); // passphrase = elem.value;
this._addUserCallBacks(); // 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'));
private _addSearchEventListeners(): void { let elem = el.getElementsByClassName("to-user-span")[0] as HTMLElement;
this._addSearchButtonEL(); let userName = elem.innerText;
this._addSearchCancelEL(); JsonAPI.contactName = userName;
this._addSearchInputEL(); // @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(
private _addUserCallBacks(): void { vm,
let userBoxes = document.getElementsByClassName('user-box'); this._model.activeUsersList,
this._chatModel.getMessages.bind(this._chatModel),
Array.from(userBoxes).forEach((ub: Element) => this._promptHandler.bind(this),
ub.addEventListener('click', this._userCallBack.bind(this, ub))) 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 _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) {
// vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName)
// log.debug(vms);
private _userCallBack(el: Element): void { if (vm.lastMessageTime) {
this._chatModel.clear(); this._newMessagesLoop = setInterval(
clearInterval(this._newMessagesLoop); this._chatModel.getMessages.bind(this._chatModel, vm, "update"),
let current = document.getElementsByClassName('user-box active'); 10_000
);
let passphrase: string = ''; this._model.notify();
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[]) { private _addSearchButtonEL() {
// vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName) this._userSearchButton.addEventListener("submit", (e) => {
log.debug(vms) 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);
});
}
if (vm.lastMessageTime != null) { private _addSearchInputEL() {
this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel, this._userSearchInputElement.addEventListener("input", (e) => {
vm, "update"), 10_000); e.preventDefault();
} if (this._userSearchInputElement.value.length < 2) {
this._model.notify(); log.debug("inputted");
} this._userSearchCancelButton.hidden = false;
}
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: "" })
})
}
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: "" });
});
}
} }