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 log = require('loglevel');
import { ObserverData } from "../observe/ObserverData";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
interface Params {
userName: string,
@ -44,10 +45,11 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
private storeUserMessages(username: string, messages: ChatMessageViewModel[], op: string) {
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);
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");
}
@ -86,6 +88,12 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
observer.update(od);
}
} 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") }
}
}
@ -100,28 +108,31 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
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)
this._messagePageMap.set(contactName, 0);
public async getMessages(vm: ActiveUserViewModel, op: string): Promise<ChatMessageViewModel[]> {
if (this._messagePageMap.get(vm.userName!) == null)
this._messagePageMap.set(vm.userName!, 0);
const pageNumber = this._messagePageMap.get(contactName)
const cVMs = await this._chatModelHelper.getMessages(contactName, passphrase, pageNumber!, lastMessageTime, op);
const pageNumber = this._messagePageMap.get(vm.userName!)
const cVMs = await this._chatModelHelper.getMessages(vm.userName!, vm.passphrase, pageNumber!, vm.lastMessageTime!, op);
if (cVMs != null) {
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(existingMessages);
log.debug('new messages:')
log.debug(cVMs);
this.storeUserMessages(contactName, cVMs, op);
this.notify({ userName: contactName, data: cVMs, op: op });
this.storeUserMessages(vm.userName!, cVMs, op);
this.notify({ userName: vm.userName!, data: cVMs, op: op });
}
else {
log.error('Messages were null');
}
if (cVMs.length != 0) {
this._messagePageMap.set(contactName, this._messagePageMap.get(contactName)! + 1);
if (cVMs.length != 0 && op == "update") vm.lastMessageTime = cVMs[cVMs.length - 1].messageTime;
if (cVMs.length != 0 && op != "update") {
this._messagePageMap.set(vm.userName!, this._messagePageMap.get(vm.userName!)! + 1);
}
return cVMs;

View File

@ -18,23 +18,32 @@ export class ChatModelHelper {
}
public async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: string | null, op: string): Promise<ChatMessageViewModel[]> {
switch (lastMessageTime) {
case null: {
const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(userName, page!);
const cVMs = Promise.all(data.map(vm => this._toChatMessageVMAsync(vm, passphrase)).reverse());
return cVMs;
public async getMessages(userName: string, passphrase: string, page: number | null, lastMessageTime: Date | null, op: string): Promise<ChatMessageViewModel[]> {
switch (op) {
case "page": {
const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(userName, page!);
const cVMs = Promise.all(data.map(vm => this._toChatMessageVMAsync(vm, passphrase)).reverse());
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!);
return data.map(vm => this._toChatMessageVM(vm, passphrase));
}
default: {
log.error("Invalid operation");
return Array();
}
}
}
public async isPassphraseValid(passphrase: string, userName: string): Promise<boolean> {
const messages: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(userName, 0);
if(messages.length === 0) return true;
if (messages.length === 0) return true;
try {
this._encryptionService.decrypt(passphrase, messages[0].messageCipher)
} catch (error) {
@ -102,17 +111,30 @@ export class ChatModelHelper {
return null;
}
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();
if (JsonAPI.authToken == null) {
log.error("authToken null");
return;
};
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',
headers: headers
});
@ -121,6 +143,52 @@ export class ChatModelHelper {
return null;
}
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 log = require("loglevel");
import bootbox = require("bootbox")
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
export class AlertifyNotificationService implements NotificationService {
private readonly _alertify = alertify;
@ -28,9 +29,8 @@ export class AlertifyNotificationService implements NotificationService {
message(message: string): void {
this._alertify.message(message);
}
passphrasePrompt(vm: ActiveUserViewModel, vms: ActiveUserViewModel[], cb1: (contactName: string, passphrase: string,
lastMessageTime: string | null, op: string) => void,
cb2: () => any, cb3: (...x: any) => any): void {
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 () {
@ -100,10 +100,19 @@ export class AlertifyNotificationService implements NotificationService {
log.error("invalid password");
return;
}
cb1(vm.userName!, result, null, "new");
vm.unlocked = true
vms.filter(v => v.userName == vm.userName).map(v => { v.passphrase = result; v.unlocked = true })
cb2();
vm.passphrase = result;
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(vms)
}
@ -111,7 +120,11 @@ export class AlertifyNotificationService implements NotificationService {
});
}
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
}, 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);
});
// $(this._messageContainer).stop().animate({
// scrollTop: $(this._messageContainer)[0].scrollHeight
// }, 1500);
} break;
default: {
const rev: ChatMessageViewModel[] = Object.create(cd.data)
rev.reverse();
@ -137,8 +161,10 @@ export class ChatView implements Observer<ChatMessageViewModel> {
const chatInput = document.getElementById('chatInput') as HTMLInputElement;
const passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
const passphrase = this._userModel.activeUsersList.find(u => u.userName == JsonAPI.contactName)?.passphrase
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");
@ -155,13 +181,13 @@ export class ChatView implements Observer<ChatMessageViewModel> {
const messageContent = chatInput.value;
const msgTime = new Date();
const context: ChatMessageViewModel = {
fromUser: JsonAPI.principleName || "error",
fromUser: JsonAPI.principleName || "error",
toUser: "",
message: messageContent,
messageTime: msgTime
};
this.update({data: new Array(context), op: "new"})
this.update({ data: new Array(context), op: "new" })
let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(passphrase!, messageContent)
let chatMessageDTO = {
@ -200,9 +226,10 @@ export class ChatView implements Observer<ChatMessageViewModel> {
if ($(this._messageContainer).scrollTop() == 0 && $(this._messageContainer).html() != "") {
let currentMsg = $('.msg:first');
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)
this._chatModel.getMessages(JsonAPI.contactName, ab!.passphrase, null, "page").then(() => {
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

@ -23,6 +23,7 @@ export class UserView implements Observer<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;
@ -65,6 +66,7 @@ export class UserView implements Observer<ActiveUserViewModel> {
private _userCallBack(el: Element): void {
this._chatModel.clear();
clearInterval(this._newMessagesLoop);
let current = document.getElementsByClassName('user-box active');
let passphrase: string = '';
@ -122,12 +124,19 @@ export class UserView implements Observer<ActiveUserViewModel> {
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 (this._newMessagesLoop != null) {
this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel,
vm, "update"), 2000);
}
}
private _promptHandler(vm: ActiveUserViewModel, vms: ActiveUserViewModel[]) {
// vms.filter(v => v.userName == vm.userName).map(v => v.userName = vm.userName)
log.debug(vms)
this._model.notify();
this._newMessagesLoop = setInterval(this._chatModel.getMessages.bind(this._chatModel,
vm, "update"), 2000);
}
private _addSearchButtonEL() {

View File

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