Improve frontend api part 1

This commit is contained in:
Rohan Sircar 2020-08-09 10:55:44 +05:30
parent e4041f9e41
commit de2ad81f8e
9 changed files with 195 additions and 214 deletions

View File

@ -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, <ChatMessageViewModel>{
...data,
message: plainText,
messageTime: new Date(data.messageTime),
});
// log.debug(
// message,
// this._props.encryptionService.decrypt(
// "password",
// message.messageCipher
// )
// );
});
};
}
}

View File

@ -1,30 +1,29 @@
import { Controller } from "./AbstractController"; import { Controller } from "./AbstractController";
import "../model/AbstractModel" import "../model/AbstractModel";
import "../model/UserModel" import "../model/UserModel";
import "../view/AbstractView" import "../view/AbstractView";
import "../view/UserView" import "../view/UserView";
import { Model } from "../model/AbstractModel"; import { Model } from "../model/AbstractModel";
import { View } from "../view/AbstractView"; import { View } from "../view/AbstractView";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { ChatModel } from "../model/ChatModel"; import { ChatModel } from "../model/ChatModel";
import { ChatView } from "../view/ChatView"; 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 { export class ChatController {
private _model: ChatModel; private readonly _props: Props;
private _view: ChatView; constructor(props: Props) {
this._props = props;
constructor(model: ChatModel, view: ChatView) {
this._model = model;
this._view = view;
}
/**
* eventHandler
*/
public eventHandler(vm: ChatMessageViewModel[]): void {
this._model.someBusinessMethod(vm);
} }
public test(): void { public test(): void {
@ -37,5 +36,7 @@ export class ChatController {
chatMessageViewModels.push(chatMessageViewModelMock); chatMessageViewModels.push(chatMessageViewModelMock);
} }
public store(userName: string, message: ChatMessageViewModel): void {
this._props.model.storeUserMessages(userName, Array(message), "new");
}
} }

View File

@ -1,50 +1,30 @@
import { Controller } from "./AbstractController"; import { Controller } from "./AbstractController";
import "../model/AbstractModel" import "../model/AbstractModel";
import "../model/UserModel" import "../model/UserModel";
import "../view/AbstractView" import "../view/AbstractView";
import "../view/UserView" import "../view/UserView";
import { Model } from "../model/AbstractModel"; import { Model } from "../model/AbstractModel";
import { View } from "../view/AbstractView"; import { View } from "../view/AbstractView";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { UserView } from "../view/UserView"; import { UserView } from "../view/UserView";
import { UserModel } from "../model/UserModel"; import { UserModel } from "../model/UserModel";
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
export class UserController { export class UserController {
private _model: UserModel; private _model: UserModel;
private _view: UserView; private _view: UserView;
constructor(model: UserModel, view: UserView) { constructor(model: UserModel, view: UserView) {
this._model = model; this._model = model;
this._view = view; this._view = view;
} }
// public getActiveUsers(): void {
// this._model.getActiveUsers();
// }
/** decryptForUser(dto: ChatMessageDTO, userName: string): string {
* eventHandler return this._model.decryptForUser(dto, userName);
*/
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();
}
} }

View File

@ -21,8 +21,10 @@ import { ChatViewDeps } from "./view/ChatViewDeps";
import { UserView } from "./view/UserView"; import { UserView } from "./view/UserView";
import { UserViewDeps } from "./view/UserViewDeps"; import { UserViewDeps } from "./view/UserViewDeps";
import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel";
import { Credentials } from "../common/global/Credentials";
// log.setLevel("TRACE"); // log.setLevel("TRACE");
const authToken = Credentials.authToken;
const chatStompClient = new Client(); const chatStompClient = new Client();
chatStompClient.webSocketFactory = () => new SockJS("/chat"); chatStompClient.webSocketFactory = () => new SockJS("/chat");
@ -56,7 +58,11 @@ const encryptionService = EncryptionServiceFactory.getEncryptionService();
const chatModelHelper = new ChatModelHelper(encryptionService, ns); const chatModelHelper = new ChatModelHelper(encryptionService, ns);
const chatModel = new ChatModel(chatModelHelper); const chatModel = new ChatModel(chatModelHelper);
const userModel = new UserModel(ns); const userModel = new UserModel({
notificationService: ns,
encryptionService,
authToken,
});
const cvDeps: ChatViewDeps = { const cvDeps: ChatViewDeps = {
chatModel: chatModel, 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'. // @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"), messageReceiveTemplate: TemplateFactory.getTemplate("msg_container_template"),
markdownService: new MarkDownItMarkDownService(), markdownService: new MarkDownItMarkDownService(),
encryptionService: encryptionService, encryptionService,
notificationService: ns, notificationService: ns,
userModel: userModel, userModel: userModel,
chatStompClient: chatStompClient, chatStompClient,
}; };
const chatView = new ChatView(cvDeps); const chatView = new ChatView(cvDeps);
chatModel.attach(chatView); chatModel.attach(chatView);
const chatController = new ChatController(chatModel, chatView); const chatController = new ChatController({
model: chatModel,
view: chatView,
chatStompClient,
encryptionService,
});
const uvDeps: UserViewDeps = { const uvDeps: UserViewDeps = {
model: userModel, model: userModel,
@ -97,7 +108,7 @@ const uvDeps: UserViewDeps = {
const userView = new UserView(uvDeps); const userView = new UserView(uvDeps);
userModel.attach(userView); userModel.attach(userView);
const userController = new UserController(userModel, userView); const userController = new UserController(userModel, userView);
userController.getActiveUsers(); // userController.getActiveUsers();
// @ts-ignore // @ts-ignore
Handlebars.registerHelper("moment", require("helper-moment")); Handlebars.registerHelper("moment", require("helper-moment"));

View File

@ -44,7 +44,7 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
log.info("Subject: Detached an observer."); log.info("Subject: Detached an observer.");
} }
private storeUserMessages( public storeUserMessages(
username: string, username: string,
messages: ChatMessageViewModel[], messages: ChatMessageViewModel[],
op: string op: string
@ -52,25 +52,28 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
switch (op) { switch (op) {
case "clear": case "clear":
this._messagesMap.set(username, []); this._messagesMap.set(username, []);
this.notify({ userName: username, data: [], op: op });
break; break;
case "page": case "page":
this._messagesMap.set( this._messagesMap.set(
username, username,
messages.concat(this.getStoredUserMessages(username)) messages.concat(this.getStoredUserMessages(username))
); );
this.notify({ userName: username, data: messages, op: op });
break; break;
// case "page": this._messagesMap.set(username, messages);
case "new": case "new":
this._messagesMap.set( this._messagesMap.set(
username, username,
this.getStoredUserMessages(username).concat(messages) this.getStoredUserMessages(username).concat(messages)
); );
this.notify({ userName: username, data: messages, op: op });
break; break;
case "update": case "update":
this._messagesMap.set( this._messagesMap.set(
username, username,
this.getStoredUserMessages(username).concat(messages) this.getStoredUserMessages(username).concat(messages)
); );
this.notify({ userName: username, data: messages, op: op });
break; break;
default: default:
new Error("Invalid option"); new Error("Invalid option");
@ -102,46 +105,17 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
} }
} }
break; 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;
case "update":
{
const od: ObserverData<ChatMessageViewModel> = {
data: p.data,
op: p.op,
};
for (const observer of this._observers) {
observer.update(od);
}
}
break;
default: { default: {
log.error("error"); const od: ObserverData<ChatMessageViewModel> = {
data: p.data,
op: p.op,
};
for (const observer of this._observers) {
observer.update(od);
}
} }
} }
} }
public someBusinessMethod(chatMessageList: ChatMessageViewModel[]): void {}
public clear(): void { public clear(): void {
log.info("Clearing model"); log.info("Clearing model");
@ -200,7 +174,7 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
} }
this.storeUserMessages(aVm.userName!, cVMsFiltered, op); 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; return cVMsFiltered;

View File

@ -1,11 +1,21 @@
import log from "loglevel"; import log from "loglevel";
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
import { NotificationService } from "../../common/service/NotificationService"; import { NotificationService } from "../../common/service/NotificationService";
import { Subject } from "../observe/Observable"; import { Subject } from "../observe/Observable";
import { Observer } from "../observe/Observer"; import { Observer } from "../observe/Observer";
import { JsonAPI } from "../singleton/JsonAPI"; import { JsonAPI } from "../singleton/JsonAPI";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { fetchErrorHandler } from "./FetchErrorHandler"; 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<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
@ -14,12 +24,18 @@ export class UserModel implements Subject<ActiveUserViewModel> {
*/ */
private readonly observers: Observer<ActiveUserViewModel>[] = []; private readonly observers: Observer<ActiveUserViewModel>[] = [];
private _activeUsersList: ActiveUserViewModel[] = Array(); private readonly _activeUsersList: ActiveUserViewModel[] = Array();
private readonly _notificationService: NotificationService; private readonly _props: Props;
constructor(notificationService: NotificationService) { constructor(props: Props) {
this._activeUsersList = []; 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<ActiveUserViewModel> {
} }
} }
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 { updateLastActive(username: String, lastActive: Date): void {
this._activeUsersList this._activeUsersList
.filter((u) => u.userName == username) .filter((u) => u.userName == username)
.forEach((u) => (u.lastActive = lastActive)); .forEach((u) => (u.lastActive = lastActive));
} }
/** public decryptForUser(dto: ChatMessageDTO, userName: string): string {
* getActiveUsers const passphrase =
*/ this._activeUsersList.find((u) => u.userName === userName)?.passphrase ||
public getActiveUsers(): void { "";
if (JsonAPI.authToken != null) { return this._props.encryptionService.decrypt(passphrase, dto.messageCipher);
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());
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

@ -41,16 +41,6 @@ export class ChatView implements Observer<ChatMessageViewModel> {
this._deps = deps; this._deps = deps;
this._initEventListeners(); 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 () { $(document).ready(function () {
$("#action_menu_btn").click(function () { $("#action_menu_btn").click(function () {
$(".action_menu").toggle(); $(".action_menu").toggle();
@ -219,27 +209,6 @@ export class ChatView implements Observer<ChatMessageViewModel> {
// } // }
} }
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() { private _chatMessagePageLoadAjax() {
this._messageContainer.addEventListener("scroll", (e) => { this._messageContainer.addEventListener("scroll", (e) => {
if ( if (

View File

@ -28,17 +28,11 @@ export async function sendReencryptedMessages(
rencryptionDTOs: ReencryptionDTO[], rencryptionDTOs: ReencryptionDTO[],
authToken: string authToken: string
) { ) {
let headers = new Headers(); return fetch(Routes.Admin.reencrypt, {
// 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, {
method: "POST", method: "POST",
headers: headers, headers: createApiHeaders(authToken),
body: JSON.stringify(rencryptionDTOs), body: JSON.stringify(rencryptionDTOs),
}).then((response) => console.log(response)); });
} }
export async function getOneMessage( export async function getOneMessage(
@ -52,10 +46,6 @@ export async function getOneMessage(
method: "GET", method: "GET",
headers: createApiHeaders(authToken), headers: createApiHeaders(authToken),
}); });
log.debug(response.clone());
// if (fetchErrorHandler(response.clone(), this._notificationService)) {
// return null;
// }
const data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
function func(data: any) { function func(data: any) {
const d1 = data.map((d: any) => { const d1 = data.map((d: any) => {
@ -77,3 +67,15 @@ export async function getStats(authToken: string) {
}); });
return (await response.json()) as StatsDTO; 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;
}

View File

@ -1,6 +1,8 @@
import { Routes } from "../routes/Routes"; import { Routes } from "../routes/Routes";
import { createApiHeaders } from "./util"; import { createApiHeaders } from "./util";
import { AdminUserDTO } from "../dto/AdminUserDTO"; import { AdminUserDTO } from "../dto/AdminUserDTO";
import { ActiveUserViewModel } from "../../chat/viewmodel/ActiveUserViewModel";
import { JsonAPI } from "../../chat/singleton/JsonAPI";
export async function getOtherUsers( export async function getOtherUsers(
authToken: string authToken: string
@ -13,3 +15,20 @@ export async function getOtherUsers(
let data = (await response.json()) as AdminUserDTO[]; let data = (await response.json()) as AdminUserDTO[];
return data; return data;
} }
export async function getActiveUsers(
authToken: string
): Promise<ActiveUserViewModel[]> {
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;
}