2024-07-20 04:17:10 +02:00
|
|
|
import * as React from "bloatless-react";
|
|
|
|
|
|
2024-07-22 02:05:27 +02:00
|
|
|
import ChatModel, { ChatMessage } from "../Model/Chat/chatModel";
|
2024-07-22 03:01:04 +02:00
|
|
|
import StorageModel, { filePaths } from "../Model/Global/storageModel";
|
2024-07-20 16:34:16 +02:00
|
|
|
|
2024-07-20 14:16:26 +02:00
|
|
|
import ChatListViewModel from "./chatListViewModel";
|
2024-07-22 01:20:27 +02:00
|
|
|
import ChatMessageViewModel from "./chatMessageViewModel";
|
2024-07-20 18:10:42 +02:00
|
|
|
import { Color } from "./colors";
|
2024-07-20 21:04:41 +02:00
|
|
|
import SettingsViewModel from "./settingsViewModel";
|
2024-07-20 18:58:25 +02:00
|
|
|
import { localeCompare } from "../Model/Utility/utility";
|
2024-07-20 04:17:10 +02:00
|
|
|
|
|
|
|
|
export default class ChatViewModel {
|
2024-07-20 14:16:26 +02:00
|
|
|
chatModel: ChatModel;
|
2024-07-20 16:34:16 +02:00
|
|
|
storageModel: StorageModel;
|
2024-07-20 21:04:41 +02:00
|
|
|
settingsViewModel: SettingsViewModel;
|
2024-07-20 20:42:43 +02:00
|
|
|
chatListViewModel: ChatListViewModel;
|
2024-07-20 04:17:10 +02:00
|
|
|
|
2024-07-20 14:16:26 +02:00
|
|
|
// state
|
2024-07-20 16:49:01 +02:00
|
|
|
index: React.State<number> = new React.State(0);
|
|
|
|
|
|
2024-07-20 14:16:26 +02:00
|
|
|
primaryChannel: React.State<string> = new React.State("");
|
|
|
|
|
primaryChannelInput: React.State<string> = new React.State("");
|
|
|
|
|
|
2024-07-20 18:49:05 +02:00
|
|
|
secondaryChannels: React.ListState<string> = new React.ListState();
|
|
|
|
|
newSecondaryChannelInput: React.State<string> = new React.State("");
|
|
|
|
|
|
2024-07-20 19:18:19 +02:00
|
|
|
encryptionKeyInput: React.State<string> = new React.State("");
|
|
|
|
|
shouldShowEncryptionKey: React.State<boolean> = new React.State(false);
|
|
|
|
|
encryptionKeyInputType: React.State<"text" | "password"> =
|
|
|
|
|
React.createProxyState([this.shouldShowEncryptionKey], () =>
|
|
|
|
|
this.shouldShowEncryptionKey.value == true ? "text" : "password"
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-20 18:10:42 +02:00
|
|
|
color: React.State<Color> = new React.State<Color>(Color.Standard);
|
|
|
|
|
|
2024-07-20 16:28:00 +02:00
|
|
|
selectedPage: React.State<ChatPageType> = new React.State<ChatPageType>(
|
|
|
|
|
ChatPageType.Messages
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-21 13:13:41 +02:00
|
|
|
chatMessageViewModels: React.MapState<ChatMessageViewModel> =
|
|
|
|
|
new React.MapState();
|
2024-07-20 20:42:43 +02:00
|
|
|
composingMessage: React.State<string> = new React.State("");
|
|
|
|
|
|
2024-07-20 14:16:26 +02:00
|
|
|
// guards
|
|
|
|
|
cannotSetPrimaryChannel: React.State<boolean> = React.createProxyState(
|
|
|
|
|
[this.primaryChannel, this.primaryChannelInput],
|
|
|
|
|
() =>
|
|
|
|
|
this.primaryChannelInput.value == "" ||
|
|
|
|
|
this.primaryChannelInput.value == this.primaryChannel.value
|
|
|
|
|
);
|
2024-07-20 23:22:08 +02:00
|
|
|
cannotAddSecondaryChannel: React.State<boolean> = React.createProxyState(
|
|
|
|
|
[this.newSecondaryChannelInput],
|
2024-07-21 01:12:35 +02:00
|
|
|
() => this.newSecondaryChannelInput.value == ""
|
2024-07-20 23:22:08 +02:00
|
|
|
);
|
2024-07-20 19:18:19 +02:00
|
|
|
cannotSetEncryptionKey: React.State<boolean>;
|
2024-07-20 21:04:41 +02:00
|
|
|
cannotSendMessage: React.State<boolean>;
|
2024-07-20 14:16:26 +02:00
|
|
|
|
2024-07-20 16:49:01 +02:00
|
|
|
// sorting
|
|
|
|
|
updateIndex = (): void => {
|
2024-07-20 20:42:43 +02:00
|
|
|
this.index.value = this.chatModel.index;
|
2024-07-20 16:49:01 +02:00
|
|
|
};
|
|
|
|
|
|
2024-07-20 20:42:43 +02:00
|
|
|
// view
|
2024-07-20 16:34:16 +02:00
|
|
|
open = (): void => {
|
2024-07-20 14:16:26 +02:00
|
|
|
this.chatListViewModel.openChat(this);
|
2024-07-21 21:41:19 +02:00
|
|
|
this.loadMessages();
|
2024-07-20 16:28:00 +02:00
|
|
|
};
|
2024-07-20 14:16:26 +02:00
|
|
|
|
2024-07-20 16:34:16 +02:00
|
|
|
close = (): void => {
|
2024-07-20 15:49:58 +02:00
|
|
|
this.chatListViewModel.closeChat();
|
2024-07-20 16:28:00 +02:00
|
|
|
};
|
2024-07-20 15:49:58 +02:00
|
|
|
|
2024-07-21 01:12:35 +02:00
|
|
|
// add
|
|
|
|
|
addChatMessage = (chatMessage: ChatMessage): void => {
|
2024-07-21 01:53:25 +02:00
|
|
|
const chatMessageModel = new ChatMessageViewModel(
|
2024-07-21 13:13:41 +02:00
|
|
|
this,
|
2024-07-21 01:53:25 +02:00
|
|
|
chatMessage,
|
|
|
|
|
chatMessage.sender == this.settingsViewModel.username.value
|
|
|
|
|
);
|
2024-07-21 13:13:41 +02:00
|
|
|
|
|
|
|
|
const existingChatMessageViewModel: ChatMessageViewModel | undefined =
|
|
|
|
|
this.chatMessageViewModels.value.get(chatMessage.id);
|
|
|
|
|
if (existingChatMessageViewModel != undefined) {
|
|
|
|
|
existingChatMessageViewModel.body.value = chatMessage.body;
|
2024-07-21 14:15:26 +02:00
|
|
|
existingChatMessageViewModel.status.value = chatMessage.status;
|
2024-07-21 13:13:41 +02:00
|
|
|
} else {
|
|
|
|
|
this.chatMessageViewModels.set(chatMessage.id, chatMessageModel);
|
|
|
|
|
}
|
2024-07-21 01:12:35 +02:00
|
|
|
};
|
|
|
|
|
|
2024-07-20 20:42:43 +02:00
|
|
|
// settings
|
2024-07-20 17:30:36 +02:00
|
|
|
setPrimaryChannel = (): void => {
|
|
|
|
|
this.chatModel.setPrimaryChannel(this.primaryChannelInput.value);
|
|
|
|
|
this.primaryChannel.value = this.chatModel.info.primaryChannel;
|
|
|
|
|
this.chatListViewModel.updateIndices();
|
2024-07-20 18:49:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
addSecondaryChannel = (): void => {
|
|
|
|
|
this.secondaryChannels.add(this.newSecondaryChannelInput.value);
|
|
|
|
|
this.newSecondaryChannelInput.value = "";
|
|
|
|
|
this.storeSecondaryChannels();
|
2024-07-21 21:41:19 +02:00
|
|
|
this.loadSecondaryChannels();
|
2024-07-20 18:49:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
removeSecondaryChannel = (secondaryChannel: string): void => {
|
|
|
|
|
this.secondaryChannels.remove(secondaryChannel);
|
|
|
|
|
this.storeSecondaryChannels();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
storeSecondaryChannels = (): void => {
|
|
|
|
|
this.chatModel.setSecondaryChannels([
|
|
|
|
|
...this.secondaryChannels.value.values(),
|
|
|
|
|
]);
|
2024-07-20 18:58:25 +02:00
|
|
|
};
|
2024-07-20 17:30:36 +02:00
|
|
|
|
2024-07-20 19:18:19 +02:00
|
|
|
setEncryptionKey = (): void => {
|
|
|
|
|
this.chatModel.setEncryptionKey(this.encryptionKeyInput.value);
|
|
|
|
|
|
|
|
|
|
// disable button
|
|
|
|
|
this.encryptionKeyInput.callSubscriptions();
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-20 18:10:42 +02:00
|
|
|
setColor = (newColor: Color): void => {
|
|
|
|
|
this.color.value = newColor;
|
|
|
|
|
this.chatModel.setColor(newColor);
|
2024-07-20 18:49:05 +02:00
|
|
|
};
|
2024-07-20 18:10:42 +02:00
|
|
|
|
2024-07-20 20:48:13 +02:00
|
|
|
remove = (): void => {
|
|
|
|
|
this.close();
|
|
|
|
|
this.chatModel.delete();
|
2024-07-21 21:41:19 +02:00
|
|
|
this.chatListViewModel.loadChats();
|
2024-07-20 21:04:41 +02:00
|
|
|
};
|
2024-07-20 20:48:13 +02:00
|
|
|
|
2024-07-20 20:53:48 +02:00
|
|
|
// messaging
|
|
|
|
|
sendMessage = (): void => {
|
2024-07-21 14:15:26 +02:00
|
|
|
if (this.cannotSendMessage.value == true) return;
|
2024-07-22 03:01:04 +02:00
|
|
|
|
2024-07-21 13:13:41 +02:00
|
|
|
this.sendMessageFromBody(this.composingMessage.value);
|
2024-07-20 20:53:48 +02:00
|
|
|
this.composingMessage.value = "";
|
2024-07-20 21:04:41 +02:00
|
|
|
};
|
2024-07-20 20:53:48 +02:00
|
|
|
|
2024-07-21 13:13:41 +02:00
|
|
|
sendMessageFromBody = (body: string): void => {
|
|
|
|
|
this.chatModel.sendMessage(body);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
decryptMessage = async (
|
|
|
|
|
messageViewModel: ChatMessageViewModel
|
|
|
|
|
): Promise<void> => {
|
2024-07-21 14:15:26 +02:00
|
|
|
const chatMessage: ChatMessage = messageViewModel.chatMessage;
|
|
|
|
|
await this.chatModel.decryptMessage(chatMessage);
|
|
|
|
|
this.chatModel.addMessage(chatMessage);
|
2024-07-21 13:13:41 +02:00
|
|
|
messageViewModel.loadData();
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-21 21:41:19 +02:00
|
|
|
// load
|
|
|
|
|
loadPageSelection = (): void => {
|
2024-07-22 03:01:04 +02:00
|
|
|
const path: string[] = StorageModel.getPath(
|
|
|
|
|
"chat",
|
|
|
|
|
filePaths.chat.lastUsedPage(this.chatModel.id)
|
|
|
|
|
);
|
2024-07-21 21:41:19 +02:00
|
|
|
const lastUsedPage: string | null = this.storageModel.read(path);
|
2024-07-20 16:34:16 +02:00
|
|
|
if (lastUsedPage != null) {
|
|
|
|
|
this.selectedPage.value = lastUsedPage as any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.selectedPage.subscribeSilent((newPage) => {
|
2024-07-21 21:41:19 +02:00
|
|
|
this.storageModel.write(path, newPage);
|
2024-07-20 16:34:16 +02:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-21 21:41:19 +02:00
|
|
|
loadSecondaryChannels = (): void => {
|
2024-07-20 18:58:25 +02:00
|
|
|
this.secondaryChannels.clear();
|
2024-07-20 19:18:19 +02:00
|
|
|
for (const secondaryChannel of this.chatModel.info.secondaryChannels.sort(
|
|
|
|
|
localeCompare
|
|
|
|
|
)) {
|
2024-07-20 18:58:25 +02:00
|
|
|
this.secondaryChannels.add(secondaryChannel);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-21 21:41:19 +02:00
|
|
|
loadMessages = (): void => {
|
2024-07-21 14:15:26 +02:00
|
|
|
for (const chatMessage of this.chatModel.messages) {
|
|
|
|
|
this.addChatMessage(chatMessage);
|
|
|
|
|
}
|
2024-07-22 03:01:04 +02:00
|
|
|
};
|
2024-07-21 14:15:26 +02:00
|
|
|
|
2024-07-20 14:16:26 +02:00
|
|
|
// init
|
2024-07-20 20:42:43 +02:00
|
|
|
constructor(
|
|
|
|
|
chatModel: ChatModel,
|
|
|
|
|
storageModel: StorageModel,
|
2024-07-20 21:04:41 +02:00
|
|
|
settingsViewModel: SettingsViewModel,
|
2024-07-20 20:42:43 +02:00
|
|
|
chatListViewModel: ChatListViewModel
|
|
|
|
|
) {
|
2024-07-21 01:12:35 +02:00
|
|
|
// models
|
2024-07-20 14:16:26 +02:00
|
|
|
this.chatModel = chatModel;
|
2024-07-20 20:42:43 +02:00
|
|
|
this.storageModel = storageModel;
|
2024-07-20 21:04:41 +02:00
|
|
|
this.settingsViewModel = settingsViewModel;
|
2024-07-20 14:16:26 +02:00
|
|
|
this.chatListViewModel = chatListViewModel;
|
|
|
|
|
|
2024-07-21 01:12:35 +02:00
|
|
|
// handlers
|
|
|
|
|
chatModel.setMessageHandler((chatMessage) => {
|
|
|
|
|
this.addChatMessage(chatMessage);
|
|
|
|
|
});
|
|
|
|
|
|
2024-07-21 21:41:19 +02:00
|
|
|
// load
|
2024-07-20 14:16:26 +02:00
|
|
|
this.primaryChannel.value = chatModel.info.primaryChannel;
|
|
|
|
|
this.primaryChannelInput.value = chatModel.info.primaryChannel;
|
2024-07-20 18:49:05 +02:00
|
|
|
|
2024-07-20 19:18:19 +02:00
|
|
|
this.encryptionKeyInput.value = chatModel.info.encryptionKey;
|
|
|
|
|
this.cannotSetEncryptionKey = React.createProxyState(
|
|
|
|
|
[this.encryptionKeyInput],
|
|
|
|
|
() => this.encryptionKeyInput.value == this.chatModel.info.encryptionKey
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-21 01:12:35 +02:00
|
|
|
this.color.value = chatModel.color;
|
|
|
|
|
|
2024-07-21 21:41:19 +02:00
|
|
|
this.loadSecondaryChannels();
|
|
|
|
|
this.loadPageSelection();
|
2024-07-21 01:12:35 +02:00
|
|
|
this.updateIndex();
|
|
|
|
|
|
|
|
|
|
// guards
|
2024-07-20 21:04:41 +02:00
|
|
|
this.cannotSendMessage = React.createProxyState(
|
2024-07-20 21:15:06 +02:00
|
|
|
[this.settingsViewModel.username, this.composingMessage],
|
|
|
|
|
() =>
|
|
|
|
|
this.settingsViewModel.username.value == "" ||
|
|
|
|
|
this.composingMessage.value == ""
|
2024-07-20 21:04:41 +02:00
|
|
|
);
|
2024-07-20 14:16:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-07-20 16:28:00 +02:00
|
|
|
|
|
|
|
|
// types
|
|
|
|
|
export enum ChatPageType {
|
2024-07-20 16:34:16 +02:00
|
|
|
Settings = "settings",
|
|
|
|
|
Messages = "messages",
|
|
|
|
|
AllObjects = "all",
|
|
|
|
|
Kanban = "kanban",
|
|
|
|
|
Calendar = "calendar",
|
|
|
|
|
Progress = "progress",
|
2024-07-20 16:28:00 +02:00
|
|
|
}
|