import * as React from "bloatless-react"; import { ChatMessage, ChatModel } from "../Model/chatModel"; import StorageModel, { storageKeys } from "../Model/storageModel"; import ChatListViewModel from "./chatListViewModel"; import ChatMessageViewModel from "../Model/chatMessageViewModel"; import { Color } from "./colors"; import SettingsViewModel from "./settingsViewModel"; import { localeCompare } from "../Model/Utility/utility"; export default class ChatViewModel { chatModel: ChatModel; storageModel: StorageModel; settingsViewModel: SettingsViewModel; chatListViewModel: ChatListViewModel; // state index: React.State = new React.State(0); primaryChannel: React.State = new React.State(""); primaryChannelInput: React.State = new React.State(""); secondaryChannels: React.ListState = new React.ListState(); newSecondaryChannelInput: React.State = new React.State(""); encryptionKeyInput: React.State = new React.State(""); shouldShowEncryptionKey: React.State = new React.State(false); encryptionKeyInputType: React.State<"text" | "password"> = React.createProxyState([this.shouldShowEncryptionKey], () => this.shouldShowEncryptionKey.value == true ? "text" : "password" ); color: React.State = new React.State(Color.Standard); selectedPage: React.State = new React.State( ChatPageType.Messages ); chatMessageViewModels: React.MapState = new React.MapState(); composingMessage: React.State = new React.State(""); // guards cannotSetPrimaryChannel: React.State = React.createProxyState( [this.primaryChannel, this.primaryChannelInput], () => this.primaryChannelInput.value == "" || this.primaryChannelInput.value == this.primaryChannel.value ); cannotAddSecondaryChannel: React.State = React.createProxyState( [this.newSecondaryChannelInput], () => this.newSecondaryChannelInput.value == "" ); cannotSetEncryptionKey: React.State; cannotSendMessage: React.State; // sorting updateIndex = (): void => { this.index.value = this.chatModel.index; }; // view open = (): void => { this.chatListViewModel.openChat(this); this.restoreMessages(); }; close = (): void => { this.chatListViewModel.closeChat(); }; // add addChatMessage = (chatMessage: ChatMessage): void => { const chatMessageModel = new ChatMessageViewModel( this, chatMessage, chatMessage.sender == this.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, chatMessageModel); } }; // settings setPrimaryChannel = (): void => { this.chatModel.setPrimaryChannel(this.primaryChannelInput.value); this.primaryChannel.value = this.chatModel.info.primaryChannel; this.chatListViewModel.updateIndices(); }; addSecondaryChannel = (): void => { this.secondaryChannels.add(this.newSecondaryChannelInput.value); this.newSecondaryChannelInput.value = ""; this.storeSecondaryChannels(); this.restoreSecondaryChannels(); }; removeSecondaryChannel = (secondaryChannel: string): void => { this.secondaryChannels.remove(secondaryChannel); this.storeSecondaryChannels(); }; storeSecondaryChannels = (): void => { this.chatModel.setSecondaryChannels([ ...this.secondaryChannels.value.values(), ]); }; setEncryptionKey = (): void => { this.chatModel.setEncryptionKey(this.encryptionKeyInput.value); // disable button this.encryptionKeyInput.callSubscriptions(); }; setColor = (newColor: Color): void => { this.color.value = newColor; this.chatModel.setColor(newColor); }; remove = (): void => { this.close(); this.chatModel.delete(); this.chatListViewModel.restoreChats(); }; // messaging sendMessage = (): void => { if (this.cannotSendMessage.value == true) return; this.sendMessageFromBody(this.composingMessage.value); this.composingMessage.value = ""; }; sendMessageFromBody = (body: string): void => { this.chatModel.sendMessage(body); }; decryptMessage = async ( messageViewModel: ChatMessageViewModel ): Promise => { const chatMessage: ChatMessage = messageViewModel.chatMessage; await this.chatModel.decryptMessage(chatMessage); this.chatModel.addMessage(chatMessage); messageViewModel.loadData(); }; // restore restorePageSelection = (): void => { const path: string[] = storageKeys.chatLastUsedPage(this.chatModel.id); const lastUsedPage: string | null = this.storageModel.restore(path); if (lastUsedPage != null) { this.selectedPage.value = lastUsedPage as any; } this.selectedPage.subscribeSilent((newPage) => { this.storageModel.store(path, newPage); }); }; restoreSecondaryChannels = (): void => { this.secondaryChannels.clear(); for (const secondaryChannel of this.chatModel.info.secondaryChannels.sort( localeCompare )) { this.secondaryChannels.add(secondaryChannel); } }; restoreMessages = (): void => { for (const chatMessage of this.chatModel.messages) { this.addChatMessage(chatMessage); } } // init constructor( chatModel: ChatModel, storageModel: StorageModel, settingsViewModel: SettingsViewModel, chatListViewModel: ChatListViewModel ) { // models this.chatModel = chatModel; this.storageModel = storageModel; this.settingsViewModel = settingsViewModel; this.chatListViewModel = chatListViewModel; // handlers chatModel.setMessageHandler((chatMessage) => { this.addChatMessage(chatMessage); }); // restore this.primaryChannel.value = chatModel.info.primaryChannel; this.primaryChannelInput.value = chatModel.info.primaryChannel; this.encryptionKeyInput.value = chatModel.info.encryptionKey; this.cannotSetEncryptionKey = React.createProxyState( [this.encryptionKeyInput], () => this.encryptionKeyInput.value == this.chatModel.info.encryptionKey ); this.color.value = chatModel.color; this.restoreSecondaryChannels(); this.restorePageSelection(); this.updateIndex(); // guards this.cannotSendMessage = React.createProxyState( [this.settingsViewModel.username, this.composingMessage], () => this.settingsViewModel.username.value == "" || this.composingMessage.value == "" ); } } // types export enum ChatPageType { Settings = "settings", Messages = "messages", AllObjects = "all", Kanban = "kanban", Calendar = "calendar", Progress = "progress", }