Files
CT/src/ViewModel/Pages/messagePageViewModel.ts
T

232 lines
7.0 KiB
TypeScript
Raw Normal View History

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";
2026-09-23 00:09:09 +02:00
import * as React from "../../react";
2026-04-18 11:55:01 +00:00
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();
2026-09-23 14:44:34 +02:00
replyViewSelectedMessage: React.State<ChatMessageViewModel|undefined> = new React.State(undefined);
2026-09-24 15:04:01 +02:00
isReplyViewActive: React.State<boolean> = React.createProxyState([this.replyViewSelectedMessage], ()=>this.replyViewSelectedMessage.value != undefined);
isReplyViewInactive = React.createProxyState([this.isReplyViewActive], ()=>!this.isReplyViewActive.value);
2026-04-11 19:26:13 +00:00
searchViewModel: SearchViewModel<ChatMessageViewModel>;
isFilterModalOpen = new React.State<boolean>(false);
2026-04-11 21:01:23 +00:00
reactionFilter = new React.State<ReactionSymbols | undefined>(undefined);
isFilterActive: React.State<boolean>;
2026-04-11 21:01:23 +00:00
replyingMessage = new React.State<ChatMessageViewModel | undefined>(
undefined,
);
2026-04-14 11:28:40 +00:00
composingMessage = new React.State<string>("");
2026-09-23 14:44:34 +02:00
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
2026-09-26 13:31:36 +02:00
sendMessage = async (): Promise<void> => {
2026-04-01 08:29:39 +00:00
if (this.cannotSendMessage.value == true) return;
2024-07-22 21:07:44 +02:00
2026-09-26 13:31:36 +02:00
const idPromise = this.sendMessageFromBody(this.composingMessage.value);
this.composingMessage.value = "";
const id = await idPromise;
if (id && this.reactionFilter.value != undefined) {
this.sendReaction(id, this.reactionFilter.value, false);
}
2026-04-01 08:29:39 +00:00
};
2024-07-22 21:07:44 +02:00
2026-09-26 13:31:36 +02:00
sendMessageFromBody = (body: string): Promise<string|false>=> {
let replyId: string | undefined = undefined;
if (this.replyingMessage.value) {
replyId = this.replyingMessage.value.chatMessage.id;
}
const id = this.chatViewModel.chatModel.sendMessage(body, replyId);
if (this.replyViewSelectedMessage.value == undefined)
this.replyingMessage.value = undefined;
2026-09-23 15:15:55 +02:00
2026-09-26 13:31:36 +02:00
return id;
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 (
2026-09-26 13:31:36 +02:00
messageViewModel: ChatMessageViewModel,
2026-04-01 08:29:39 +00:00
): Promise<void> => {
2026-09-26 13:31:36 +02:00
const chatMessage: ChatMessage = messageViewModel.chatMessage;
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
this.chatViewModel.chatModel.addMessage(chatMessage);
messageViewModel.loadData();
2026-04-01 08:29:39 +00:00
};
2024-07-22 21:07:44 +02:00
sendReaction = (
2026-09-26 13:31:36 +02:00
messageId: string,
content: ReactionSymbols,
isDeleting: boolean,
): void => {
2026-09-26 13:31:36 +02:00
this.chatViewModel.chatModel.sendReaction(
messageId,
content,
isDeleting,
);
2026-04-06 20:47:49 +00:00
};
2026-09-23 00:09:09 +02:00
setReply = (
2026-09-26 13:31:36 +02:00
chatMessageViewModel: ChatMessageViewModel | undefined,
2026-09-23 15:30:21 +02:00
setFocus: boolean = true,
2026-09-23 00:09:09 +02:00
): void => {
2026-09-26 13:31:36 +02:00
this.replyingMessage.value = chatMessageViewModel;
2026-09-23 15:30:21 +02:00
if (setFocus == false || chatMessageViewModel == undefined) return;
2026-09-26 13:31:36 +02:00
this.setFocus();
2026-04-15 12:47:10 +00:00
};
2026-09-23 15:30:21 +02:00
resetReply = (setFocus: boolean = true): void => {
2026-09-26 13:31:36 +02:00
this.replyingMessage.value = undefined;
2026-09-23 15:30:21 +02:00
if (setFocus == false) return;
2026-09-26 13:31:36 +02:00
this.setFocus();
2026-04-15 12:47:10 +00:00
};
2026-04-01 08:29:39 +00:00
// view
showChatMessage = (chatMessage: ChatMessage): void => {
2026-09-26 13:31:36 +02:00
const chatMessageViewModel = new ChatMessageViewModel(
this.coreViewModel,
this,
chatMessage,
chatMessage.sender ==
this.chatViewModel.settingsViewModel.username.value,
);
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,
);
}
2026-04-01 08:29:39 +00:00
};
2026-04-07 18:48:01 +00:00
handleReaction = (reaction: ChatMessageReaction): void => {
2026-09-26 13:31:36 +02:00
const messageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(reaction.messageId);
if (messageViewModel == undefined) return;
messageViewModel.handleReaction(reaction);
2026-04-06 20:47:49 +00:00
};
2026-04-12 19:20:37 +00:00
showFilterModal = (): void => {
2026-09-26 13:31:36 +02:00
this.isFilterModalOpen.value = true;
2026-04-11 19:26:13 +00:00
};
2026-04-12 19:20:37 +00:00
hideFilterModal = (): void => {
2026-09-26 13:31:36 +02:00
this.isFilterModalOpen.value = false;
};
2026-09-24 02:16:07 +02:00
revokeReactionFilter = (persist: boolean = true): void => {
2026-09-26 13:31:36 +02:00
this.reactionFilter.value = undefined;
2026-09-24 02:16:07 +02:00
if (persist) this.chatViewModel.chatModel.storeFilter("");
};
2026-04-11 21:01:23 +00:00
setReactionFilter = (content: ReactionSymbols): void => {
2026-09-26 13:31:36 +02:00
this.reactionFilter.value = content;
2026-09-24 02:11:03 +02:00
this.chatViewModel.chatModel.storeFilter(content);
2026-04-11 21:01:23 +00:00
};
2026-04-12 19:20:37 +00:00
resetFilter = (): void => {
2026-09-26 13:31:36 +02:00
this.revokeReactionFilter();
this.searchViewModel.search("");
2026-04-12 19:20:37 +00:00
};
2026-09-23 15:15:55 +02:00
setReplyView = (message: ChatMessageViewModel): void => {
this.replyViewSelectedMessage.value = message;
2026-09-24 02:16:07 +02:00
this.revokeReactionFilter(false);
2026-09-23 15:30:21 +02:00
this.setReply(message, false);
2026-09-23 15:15:55 +02:00
}
2026-09-23 14:44:34 +02:00
resetReplyView = (): void => {
this.replyViewSelectedMessage.value = undefined;
2026-09-23 15:30:21 +02:00
this.resetReply(false);
2026-09-24 02:16:07 +02:00
this.restoreFilter();
2026-09-23 14:44:34 +02:00
}
2026-04-15 12:47:10 +00:00
setFocus = (): void => {
2026-09-26 13:31:36 +02:00
this.focusSetter.callSubscriptions();
2026-04-15 12:47:10 +00:00
};
2026-04-01 08:29:39 +00:00
// load
loadData = (): void => {
2026-09-26 13:31:36 +02:00
this.chatMessageViewModels.clear();
for (const chatMessage of this.chatViewModel.chatModel.messages) {
this.showChatMessage(chatMessage);
}
for (const reaction of this.chatViewModel.chatModel.reactions) {
this.handleReaction(reaction);
}
2026-04-01 08:29:39 +00:00
};
2024-07-22 21:07:44 +02:00
2026-09-24 02:16:07 +02:00
restoreFilter = (): void => {
const previousFilter = this.chatViewModel.chatModel.getFilter() as any;
if (Object.values(ReactionSymbols).includes(previousFilter)) this.setReactionFilter(previousFilter);
}
2026-04-01 08:29:39 +00:00
// init
constructor(
2026-09-26 13:31:36 +02:00
public readonly coreViewModel: CoreViewModel,
public readonly chatViewModel: ChatViewModel,
2026-04-01 08:29:39 +00:00
) {
2026-09-26 13:31:36 +02:00
super("message-page");
2026-09-24 02:16:07 +02:00
this.restoreFilter();
2026-04-12 19:20:37 +00:00
2026-09-26 13:31:36 +02:00
// states
this.cannotSendMessage = React.createProxyState(
[
this.chatViewModel.settingsViewModel.username,
this.composingMessage,
],
() =>
this.chatViewModel.settingsViewModel.username.value == "" ||
this.composingMessage.value == "",
);
this.searchViewModel = new SearchViewModel(
this.chatMessageViewModels,
this.filteredMessageViewModels,
(chatMessageViewModel) => [chatMessageViewModel.body.value],
new React.ListState(),
);
this.isFilterActive = React.createProxyState(
[this.searchViewModel.appliedQuery, this.reactionFilter],
() =>
this.searchViewModel.appliedQuery.value != "" ||
this.reactionFilter.value != undefined,
);
// keystrokes
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
if (this.isFilterModalOpen.value == true) {
this.hideFilterModal();
} else {
this.replyingMessage.value = undefined;
}
});
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
2026-04-01 08:29:39 +00:00
}
2024-07-22 21:07:44 +02:00
}