2026-09-22 21:57:22 +02:00
|
|
|
import * as React from "../../react";
|
2026-04-18 11:55:01 +00:00
|
|
|
import SearchViewModel from "../Utility/searchViewModel";
|
|
|
|
|
import CoreViewModel, { Context } from "../Global/coreViewModel";
|
|
|
|
|
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
|
|
|
|
|
import ChatMessageViewModel from "../Chat/chatMessageViewModel";
|
|
|
|
|
import { CommonKeys } from "../../View/keystrokes";
|
2026-04-06 20:47:49 +00:00
|
|
|
import {
|
|
|
|
|
ChatMessage,
|
|
|
|
|
ChatMessageReaction,
|
2026-04-07 18:39:55 +00:00
|
|
|
ReactionSymbols,
|
2026-04-06 20:47:49 +00:00
|
|
|
} from "../../Model/Chat/chatModel";
|
2026-04-12 19:20:37 +00:00
|
|
|
|
|
|
|
|
export default class MessagePageViewModel extends Context {
|
2026-04-01 08:29:39 +00:00
|
|
|
// state
|
|
|
|
|
chatMessageViewModels: React.MapState<ChatMessageViewModel> =
|
|
|
|
|
new React.MapState();
|
2026-04-11 19:26:13 +00:00
|
|
|
filteredMessageViewModels: React.ListState<ChatMessageViewModel> =
|
|
|
|
|
new React.ListState();
|
|
|
|
|
searchViewModel: SearchViewModel<ChatMessageViewModel>;
|
|
|
|
|
isFilterModalOpen = new React.State<boolean>(false);
|
2026-04-11 21:01:23 +00:00
|
|
|
reactionFilter = new React.State<ReactionSymbols | undefined>(undefined);
|
2026-04-12 11:58:45 +00:00
|
|
|
isFilterActive: React.State<boolean>;
|
2026-04-11 21:01:23 +00:00
|
|
|
|
2026-04-14 13:15:38 +00:00
|
|
|
replyingMessage = new React.State<ChatMessageViewModel | undefined>(
|
|
|
|
|
undefined,
|
|
|
|
|
);
|
2026-04-14 11:28:40 +00:00
|
|
|
composingMessage = new React.State<string>("");
|
2026-04-15 12:47:10 +00:00
|
|
|
focusSetter = new React.State(null);
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
// guards
|
|
|
|
|
cannotSendMessage: React.State<boolean>;
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
// methods
|
|
|
|
|
sendMessage = (): void => {
|
|
|
|
|
if (this.cannotSendMessage.value == true) return;
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
this.sendMessageFromBody(this.composingMessage.value);
|
|
|
|
|
this.composingMessage.value = "";
|
|
|
|
|
};
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
sendMessageFromBody = (body: string): void => {
|
2026-04-15 12:47:10 +00:00
|
|
|
let replyId: string | undefined = undefined;
|
2026-04-14 13:15:38 +00:00
|
|
|
if (this.replyingMessage.value) {
|
|
|
|
|
replyId = this.replyingMessage.value.chatMessage.id;
|
|
|
|
|
}
|
2026-04-15 12:47:10 +00:00
|
|
|
this.chatViewModel.chatModel.sendMessage(body, replyId);
|
2026-04-14 13:15:38 +00:00
|
|
|
this.replyingMessage.value = undefined;
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
decryptMessage = async (
|
|
|
|
|
messageViewModel: ChatMessageViewModel,
|
|
|
|
|
): Promise<void> => {
|
|
|
|
|
const chatMessage: ChatMessage = messageViewModel.chatMessage;
|
|
|
|
|
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
|
|
|
|
|
this.chatViewModel.chatModel.addMessage(chatMessage);
|
|
|
|
|
messageViewModel.loadData();
|
|
|
|
|
};
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-08 20:31:25 +00:00
|
|
|
sendReaction = (
|
|
|
|
|
messageId: string,
|
|
|
|
|
content: ReactionSymbols,
|
|
|
|
|
isDeleting: boolean,
|
|
|
|
|
): void => {
|
|
|
|
|
this.chatViewModel.chatModel.sendReaction(
|
|
|
|
|
messageId,
|
|
|
|
|
content,
|
|
|
|
|
isDeleting,
|
|
|
|
|
);
|
2026-04-06 20:47:49 +00:00
|
|
|
};
|
|
|
|
|
|
2026-09-09 00:04:27 +02:00
|
|
|
setReply = (chatMessageViewModel: ChatMessageViewModel|undefined): void => {
|
2026-04-15 12:47:10 +00:00
|
|
|
this.replyingMessage.value = chatMessageViewModel;
|
|
|
|
|
this.setFocus();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
resetReply = (): void => {
|
|
|
|
|
this.replyingMessage.value = undefined;
|
|
|
|
|
this.setFocus();
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
// view
|
|
|
|
|
showChatMessage = (chatMessage: ChatMessage): void => {
|
|
|
|
|
const chatMessageViewModel = new ChatMessageViewModel(
|
|
|
|
|
this.coreViewModel,
|
|
|
|
|
this,
|
|
|
|
|
chatMessage,
|
|
|
|
|
chatMessage.sender ==
|
|
|
|
|
this.chatViewModel.settingsViewModel.username.value,
|
|
|
|
|
);
|
2024-07-23 00:25:28 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
const existingChatMessageViewModel: ChatMessageViewModel | undefined =
|
|
|
|
|
this.chatMessageViewModels.value.get(chatMessage.id);
|
|
|
|
|
if (existingChatMessageViewModel != undefined) {
|
|
|
|
|
existingChatMessageViewModel.body.value = chatMessage.body;
|
|
|
|
|
existingChatMessageViewModel.status.value = chatMessage.status;
|
|
|
|
|
} else {
|
|
|
|
|
this.chatMessageViewModels.set(
|
|
|
|
|
chatMessage.id,
|
|
|
|
|
chatMessageViewModel,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-07-23 00:25:28 +02:00
|
|
|
|
2026-04-07 18:48:01 +00:00
|
|
|
handleReaction = (reaction: ChatMessageReaction): void => {
|
2026-04-06 20:47:49 +00:00
|
|
|
const messageViewModel: ChatMessageViewModel | undefined =
|
|
|
|
|
this.chatMessageViewModels.value.get(reaction.messageId);
|
|
|
|
|
if (messageViewModel == undefined) return;
|
2026-04-07 18:48:01 +00:00
|
|
|
messageViewModel.handleReaction(reaction);
|
2026-04-06 20:47:49 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-12 19:20:37 +00:00
|
|
|
showFilterModal = (): void => {
|
2026-04-11 19:26:13 +00:00
|
|
|
this.isFilterModalOpen.value = true;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-12 19:20:37 +00:00
|
|
|
hideFilterModal = (): void => {
|
2026-04-11 20:37:19 +00:00
|
|
|
this.isFilterModalOpen.value = false;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-11 21:01:23 +00:00
|
|
|
revokeReactionFilter = (): void => {
|
|
|
|
|
this.reactionFilter.value = undefined;
|
2026-04-12 11:58:45 +00:00
|
|
|
};
|
2026-04-11 21:01:23 +00:00
|
|
|
|
|
|
|
|
setReactionFilter = (content: ReactionSymbols): void => {
|
|
|
|
|
this.reactionFilter.value = content;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-12 19:20:37 +00:00
|
|
|
resetFilter = (): void => {
|
|
|
|
|
this.revokeReactionFilter();
|
|
|
|
|
this.searchViewModel.search("");
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-15 12:47:10 +00:00
|
|
|
setFocus = (): void => {
|
|
|
|
|
this.focusSetter.callSubscriptions();
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
// load
|
|
|
|
|
loadData = (): void => {
|
|
|
|
|
this.chatMessageViewModels.clear();
|
|
|
|
|
for (const chatMessage of this.chatViewModel.chatModel.messages) {
|
|
|
|
|
this.showChatMessage(chatMessage);
|
|
|
|
|
}
|
2026-04-06 20:47:49 +00:00
|
|
|
for (const reaction of this.chatViewModel.chatModel.reactions) {
|
2026-04-07 18:48:01 +00:00
|
|
|
this.handleReaction(reaction);
|
2026-04-06 20:47:49 +00:00
|
|
|
}
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
2024-07-22 21:07:44 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
// init
|
|
|
|
|
constructor(
|
2026-04-13 10:58:21 +00:00
|
|
|
public readonly coreViewModel: CoreViewModel,
|
|
|
|
|
public readonly chatViewModel: ChatViewModel,
|
2026-04-01 08:29:39 +00:00
|
|
|
) {
|
2026-04-13 19:45:52 +00:00
|
|
|
super("message-page");
|
2026-04-12 19:20:37 +00:00
|
|
|
|
|
|
|
|
// states
|
2026-04-01 08:29:39 +00:00
|
|
|
this.cannotSendMessage = React.createProxyState(
|
|
|
|
|
[
|
|
|
|
|
this.chatViewModel.settingsViewModel.username,
|
|
|
|
|
this.composingMessage,
|
|
|
|
|
],
|
|
|
|
|
() =>
|
|
|
|
|
this.chatViewModel.settingsViewModel.username.value == "" ||
|
|
|
|
|
this.composingMessage.value == "",
|
|
|
|
|
);
|
2026-04-11 19:26:13 +00:00
|
|
|
|
|
|
|
|
this.searchViewModel = new SearchViewModel(
|
|
|
|
|
this.chatMessageViewModels,
|
|
|
|
|
this.filteredMessageViewModels,
|
|
|
|
|
(chatMessageViewModel) => [chatMessageViewModel.body.value],
|
|
|
|
|
new React.ListState(),
|
|
|
|
|
);
|
2026-04-12 11:58:45 +00:00
|
|
|
|
|
|
|
|
this.isFilterActive = React.createProxyState(
|
|
|
|
|
[this.searchViewModel.appliedQuery, this.reactionFilter],
|
|
|
|
|
() =>
|
|
|
|
|
this.searchViewModel.appliedQuery.value != "" ||
|
|
|
|
|
this.reactionFilter.value != undefined,
|
|
|
|
|
);
|
2026-04-12 19:20:37 +00:00
|
|
|
|
|
|
|
|
// keystrokes
|
|
|
|
|
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
|
2026-04-15 12:47:10 +00:00
|
|
|
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
|
|
|
|
|
if (this.isFilterModalOpen.value == true) {
|
|
|
|
|
this.hideFilterModal();
|
|
|
|
|
} else {
|
|
|
|
|
this.replyingMessage.value = undefined;
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-04-12 19:20:37 +00:00
|
|
|
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
|
2026-04-15 12:47:10 +00:00
|
|
|
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
|
2026-04-12 19:20:37 +00:00
|
|
|
|
|
|
|
|
this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
|
2026-04-01 08:29:39 +00:00
|
|
|
}
|
2024-07-22 21:07:44 +02:00
|
|
|
}
|