Improve frontend api part 1
This commit is contained in:
parent
e4041f9e41
commit
de2ad81f8e
56
src/main/frontend/chat/component/ChatComponent.ts
Normal file
56
src/main/frontend/chat/component/ChatComponent.ts
Normal 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
|
||||
// )
|
||||
// );
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public store(userName: string, message: ChatMessageViewModel): void {
|
||||
this._props.model.storeUserMessages(userName, Array(message), "new");
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
decryptForUser(dto: ChatMessageDTO, userName: string): string {
|
||||
return this._model.decryptForUser(dto, userName);
|
||||
}
|
||||
}
|
||||
|
@ -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"));
|
||||
|
@ -44,7 +44,7 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
|
||||
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<ChatMessageViewModel> {
|
||||
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<ChatMessageViewModel> {
|
||||
}
|
||||
}
|
||||
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: {
|
||||
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 {
|
||||
log.info("Clearing model");
|
||||
this._messagePageMap.set(JsonAPI.contactName!, 0);
|
||||
@ -200,7 +174,7 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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<ActiveUserViewModel> {
|
||||
/**
|
||||
* @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 _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<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 {
|
||||
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<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() {}
|
||||
}
|
||||
|
@ -41,16 +41,6 @@ export class ChatView implements Observer<ChatMessageViewModel> {
|
||||
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<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() {
|
||||
this._messageContainer.addEventListener("scroll", (e) => {
|
||||
if (
|
||||
|
@ -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<any> = 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;
|
||||
}
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user