diff --git a/src/main/frontend/chat/component/ChatComponent.ts b/src/main/frontend/chat/component/ChatComponent.ts new file mode 100644 index 0000000..6f68899 --- /dev/null +++ b/src/main/frontend/chat/component/ChatComponent.ts @@ -0,0 +1,56 @@ +import { UserModel } from "../model/UserModel"; +import { ChatModel } from "../model/ChatModel"; +import { UserView } from "../view/UserView"; +import { ChatView } from "../view/ChatView"; +import { UserController } from "../controller/UserController"; +import { ChatController } from "../controller/ChatController"; +import { Client } from "@stomp/stompjs"; +import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; +import log from "loglevel"; +import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; + +export interface Props { + // userModel: UserModel; + // chatModel: ChatModel; + // userView: UserView; + // chatView: ChatView; + userController: UserController; + chatController: ChatController; + chatStompClient: Client; +} + +export interface State {} + +export class ChatComponent { + private readonly _props: Props; + private readonly _state: State; + + constructor(props: Props, state: State) { + this._props = props; + this._state = state; + } + + private async wsHandler() { + this._props.chatStompClient.onConnect = () => { + this._props.chatStompClient.subscribe("/user/queue/reply", (reply) => { + const data = JSON.parse(reply.body) as ChatMessageDTO; + const plainText = this._props.userController.decryptForUser( + data, + data.fromUser + ); + this._props.chatController.store(data.fromUser, { + ...data, + message: plainText, + messageTime: new Date(data.messageTime), + }); + // log.debug( + // message, + // this._props.encryptionService.decrypt( + // "password", + // message.messageCipher + // ) + // ); + }); + }; + } +} diff --git a/src/main/frontend/chat/controller/ChatController.ts b/src/main/frontend/chat/controller/ChatController.ts index 1dbbaa0..1d9a68c 100644 --- a/src/main/frontend/chat/controller/ChatController.ts +++ b/src/main/frontend/chat/controller/ChatController.ts @@ -1,41 +1,42 @@ import { Controller } from "./AbstractController"; -import "../model/AbstractModel" -import "../model/UserModel" -import "../view/AbstractView" -import "../view/UserView" +import "../model/AbstractModel"; +import "../model/UserModel"; +import "../view/AbstractView"; +import "../view/UserView"; import { Model } from "../model/AbstractModel"; import { View } from "../view/AbstractView"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatModel } from "../model/ChatModel"; import { ChatView } from "../view/ChatView"; +import { Client } from "@stomp/stompjs"; +import { EncryptionService } from "../../common/service/EncryptionService"; +import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; +import log from "loglevel"; + +export interface Props { + model: ChatModel; + view: ChatView; + chatStompClient: Client; + encryptionService: EncryptionService; +} export class ChatController { - private _model: ChatModel; - private _view: ChatView; + private readonly _props: Props; + constructor(props: Props) { + this._props = props; + } + public test(): void { + const chatMessageViewModels: ChatMessageViewModel[] = []; + let chatMessageViewModelMock = new ChatMessageViewModel(); + chatMessageViewModelMock.fromUser = "user1"; + chatMessageViewModelMock.toUser = "user2"; + chatMessageViewModelMock.message = ""; + chatMessageViewModelMock.messageTime = new Date(); + chatMessageViewModels.push(chatMessageViewModelMock); + } - constructor(model: ChatModel, view: ChatView) { - this._model = model; - this._view = view; - } - - - /** - * eventHandler - */ - public eventHandler(vm: ChatMessageViewModel[]): void { - this._model.someBusinessMethod(vm); - } - - public test(): void { - const chatMessageViewModels: ChatMessageViewModel[] = []; - let chatMessageViewModelMock = new ChatMessageViewModel(); - chatMessageViewModelMock.fromUser = "user1"; - chatMessageViewModelMock.toUser = "user2"; - chatMessageViewModelMock.message = ""; - chatMessageViewModelMock.messageTime = new Date(); - chatMessageViewModels.push(chatMessageViewModelMock); - } - - -} \ No newline at end of file + public store(userName: string, message: ChatMessageViewModel): void { + this._props.model.storeUserMessages(userName, Array(message), "new"); + } +} diff --git a/src/main/frontend/chat/controller/UserController.ts b/src/main/frontend/chat/controller/UserController.ts index 7339542..10dcb56 100644 --- a/src/main/frontend/chat/controller/UserController.ts +++ b/src/main/frontend/chat/controller/UserController.ts @@ -1,50 +1,30 @@ import { Controller } from "./AbstractController"; -import "../model/AbstractModel" -import "../model/UserModel" -import "../view/AbstractView" -import "../view/UserView" +import "../model/AbstractModel"; +import "../model/UserModel"; +import "../view/AbstractView"; +import "../view/UserView"; import { Model } from "../model/AbstractModel"; import { View } from "../view/AbstractView"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { UserView } from "../view/UserView"; import { UserModel } from "../model/UserModel"; +import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; +import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; export class UserController { - private _model: UserModel; - private _view: UserView; + private _model: UserModel; + private _view: UserView; + constructor(model: UserModel, view: UserView) { + this._model = model; + this._view = view; + } - constructor(model: UserModel, view: UserView) { - this._model = model; - this._view = view; - } + // public getActiveUsers(): void { + // this._model.getActiveUsers(); + // } - - /** - * eventHandler - */ - public eventHandler(vm: ActiveUserViewModel[]): void { - this._model.someBusinessMethod(vm); - } - - public test(): void { - const activeUsersMock: ActiveUserViewModel[] = []; - let activeUserViewModelMock = new ActiveUserViewModel(); - activeUserViewModelMock.userName = "some user"; - // activeUserViewModelMock.lastActive = "3 hrs ago"; - activeUserViewModelMock.online = true; - activeUsersMock.push(activeUserViewModelMock); - activeUserViewModelMock = new ActiveUserViewModel(); - // activeUserViewModelMock.lastActive = "3 hrs ago"; - activeUserViewModelMock.online = true; - activeUserViewModelMock.userName = "some user 2"; - activeUsersMock.push(activeUserViewModelMock); - this.eventHandler(activeUsersMock); - } - - public getActiveUsers(): void { - this._model.getActiveUsers(); - } - - -} \ No newline at end of file + decryptForUser(dto: ChatMessageDTO, userName: string): string { + return this._model.decryptForUser(dto, userName); + } +} diff --git a/src/main/frontend/chat/main.ts b/src/main/frontend/chat/main.ts index 72a5679..65d36fe 100644 --- a/src/main/frontend/chat/main.ts +++ b/src/main/frontend/chat/main.ts @@ -21,8 +21,10 @@ import { ChatViewDeps } from "./view/ChatViewDeps"; import { UserView } from "./view/UserView"; import { UserViewDeps } from "./view/UserViewDeps"; import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel"; +import { Credentials } from "../common/global/Credentials"; // log.setLevel("TRACE"); +const authToken = Credentials.authToken; const chatStompClient = new Client(); chatStompClient.webSocketFactory = () => new SockJS("/chat"); @@ -56,7 +58,11 @@ const encryptionService = EncryptionServiceFactory.getEncryptionService(); const chatModelHelper = new ChatModelHelper(encryptionService, ns); const chatModel = new ChatModel(chatModelHelper); -const userModel = new UserModel(ns); +const userModel = new UserModel({ + notificationService: ns, + encryptionService, + authToken, +}); 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'. @@ -66,14 +72,19 @@ const cvDeps: ChatViewDeps = { ), messageReceiveTemplate: TemplateFactory.getTemplate("msg_container_template"), markdownService: new MarkDownItMarkDownService(), - encryptionService: encryptionService, + encryptionService, notificationService: ns, userModel: userModel, - chatStompClient: chatStompClient, + chatStompClient, }; const chatView = new ChatView(cvDeps); chatModel.attach(chatView); -const chatController = new ChatController(chatModel, chatView); +const chatController = new ChatController({ + model: chatModel, + view: chatView, + chatStompClient, + encryptionService, +}); const uvDeps: UserViewDeps = { model: userModel, @@ -97,7 +108,7 @@ const uvDeps: UserViewDeps = { const userView = new UserView(uvDeps); userModel.attach(userView); const userController = new UserController(userModel, userView); -userController.getActiveUsers(); +// userController.getActiveUsers(); // @ts-ignore Handlebars.registerHelper("moment", require("helper-moment")); diff --git a/src/main/frontend/chat/model/ChatModel.ts b/src/main/frontend/chat/model/ChatModel.ts index 240f377..ef238c9 100644 --- a/src/main/frontend/chat/model/ChatModel.ts +++ b/src/main/frontend/chat/model/ChatModel.ts @@ -44,7 +44,7 @@ export class ChatModel implements Subject { log.info("Subject: Detached an observer."); } - private storeUserMessages( + public storeUserMessages( username: string, messages: ChatMessageViewModel[], op: string @@ -52,25 +52,28 @@ export class ChatModel implements Subject { switch (op) { case "clear": this._messagesMap.set(username, []); + this.notify({ userName: username, data: [], op: op }); break; case "page": this._messagesMap.set( username, messages.concat(this.getStoredUserMessages(username)) ); + this.notify({ userName: username, data: messages, op: op }); break; - // case "page": this._messagesMap.set(username, messages); case "new": this._messagesMap.set( username, this.getStoredUserMessages(username).concat(messages) ); + this.notify({ userName: username, data: messages, op: op }); break; case "update": this._messagesMap.set( username, this.getStoredUserMessages(username).concat(messages) ); + this.notify({ userName: username, data: messages, op: op }); break; default: new Error("Invalid option"); @@ -102,47 +105,18 @@ export class ChatModel implements Subject { } } break; - case "new": - { - const od: ObserverData = { - data: p.data, - op: p.op, - }; - for (const observer of this._observers) { - observer.update(od); - } - } - break; - case "page": - { - const od: ObserverData = { - data: p.data, - op: p.op, - }; - for (const observer of this._observers) { - observer.update(od); - } - } - break; - case "update": - { - const od: ObserverData = { - data: p.data, - op: p.op, - }; - for (const observer of this._observers) { - observer.update(od); - } - } - break; default: { - log.error("error"); + const od: ObserverData = { + data: p.data, + op: p.op, + }; + for (const observer of this._observers) { + observer.update(od); + } } } } - public someBusinessMethod(chatMessageList: ChatMessageViewModel[]): void {} - public clear(): void { log.info("Clearing model"); this._messagePageMap.set(JsonAPI.contactName!, 0); @@ -200,7 +174,7 @@ export class ChatModel implements Subject { } this.storeUserMessages(aVm.userName!, cVMsFiltered, op); - this.notify({ userName: aVm.userName!, data: cVMsFiltered, op: op }); + // this.notify({ userName: aVm.userName!, data: cVMsFiltered, op: op }); } return cVMsFiltered; diff --git a/src/main/frontend/chat/model/UserModel.ts b/src/main/frontend/chat/model/UserModel.ts index bf264e9..c7c3e40 100644 --- a/src/main/frontend/chat/model/UserModel.ts +++ b/src/main/frontend/chat/model/UserModel.ts @@ -1,11 +1,21 @@ import log from "loglevel"; +import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; import { NotificationService } from "../../common/service/NotificationService"; import { Subject } from "../observe/Observable"; import { Observer } from "../observe/Observer"; import { JsonAPI } from "../singleton/JsonAPI"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; +import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { fetchErrorHandler } from "./FetchErrorHandler"; +import { EncryptionService } from "../../common/service/EncryptionService"; +import { createApiHeaders } from "../../common/ajax/util"; +import { getActiveUsers } from "../../common/ajax/Users"; +export interface Props { + encryptionService: EncryptionService; + notificationService: NotificationService; + authToken: string; +} export class UserModel implements Subject { /** * @type {Observer[]} List of subscribers. In real life, the list of @@ -14,12 +24,18 @@ export class UserModel implements Subject { */ private readonly observers: Observer[] = []; - private _activeUsersList: ActiveUserViewModel[] = Array(); - private readonly _notificationService: NotificationService; + private readonly _activeUsersList: ActiveUserViewModel[] = Array(); + private readonly _props: Props; - constructor(notificationService: NotificationService) { + constructor(props: Props) { this._activeUsersList = []; - this._notificationService = notificationService; + this._props = props; + + getActiveUsers(props.authToken).then((data) => { + log.debug(data); + this._activeUsersList.push(...data); + this.notify(); + }); } /** @@ -54,63 +70,16 @@ export class UserModel implements Subject { } } - public someBusinessMethod(activeuserList: ActiveUserViewModel[]): void { - this._activeUsersList = activeuserList; - this.helperMethod(); - log.info(`Subject: My state has just changed`); - log.trace(activeuserList); - this.notify(); - } - updateLastActive(username: String, lastActive: Date): void { this._activeUsersList .filter((u) => u.userName == username) .forEach((u) => (u.lastActive = lastActive)); } - /** - * 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"); - } + public decryptForUser(dto: ChatMessageDTO, userName: string): string { + const passphrase = + this._activeUsersList.find((u) => u.userName === userName)?.passphrase || + ""; + return this._props.encryptionService.decrypt(passphrase, dto.messageCipher); } - - private async _getActiveUsersAjax(authToken: string): Promise { - let headers = new Headers(); - headers.append("X-AUTH-TOKEN", authToken); - let response = await fetch(JsonAPI.ACTIVE_USERS_GET, { - method: "GET", - headers: headers, - }); - log.debug(response.clone()); - if (fetchErrorHandler(response.clone(), this._notificationService)) { - return null; - } - let data = await response.json(); - // return data; - return new Promise((resolve, reject) => { - if (data != null) { - resolve(data); - } else reject("Response data null"); - }); - } - - private helperMethod() {} } diff --git a/src/main/frontend/chat/view/ChatView.ts b/src/main/frontend/chat/view/ChatView.ts index 95b1819..ce8e9e4 100644 --- a/src/main/frontend/chat/view/ChatView.ts +++ b/src/main/frontend/chat/view/ChatView.ts @@ -41,16 +41,6 @@ export class ChatView implements Observer { this._deps = deps; this._initEventListeners(); - this._deps.chatStompClient.onConnect = () => { - this._deps.chatStompClient.subscribe("/user/queue/reply", (reply) => { - const message = JSON.parse(reply.body) as ChatMessageDTO; - log.debug( - message, - this._encryptionService.decrypt("password", message.messageCipher) - ); - }); - }; - $(document).ready(function () { $("#action_menu_btn").click(function () { $(".action_menu").toggle(); @@ -219,27 +209,6 @@ export class ChatView implements Observer { // } } - private _sendMessageAJAX(chatMessageDTO: any): 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 ( diff --git a/src/main/frontend/common/ajax/Messages.ts b/src/main/frontend/common/ajax/Messages.ts index eb7492c..c684d53 100644 --- a/src/main/frontend/common/ajax/Messages.ts +++ b/src/main/frontend/common/ajax/Messages.ts @@ -28,17 +28,11 @@ export async function sendReencryptedMessages( rencryptionDTOs: ReencryptionDTO[], authToken: string ) { - let headers = new Headers(); - // console.log("Token = " + btoa("hmm" + ":" + "hmm")) - - // headers.append('Accept','application/json') - headers.append("Content-Type", "application/json"); - headers.append("X-AUTH-TOKEN", authToken); - fetch(Routes.Admin.reencrypt, { + return fetch(Routes.Admin.reencrypt, { method: "POST", - headers: headers, + headers: createApiHeaders(authToken), body: JSON.stringify(rencryptionDTOs), - }).then((response) => console.log(response)); + }); } export async function getOneMessage( @@ -52,10 +46,6 @@ export async function getOneMessage( method: "GET", headers: createApiHeaders(authToken), }); - log.debug(response.clone()); - // if (fetchErrorHandler(response.clone(), this._notificationService)) { - // return null; - // } const data: Promise = await response.json(); function func(data: any) { const d1 = data.map((d: any) => { @@ -77,3 +67,15 @@ export async function getStats(authToken: string) { }); return (await response.json()) as StatsDTO; } + +export async function sendMessageAJAX( + chatMessageDTO: ChatMessageDTO, + authToken: string +) { + const response = fetch(JsonAPI.MESSAGE_POST, { + method: "POST", + headers: createApiHeaders(authToken), + body: JSON.stringify(chatMessageDTO), + }); + return response; +} diff --git a/src/main/frontend/common/ajax/Users.ts b/src/main/frontend/common/ajax/Users.ts index 2562ca2..275ea88 100644 --- a/src/main/frontend/common/ajax/Users.ts +++ b/src/main/frontend/common/ajax/Users.ts @@ -1,6 +1,8 @@ import { Routes } from "../routes/Routes"; import { createApiHeaders } from "./util"; import { AdminUserDTO } from "../dto/AdminUserDTO"; +import { ActiveUserViewModel } from "../../chat/viewmodel/ActiveUserViewModel"; +import { JsonAPI } from "../../chat/singleton/JsonAPI"; export async function getOtherUsers( authToken: string @@ -13,3 +15,20 @@ export async function getOtherUsers( let data = (await response.json()) as AdminUserDTO[]; return data; } + +export async function getActiveUsers( + authToken: string +): Promise { + const response = await fetch(JsonAPI.ACTIVE_USERS_GET, { + method: "GET", + headers: createApiHeaders(authToken), + }); + const data = await response.json(); + const data2 = data.map((d: any) => { + if (d.lastActive == null) return d; + + d.lastActive = new Date(d.lastActive); + return d; + }) as ActiveUserViewModel[]; + return data2; +}