Added logic for fetching new messages

This commit is contained in:
Rohan Sircar 2020-03-18 10:31:05 +05:30
parent b236b87e5d
commit e1520d5182
6 changed files with 166 additions and 37 deletions

View File

@ -5,6 +5,7 @@ import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { ChatModelHelper } from "./ChatModelHelper"; import { ChatModelHelper } from "./ChatModelHelper";
import log = require('loglevel'); import log = require('loglevel');
import { ObserverData } from "../observe/ObserverData"; import { ObserverData } from "../observe/ObserverData";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
interface Params { interface Params {
userName: string, userName: string,
@ -44,10 +45,11 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
private storeUserMessages(username: string, messages: ChatMessageViewModel[], op: string) { private storeUserMessages(username: string, messages: ChatMessageViewModel[], op: string) {
switch (op) { switch (op) {
case "clear": this._messagesMap.set(username, []); case "clear": this._messagesMap.set(username, []); break;
case "page": this._messagesMap.set(username, messages.concat(this.getStoredUserMessages(username))); break; case "page": this._messagesMap.set(username, messages.concat(this.getStoredUserMessages(username))); break;
// case "page": this._messagesMap.set(username, messages); // case "page": this._messagesMap.set(username, messages);
case "new": this._messagesMap.set(username, this.getStoredUserMessages(username).concat(messages)); break; case "new": this._messagesMap.set(username, this.getStoredUserMessages(username).concat(messages)); break;
case "update": this._messagesMap.set(username, this.getStoredUserMessages(username).concat(messages)); break;
default: new Error("Invalid option"); default: new Error("Invalid option");
} }
@ -86,6 +88,12 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
observer.update(od); observer.update(od);
} }
} break; } break;
case "update": {
const od: ObserverData<ChatMessageViewModel> = { data: p.data, op: p.op }
for (const observer of this._observers) {
observer.update(od);
}
} break;
default: { log.error("error") } default: { log.error("error") }
} }
} }
@ -100,28 +108,31 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
this.notify({ userName: "", data: [], op: "clear" }) this.notify({ userName: "", data: [], op: "clear" })
} }
public async getMessages(contactName: string, passphrase: string, lastMessageTime: string | null, op: string): Promise<ChatMessageViewModel[]> { public async getMessages(vm: ActiveUserViewModel, op: string): Promise<ChatMessageViewModel[]> {
if (this._messagePageMap.get(contactName) == null) if (this._messagePageMap.get(vm.userName!) == null)
this._messagePageMap.set(contactName, 0); this._messagePageMap.set(vm.userName!, 0);
const pageNumber = this._messagePageMap.get(contactName) const pageNumber = this._messagePageMap.get(vm.userName!)
const cVMs = await this._chatModelHelper.getMessages(contactName, passphrase, pageNumber!, lastMessageTime, op); const cVMs = await this._chatModelHelper.getMessages(vm.userName!, vm.passphrase, pageNumber!, vm.lastMessageTime!, op);
if (cVMs != null) { if (cVMs != null) {
log.info('Subject: My state has just changed') log.info('Subject: My state has just changed')
const existingMessages = this.getStoredUserMessages(contactName); const existingMessages = this.getStoredUserMessages(vm.userName!);
log.debug('existing message:') log.debug('existing message:')
log.debug(existingMessages); log.debug(existingMessages);
log.debug('new messages:') log.debug('new messages:')
log.debug(cVMs); log.debug(cVMs);
this.storeUserMessages(contactName, cVMs, op); this.storeUserMessages(vm.userName!, cVMs, op);
this.notify({ userName: contactName, data: cVMs, op: op }); this.notify({ userName: vm.userName!, data: cVMs, op: op });
} }
else { else {
log.error('Messages were null'); log.error('Messages were null');
} }
if (cVMs.length != 0) { if (cVMs.length != 0 && op == "update") vm.lastMessageTime = cVMs[cVMs.length - 1].messageTime;
this._messagePageMap.set(contactName, this._messagePageMap.get(contactName)! + 1);
if (cVMs.length != 0 && op != "update") {
this._messagePageMap.set(vm.userName!, this._messagePageMap.get(vm.userName!)! + 1);
} }
return cVMs; return cVMs;

View File

@ -18,17 +18,26 @@ export class ChatModelHelper {
} }
public async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: string | null, op: string): Promise<ChatMessageViewModel[]> { public async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: Date | null, op: string): Promise<ChatMessageViewModel[]> {
switch (lastMessageTime) { switch (op) {
case null: { case "page": {
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: { case "new": {
const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(userName, page!);
const cVMs = Promise.all(data.map(vm => this._toChatMessageVMAsync(vm, passphrase)).reverse());
return cVMs;
}
case "update": {
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));
} }
default: {
log.error("Invalid operation");
return Array();
}
} }
} }
@ -102,17 +111,30 @@ export class ChatModelHelper {
return null; return null;
} }
const data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
return data; function func(data: any) {
const d1 = data.map((d: any) => {
if (d.messageTime == null)
return null;
d.messageTime = new Date(d.messageTime)
return d;
});
return d1;
}
const data2 = func(data)
return data2;
} }
private async _getNewMessagesAjax(toUser: string, lastMessageTimeStamp: string): Promise<any> { private async _getMessagesAjax(toUser: string, lastMessageTimeStamp: Date): 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 response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}/${lastMessageTimeStamp}`, { // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
// log.debug(url)
const response = await fetch(`/api/chat/get/messages/${toUser}`, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
@ -121,6 +143,52 @@ export class ChatModelHelper {
return null; return null;
} }
const data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
return data; function func(data: any) {
const d1 = data.map((d: any) => {
if (d.messageTime == null)
return null;
d.messageTime = new Date(d.messageTime)
return d;
});
return d1;
}
const data2 = func(data)
return data2;
}
private async _getNewMessagesAjax(toUser: string, lastMessageTimeStamp: Date): Promise<any> {
const headers = new Headers();
if (JsonAPI.authToken == null) {
log.error("authToken null");
return;
};
headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
// const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
// log.debug(url)
log.debug(lastMessageTimeStamp);
log.debug(lastMessageTimeStamp.toISOString())
const response = await fetch(`/api/chat/get/messages/${toUser}/${lastMessageTimeStamp.toISOString()}`, {
method: 'GET',
headers: headers
});
log.debug(response.clone());
if (fetchErrorHandler(response.clone(), this._notificationService)) {
return null;
}
const data: Promise<any> = await response.json();
function func(data: any) {
const d1 = data.map((d: any) => {
if (d.messageTime == null)
return null;
d.messageTime = new Date(d.messageTime)
return d;
});
return d1;
}
const data2 = func(data)
log.debug("LOOK HERE NOW ", data, data2)
return data2;
} }
} }

View File

@ -4,6 +4,7 @@ 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";
export class AlertifyNotificationService implements NotificationService { export class AlertifyNotificationService implements NotificationService {
private readonly _alertify = alertify; private readonly _alertify = alertify;
@ -28,9 +29,8 @@ export class AlertifyNotificationService implements NotificationService {
message(message: string): void { message(message: string): void {
this._alertify.message(message); this._alertify.message(message);
} }
passphrasePrompt(vm: ActiveUserViewModel, vms: ActiveUserViewModel[], cb1: (contactName: string, passphrase: string, passphrasePrompt(vm: ActiveUserViewModel, vms: ActiveUserViewModel[], cb1: (v: ActiveUserViewModel, op: string) => ChatMessageViewModel[],
lastMessageTime: string | null, op: string) => void, cb2: (...x: any) => any, cb3: (...x: any) => any): void {
cb2: () => any, cb3: (...x: any) => any): void {
// alertify.myprompt || alertify.dialog('myprompt', function () { // alertify.myprompt || alertify.dialog('myprompt', function () {
@ -100,10 +100,19 @@ export class AlertifyNotificationService implements NotificationService {
log.error("invalid password"); log.error("invalid password");
return; return;
} }
cb1(vm.userName!, result, null, "new");
vm.unlocked = true vm.unlocked = true
vms.filter(v => v.userName == vm.userName).map(v => { v.passphrase = result; v.unlocked = true }) vm.passphrase = result;
cb2(); const chatMessages: ChatMessageViewModel[] = await cb1(vm, "new");
log.debug("here", chatMessages)
vms.filter(v => v.userName == vm.userName).map(v => {
v.passphrase = result;
v.unlocked = true;
v.lastMessageTime = new Date(chatMessages[chatMessages.length - 1].messageTime);
})
log.debug("last message time = ", chatMessages[chatMessages.length - 1].messageTime)
vm.lastMessageTime = new Date(chatMessages[chatMessages.length - 1].messageTime);
cb2(vm, vms);
log.debug(vm) log.debug(vm)
log.debug(vms) log.debug(vms)
} }
@ -111,7 +120,11 @@ export class AlertifyNotificationService implements NotificationService {
}); });
} }
else { else {
cb1(vm.userName!, vm.passphrase, null, "new"); cb1(vm, "new");
} }
} }
} }
// log.debug("LOOK HERE",vm.lastMessageTime)
// setInterval(this._chatModel.getMessages.bind(this._chatModel,
// vm.userName!, vm.passphrase, vm.lastMessageTime!, "new"), 2000);

View File

@ -76,6 +76,30 @@ export class ChatView implements Observer<ChatMessageViewModel> {
scrollTop: $(this._messageContainer)[0].scrollHeight scrollTop: $(this._messageContainer)[0].scrollHeight
}, 1500); }, 1500);
} break; } 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);
});
// $(this._messageContainer).stop().animate({
// scrollTop: $(this._messageContainer)[0].scrollHeight
// }, 1500);
} break;
default: { default: {
const rev: ChatMessageViewModel[] = Object.create(cd.data) const rev: ChatMessageViewModel[] = Object.create(cd.data)
rev.reverse(); rev.reverse();
@ -137,8 +161,10 @@ export class ChatView implements Observer<ChatMessageViewModel> {
const chatInput = document.getElementById('chatInput') as HTMLInputElement; const chatInput = document.getElementById('chatInput') as HTMLInputElement;
const passphraseInput = document.getElementById('passphrase') as HTMLInputElement; const vm = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName);
const passphrase = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName)?.passphrase // new Date().
vm!.lastMessageTime = new Date();
const passphrase = vm?.passphrase
if (chatInput.value == '' || chatInput.value == null) { if (chatInput.value == '' || chatInput.value == null) {
this._notificationService.error("Please enter a message"); this._notificationService.error("Please enter a message");
@ -200,9 +226,10 @@ export class ChatView implements Observer<ChatMessageViewModel> {
if ($(this._messageContainer).scrollTop() == 0 && $(this._messageContainer).html() != "") { if ($(this._messageContainer).scrollTop() == 0 && $(this._messageContainer).html() != "") {
let currentMsg = $('.msg:first'); let currentMsg = $('.msg:first');
log.debug('Reached top') log.debug('Reached top')
let ab = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName) const vm = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName)
if (JsonAPI.contactName != null) if (JsonAPI.contactName != null)
this._chatModel.getMessages(JsonAPI.contactName, ab!.passphrase, null, "page").then(() => {
this._chatModel.getMessages(vm!, "page").then(() => {
if (currentMsg != null) { if (currentMsg != null) {
// log.debug(currentMsg.offset()!.top) // log.debug(currentMsg.offset()!.top)
$(this._messageContainer).scrollTop(currentMsg.position().top - $('.msg').position()!.top) $(this._messageContainer).scrollTop(currentMsg.position().top - $('.msg').position()!.top)

View File

@ -23,6 +23,7 @@ export class UserView implements Observer<ActiveUserViewModel> {
private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate<ActiveUserViewModel>; private readonly _userContactOnlineTemplate: Handlebars.TemplateDelegate<ActiveUserViewModel>;
private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate<ActiveUserViewModel>; private readonly _userContactOfflineTemplate: Handlebars.TemplateDelegate<ActiveUserViewModel>;
private readonly _notificationService: NotificationService; private readonly _notificationService: NotificationService;
private _newMessagesLoop: any;
constructor(deps: UserViewDeps) { constructor(deps: UserViewDeps) {
this._model = deps.model; this._model = deps.model;
@ -65,6 +66,7 @@ export class UserView implements Observer<ActiveUserViewModel> {
private _userCallBack(el: Element): void { private _userCallBack(el: Element): void {
this._chatModel.clear(); this._chatModel.clear();
clearInterval(this._newMessagesLoop);
let current = document.getElementsByClassName('user-box active'); let current = document.getElementsByClassName('user-box active');
let passphrase: string = ''; let passphrase: string = '';
@ -122,12 +124,19 @@ export class UserView implements Observer<ActiveUserViewModel> {
this._promptHandler.bind(this), this._chatModel.isPassphraseValid.bind(this._chatModel)); this._promptHandler.bind(this), this._chatModel.isPassphraseValid.bind(this._chatModel));
// this._chatModel.getMessages(userName, vm.passphrase, null, "new"); // this._chatModel.getMessages(userName, vm.passphrase, null, "new");
el.className += " active"; el.className += " active";
log.debug("loop", this._newMessagesLoop)
if (this._newMessagesLoop != null) {
this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel,
vm, "update"), 2000);
}
} }
private _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) { private _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) {
// vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName) // vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName)
log.debug(vms) log.debug(vms)
this._model.notify(); this._model.notify();
this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel,
vm, "update"), 2000);
} }
private _addSearchButtonEL() { private _addSearchButtonEL() {

View File

@ -4,4 +4,5 @@ export class ActiveUserViewModel {
unlocked: boolean = false; unlocked: boolean = false;
passphrase: string = ""; passphrase: string = "";
lastActive: Date | undefined; lastActive: Date | undefined;
lastMessageTime: Date | undefined;
} }