remodel ChatViewModel
This commit is contained in:
@@ -5,6 +5,7 @@ import FileModel, { FileContent } from "../Files/fileModel";
|
||||
import StorageModel, { filePaths } from "../Global/storageModel";
|
||||
import {
|
||||
createTimestamp,
|
||||
localeCompare,
|
||||
parseValidObject,
|
||||
stringify,
|
||||
} from "../Utility/utility";
|
||||
@@ -31,6 +32,10 @@ export default class ChatModel {
|
||||
|
||||
chatMessageHandler: (chatMessage: ChatMessage) => void = () => {};
|
||||
|
||||
get secondaryChannels(): string[] {
|
||||
return this.info.secondaryChannels.sort(localeCompare);
|
||||
}
|
||||
|
||||
// paths
|
||||
getBasePath = (): string[] => {
|
||||
return StorageModel.getPath("chat", filePaths.chat.chatBase(this.id));
|
||||
|
||||
@@ -3,15 +3,17 @@ import "./messagePage.css";
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import { ChatMessageViewModelToView } from "../Components/chatMessage";
|
||||
import ChatViewModel from "../../ViewModel/Chat/chatViewModel";
|
||||
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
||||
import { translations } from "../translations";
|
||||
|
||||
export function MessagePage(chatViewModel: ChatViewModel) {
|
||||
export function MessagePage(messagePageViewModel: MessagePageViewModel) {
|
||||
messagePageViewModel.loadMessages();
|
||||
|
||||
const messageContainer = (
|
||||
<div
|
||||
id="message-container"
|
||||
children:append={[
|
||||
chatViewModel.chatMessageViewModels,
|
||||
messagePageViewModel.chatMessageViewModels,
|
||||
ChatMessageViewModelToView,
|
||||
]}
|
||||
></div>
|
||||
@@ -28,14 +30,16 @@ export function MessagePage(chatViewModel: ChatViewModel) {
|
||||
|
||||
scrollDown();
|
||||
}
|
||||
chatViewModel.chatMessageViewModels.subscribeSilent(scrollDownIfApplicable);
|
||||
messagePageViewModel.chatMessageViewModels.subscribeSilent(
|
||||
scrollDownIfApplicable
|
||||
);
|
||||
setTimeout(() => scrollDown(), 100);
|
||||
|
||||
return (
|
||||
<div id="message-page">
|
||||
<div class="pane">
|
||||
<div class="toolbar">
|
||||
<span subscribe:innerText={chatViewModel.primaryChannel}></span>
|
||||
<span>{translations.chatPage.message.messagesHeadline}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
{messageContainer}
|
||||
@@ -43,8 +47,8 @@ export function MessagePage(chatViewModel: ChatViewModel) {
|
||||
<div class="content-width-constraint">
|
||||
<div class="input-width-constraint">
|
||||
<input
|
||||
bind:value={chatViewModel.composingMessage}
|
||||
on:enter={chatViewModel.sendMessage}
|
||||
bind:value={messagePageViewModel.composingMessage}
|
||||
on:enter={messagePageViewModel.sendMessage}
|
||||
placeholder={
|
||||
translations.chatPage.message.composerInputPlaceholder
|
||||
}
|
||||
@@ -54,8 +58,8 @@ export function MessagePage(chatViewModel: ChatViewModel) {
|
||||
aria-label={
|
||||
translations.chatPage.message.sendMessageButtonAudioLabel
|
||||
}
|
||||
on:click={chatViewModel.sendMessage}
|
||||
toggle:disabled={chatViewModel.cannotSendMessage}
|
||||
on:click={messagePageViewModel.sendMessage}
|
||||
toggle:disabled={messagePageViewModel.cannotSendMessage}
|
||||
>
|
||||
<span class="icon">send</span>
|
||||
</button>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import ChatViewModel from "../../ViewModel/chatViewModel";
|
||||
import { Color } from "../../colors";
|
||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||
import { DeletableListItem } from "../Components/deletableListItem";
|
||||
import SettingsPageViewModel from "../../ViewModel/Pages/settingsPageViewModel";
|
||||
import { translations } from "../translations";
|
||||
|
||||
export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
settingsPageViewModel.loadData();
|
||||
|
||||
const secondaryChannelConverter: React.StateItemConverter<string> = (
|
||||
secondaryChannel: string
|
||||
) => {
|
||||
return DeletableListItem(secondaryChannel, <span></span>, () => {
|
||||
chatViewModel.removeSecondaryChannel(secondaryChannel);
|
||||
settingsPageViewModel.removeSecondaryChannel(secondaryChannel);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -27,8 +29,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
<div>
|
||||
<span>{translations.chatPage.settings.primaryChannelLabel}</span>
|
||||
<input
|
||||
bind:value={chatViewModel.primaryChannelInput}
|
||||
on:enter={chatViewModel.setPrimaryChannel}
|
||||
bind:value={settingsPageViewModel.primaryChannelInput}
|
||||
on:enter={settingsPageViewModel.setPrimaryChannel}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -38,8 +40,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
aria-label={
|
||||
translations.chatPage.settings.setPrimaryChannelButtonAudioLabel
|
||||
}
|
||||
on:click={chatViewModel.setPrimaryChannel}
|
||||
toggle:disabled={chatViewModel.cannotSetPrimaryChannel}
|
||||
on:click={settingsPageViewModel.setPrimaryChannel}
|
||||
toggle:disabled={settingsPageViewModel.cannotSetPrimaryChannel}
|
||||
>
|
||||
{translations.general.setButton}
|
||||
<span class="icon">check</span>
|
||||
@@ -56,8 +58,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
placeholder={
|
||||
translations.chatPage.settings.newSecondaryChannelPlaceholder
|
||||
}
|
||||
bind:value={chatViewModel.newSecondaryChannelInput}
|
||||
on:enter={chatViewModel.addSecondaryChannel}
|
||||
bind:value={settingsPageViewModel.newSecondaryChannelInput}
|
||||
on:enter={settingsPageViewModel.addSecondaryChannel}
|
||||
></input>
|
||||
<button
|
||||
class="primary"
|
||||
@@ -65,8 +67,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
translations.chatPage.settings
|
||||
.addSecondaryChannelButtonAudioLabel
|
||||
}
|
||||
on:click={chatViewModel.addSecondaryChannel}
|
||||
toggle:disabled={chatViewModel.cannotAddSecondaryChannel}
|
||||
on:click={settingsPageViewModel.addSecondaryChannel}
|
||||
toggle:disabled={settingsPageViewModel.cannotAddSecondaryChannel}
|
||||
>
|
||||
<span class="icon">add</span>
|
||||
</button>
|
||||
@@ -75,7 +77,7 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
<div
|
||||
class="flex-column gap width-input"
|
||||
children:append={[
|
||||
chatViewModel.secondaryChannels,
|
||||
settingsPageViewModel.secondaryChannels,
|
||||
secondaryChannelConverter,
|
||||
]}
|
||||
></div>
|
||||
@@ -87,9 +89,9 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
<div>
|
||||
<span>{translations.chatPage.settings.encryptionKeyLabel}</span>
|
||||
<input
|
||||
bind:value={chatViewModel.encryptionKeyInput}
|
||||
on:enter={chatViewModel.setEncryptionKey}
|
||||
set:type={chatViewModel.encryptionKeyInputType}
|
||||
bind:value={settingsPageViewModel.encryptionKeyInput}
|
||||
on:enter={settingsPageViewModel.setEncryptionKey}
|
||||
set:type={settingsPageViewModel.encryptionKeyInputType}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -99,8 +101,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
aria-label={
|
||||
translations.chatPage.settings.setEncryptionKeyButtonAudioLabel
|
||||
}
|
||||
on:click={chatViewModel.setEncryptionKey}
|
||||
toggle:disabled={chatViewModel.cannotSetEncryptionKey}
|
||||
on:click={settingsPageViewModel.setEncryptionKey}
|
||||
toggle:disabled={settingsPageViewModel.cannotSetEncryptionKey}
|
||||
>
|
||||
{translations.general.setButton}
|
||||
<span class="icon">check</span>
|
||||
@@ -110,7 +112,7 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
<label class="inline">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={chatViewModel.shouldShowEncryptionKey}
|
||||
bind:checked={settingsPageViewModel.shouldShowEncryptionKey}
|
||||
></input>
|
||||
{translations.chatPage.settings.showEncryptionKey}
|
||||
</label>
|
||||
@@ -120,12 +122,12 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
<div class="flex-row gap width-input">
|
||||
{...Object.values(Color).map((color) => {
|
||||
const isSelected = React.createProxyState(
|
||||
[chatViewModel.color],
|
||||
() => chatViewModel.color.value == color
|
||||
[settingsPageViewModel.color],
|
||||
() => settingsPageViewModel.color.value == color
|
||||
);
|
||||
|
||||
function setColor() {
|
||||
chatViewModel.setColor(color);
|
||||
settingsPageViewModel.setColor(color);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -146,7 +148,7 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
||||
{DangerousActionButton(
|
||||
translations.chatPage.settings.deleteChatButton,
|
||||
"chat_error",
|
||||
chatViewModel.remove
|
||||
settingsPageViewModel.remove
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import TaskViewModel from "../../ViewModel/Files/taskViewModel";
|
||||
import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
||||
import { translations } from "../translations";
|
||||
|
||||
export function TaskPage(taskViewModel: TaskViewModel) {
|
||||
export function TaskPage(taskPageViewModel: TaskPageViewModel) {
|
||||
return (
|
||||
<div id="task-page">
|
||||
<div class="pane side">
|
||||
<div class="content">
|
||||
<div class="flex-row width-input">
|
||||
<input
|
||||
bind:value={taskViewModel.newBoardNameInput}
|
||||
on:enter={taskViewModel.createBoard}
|
||||
bind:value={taskPageViewModel.newBoardNameInput}
|
||||
on:enter={taskPageViewModel.createBoard}
|
||||
placeholder={translations.chatPage.task.newBoardNamePlaceholder}
|
||||
></input>
|
||||
<button
|
||||
@@ -19,8 +19,8 @@ export function TaskPage(taskViewModel: TaskViewModel) {
|
||||
aria-label={
|
||||
translations.chatPage.task.createBoardButtonAudioLabel
|
||||
}
|
||||
on:click={taskViewModel.createBoard}
|
||||
toggle:disabled={taskViewModel.cannotCreateBoard}
|
||||
on:click={taskPageViewModel.createBoard}
|
||||
toggle:disabled={taskPageViewModel.cannotCreateBoard}
|
||||
>
|
||||
<span class="icon">add</span>
|
||||
</button>
|
||||
|
||||
@@ -6,12 +6,18 @@ import ChatViewModel from "../../ViewModel/Chat/chatViewModel";
|
||||
|
||||
export function ChatEntry(chatViewModel: ChatViewModel) {
|
||||
const view = (
|
||||
<button set:color={chatViewModel.color} class="chat-entry tile" on:click={chatViewModel.open}>
|
||||
<button
|
||||
set:color={chatViewModel.settingsPageViewModel.color}
|
||||
class="chat-entry tile"
|
||||
on:click={chatViewModel.open}
|
||||
>
|
||||
<span
|
||||
class="shadow"
|
||||
subscribe:innerText={chatViewModel.primaryChannel}
|
||||
subscribe:innerText={chatViewModel.settingsPageViewModel.primaryChannel}
|
||||
></span>
|
||||
<h2 subscribe:innerText={chatViewModel.primaryChannel}></h2>
|
||||
<h2
|
||||
subscribe:innerText={chatViewModel.settingsPageViewModel.primaryChannel}
|
||||
></h2>
|
||||
</button>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import ChatMessageViewModel from "../../Model/chatMessageViewModel";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
import { translations } from "../translations";
|
||||
|
||||
export function ChatMessageInfoModal(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import ChatViewModel, { ChatPageType } from "../../ViewModel/chatViewModel";
|
||||
import ChatViewModel, { ChatPageType } from "../../ViewModel/Chat/chatViewModel";
|
||||
|
||||
export function ChatViewToggleButton(
|
||||
label: string,
|
||||
|
||||
+12
-7
@@ -15,19 +15,24 @@ export function ChatPage(chatViewModel: ChatViewModel) {
|
||||
|
||||
chatViewModel.selectedPage.subscribe((selectedPage) => {
|
||||
switch (selectedPage) {
|
||||
case ChatPageType.Settings:
|
||||
return (mainContent.value = SettingsPage(chatViewModel));
|
||||
case ChatPageType.Tasks:
|
||||
return (mainContent.value = TaskPage(chatViewModel));
|
||||
default:
|
||||
return (mainContent.value = MessagePage(chatViewModel));
|
||||
case ChatPageType.Settings: {
|
||||
mainContent.value = SettingsPage(chatViewModel.settingsPageViewModel);
|
||||
break;
|
||||
}
|
||||
case ChatPageType.Tasks: {
|
||||
mainContent.value = TaskPage(chatViewModel.taskPageViewModel);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
mainContent.value = MessagePage(chatViewModel.messagePageViewModel);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<article
|
||||
id="chat-page"
|
||||
set:color={chatViewModel.color}
|
||||
set:color={chatViewModel.settingsPageViewModel.color}
|
||||
class="subtle-background"
|
||||
>
|
||||
<div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import "./chatPage.css";
|
||||
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import ChatListViewModel from "../ViewModel/chatListViewModel";
|
||||
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||
import { ChatPage } from "./chatPage";
|
||||
|
||||
export function ChatPageWrapper(chatListViewModel: ChatListViewModel) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from "bloatless-react";
|
||||
|
||||
import { Option, StringToOption } from "./Components/option";
|
||||
|
||||
import ChatListViewModel from "../ViewModel/chatListViewModel";
|
||||
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
|
||||
import ConnectionViewModel from "../ViewModel/Global/connectionViewModel";
|
||||
import SettingsViewModel from "../ViewModel/Global/settingsViewModel";
|
||||
|
||||
@@ -104,6 +104,8 @@ const englishTranslations = {
|
||||
},
|
||||
|
||||
message: {
|
||||
messagesHeadline: "Messages",
|
||||
|
||||
composerInputPlaceholder: "Type a message...",
|
||||
sendMessageButtonAudioLabel: "send message",
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@ import * as React from "bloatless-react";
|
||||
import { ChatMessage, ChatMessageStatus } from "../../Model/Chat/chatModel";
|
||||
|
||||
import ChatViewModel from "./chatViewModel";
|
||||
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
||||
|
||||
export default class ChatMessageViewModel {
|
||||
chatViewModel: ChatViewModel;
|
||||
messagePageViewModel: MessagePageViewModel;
|
||||
|
||||
// data
|
||||
chatMessage: ChatMessage;
|
||||
@@ -25,10 +26,10 @@ export default class ChatMessageViewModel {
|
||||
navigator.clipboard.writeText(this.body.value);
|
||||
};
|
||||
resendMessage = (): void => {
|
||||
this.chatViewModel.sendMessageFromBody(this.body.value);
|
||||
this.messagePageViewModel.sendMessageFromBody(this.body.value);
|
||||
};
|
||||
decryptMessage = (): void => {
|
||||
this.chatViewModel.decryptMessage(this);
|
||||
this.messagePageViewModel.decryptMessage(this);
|
||||
};
|
||||
|
||||
// load
|
||||
@@ -42,11 +43,11 @@ export default class ChatMessageViewModel {
|
||||
|
||||
// init
|
||||
constructor(
|
||||
chatViewModel: ChatViewModel,
|
||||
messagePageViewModel: MessagePageViewModel,
|
||||
chatMessage: ChatMessage,
|
||||
sentByUser: boolean
|
||||
) {
|
||||
this.chatViewModel = chatViewModel;
|
||||
this.messagePageViewModel = messagePageViewModel;
|
||||
|
||||
this.chatMessage = chatMessage;
|
||||
this.sentByUser = sentByUser;
|
||||
|
||||
@@ -4,10 +4,10 @@ import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
|
||||
import StorageModel, { filePaths } from "../../Model/Global/storageModel";
|
||||
|
||||
import ChatListViewModel from "./chatListViewModel";
|
||||
import ChatMessageViewModel from "./chatMessageViewModel";
|
||||
import { Color } from "../../colors";
|
||||
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
||||
import SettingsPageViewModel from "../Pages/settingsPageViewModel";
|
||||
import SettingsViewModel from "../Global/settingsViewModel";
|
||||
import { localeCompare } from "../../Model/Utility/utility";
|
||||
import TaskPageViewModel from "../Pages/taskPageViewModel";
|
||||
|
||||
export default class ChatViewModel {
|
||||
chatModel: ChatModel;
|
||||
@@ -15,46 +15,17 @@ export default class ChatViewModel {
|
||||
settingsViewModel: SettingsViewModel;
|
||||
chatListViewModel: ChatListViewModel;
|
||||
|
||||
taskPageViewModel: TaskPageViewModel;
|
||||
messagePageViewModel: MessagePageViewModel;
|
||||
settingsPageViewModel: SettingsPageViewModel;
|
||||
|
||||
// state
|
||||
index: React.State<number> = new React.State(0);
|
||||
|
||||
primaryChannel: React.State<string> = new React.State("");
|
||||
primaryChannelInput: React.State<string> = new React.State("");
|
||||
|
||||
secondaryChannels: React.ListState<string> = new React.ListState();
|
||||
newSecondaryChannelInput: React.State<string> = new React.State("");
|
||||
|
||||
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"
|
||||
);
|
||||
|
||||
color: React.State<Color> = new React.State<Color>(Color.Standard);
|
||||
|
||||
selectedPage: React.State<ChatPageType> = new React.State<ChatPageType>(
|
||||
ChatPageType.Messages
|
||||
);
|
||||
|
||||
chatMessageViewModels: React.MapState<ChatMessageViewModel> =
|
||||
new React.MapState();
|
||||
composingMessage: React.State<string> = new React.State("");
|
||||
|
||||
// guards
|
||||
cannotSetPrimaryChannel: React.State<boolean> = React.createProxyState(
|
||||
[this.primaryChannel, this.primaryChannelInput],
|
||||
() =>
|
||||
this.primaryChannelInput.value == "" ||
|
||||
this.primaryChannelInput.value == this.primaryChannel.value
|
||||
);
|
||||
cannotAddSecondaryChannel: React.State<boolean> = React.createProxyState(
|
||||
[this.newSecondaryChannelInput],
|
||||
() => this.newSecondaryChannelInput.value == ""
|
||||
);
|
||||
cannotSetEncryptionKey: React.State<boolean>;
|
||||
cannotSendMessage: React.State<boolean>;
|
||||
|
||||
// sorting
|
||||
updateIndex = (): void => {
|
||||
this.index.value = this.chatModel.index;
|
||||
@@ -63,95 +34,12 @@ export default class ChatViewModel {
|
||||
// view
|
||||
open = (): void => {
|
||||
this.chatListViewModel.openChat(this);
|
||||
this.loadMessages();
|
||||
};
|
||||
|
||||
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.loadSecondaryChannels();
|
||||
};
|
||||
|
||||
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.loadChats();
|
||||
};
|
||||
|
||||
// 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<void> => {
|
||||
const chatMessage: ChatMessage = messageViewModel.chatMessage;
|
||||
await this.chatModel.decryptMessage(chatMessage);
|
||||
this.chatModel.addMessage(chatMessage);
|
||||
messageViewModel.loadData();
|
||||
};
|
||||
|
||||
// load
|
||||
loadPageSelection = (): void => {
|
||||
const path: string[] = StorageModel.getPath(
|
||||
@@ -168,21 +56,6 @@ export default class ChatViewModel {
|
||||
});
|
||||
};
|
||||
|
||||
loadSecondaryChannels = (): void => {
|
||||
this.secondaryChannels.clear();
|
||||
for (const secondaryChannel of this.chatModel.info.secondaryChannels.sort(
|
||||
localeCompare
|
||||
)) {
|
||||
this.secondaryChannels.add(secondaryChannel);
|
||||
}
|
||||
};
|
||||
|
||||
loadMessages = (): void => {
|
||||
for (const chatMessage of this.chatModel.messages) {
|
||||
this.addChatMessage(chatMessage);
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(
|
||||
chatModel: ChatModel,
|
||||
@@ -196,34 +69,22 @@ export default class ChatViewModel {
|
||||
this.settingsViewModel = settingsViewModel;
|
||||
this.chatListViewModel = chatListViewModel;
|
||||
|
||||
// page viewModels
|
||||
this.taskPageViewModel = new TaskPageViewModel(
|
||||
this.chatModel.fileModel.taskModel,
|
||||
this.storageModel
|
||||
);
|
||||
this.messagePageViewModel = new MessagePageViewModel(this);
|
||||
this.settingsPageViewModel = new SettingsPageViewModel(this);
|
||||
|
||||
// handlers
|
||||
chatModel.setMessageHandler((chatMessage) => {
|
||||
this.addChatMessage(chatMessage);
|
||||
this.messagePageViewModel.addChatMessage(chatMessage);
|
||||
});
|
||||
|
||||
// load
|
||||
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.loadSecondaryChannels();
|
||||
this.loadPageSelection();
|
||||
this.updateIndex();
|
||||
|
||||
// guards
|
||||
this.cannotSendMessage = React.createProxyState(
|
||||
[this.settingsViewModel.username, this.composingMessage],
|
||||
() =>
|
||||
this.settingsViewModel.username.value == "" ||
|
||||
this.composingMessage.value == ""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import { ChatMessage } from "../../Model/Chat/chatModel";
|
||||
import ChatMessageViewModel from "../Chat/chatMessageViewModel";
|
||||
import ChatViewModel from "../Chat/chatViewModel";
|
||||
|
||||
export default class MessagePageViewModel {
|
||||
chatViewModel: ChatViewModel;
|
||||
|
||||
// state
|
||||
chatMessageViewModels: React.MapState<ChatMessageViewModel> =
|
||||
new React.MapState();
|
||||
composingMessage: React.State<string> = new React.State("");
|
||||
|
||||
// guards
|
||||
cannotSendMessage: React.State<boolean>;
|
||||
|
||||
// methods
|
||||
addChatMessage = (chatMessage: ChatMessage): void => {
|
||||
const chatMessageModel = new ChatMessageViewModel(
|
||||
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, chatMessageModel);
|
||||
}
|
||||
};
|
||||
|
||||
// messaging
|
||||
sendMessage = (): void => {
|
||||
if (this.cannotSendMessage.value == true) return;
|
||||
|
||||
this.sendMessageFromBody(this.composingMessage.value);
|
||||
this.composingMessage.value = "";
|
||||
};
|
||||
|
||||
sendMessageFromBody = (body: string): void => {
|
||||
this.chatViewModel.chatModel.sendMessage(body);
|
||||
};
|
||||
|
||||
decryptMessage = async (
|
||||
messageViewModel: ChatMessageViewModel
|
||||
): Promise<void> => {
|
||||
const chatMessage: ChatMessage = messageViewModel.chatMessage;
|
||||
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
|
||||
this.chatViewModel.chatModel.addMessage(chatMessage);
|
||||
messageViewModel.loadData();
|
||||
};
|
||||
|
||||
// load
|
||||
loadData = (): void => {
|
||||
this.cannotSendMessage = React.createProxyState(
|
||||
[this.chatViewModel.settingsViewModel.username, this.composingMessage],
|
||||
() =>
|
||||
this.chatViewModel.settingsViewModel.username.value == "" ||
|
||||
this.composingMessage.value == ""
|
||||
);
|
||||
};
|
||||
|
||||
loadMessages = (): void => {
|
||||
for (const chatMessage of this.chatViewModel.chatModel.messages) {
|
||||
this.addChatMessage(chatMessage);
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(chatViewModel: ChatViewModel) {
|
||||
this.chatViewModel = chatViewModel;
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import ChatViewModel from "../Chat/chatViewModel";
|
||||
import { Color } from "../../colors";
|
||||
|
||||
export default class SettingsPageViewModel {
|
||||
chatViewModel: ChatViewModel;
|
||||
|
||||
// state
|
||||
primaryChannel: React.State<string> = new React.State("");
|
||||
primaryChannelInput: React.State<string> = new React.State("");
|
||||
|
||||
secondaryChannels: React.ListState<string> = new React.ListState();
|
||||
newSecondaryChannelInput: React.State<string> = new React.State("");
|
||||
|
||||
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"
|
||||
);
|
||||
|
||||
color: React.State<Color> = new React.State<Color>(Color.Standard);
|
||||
|
||||
// guards
|
||||
cannotSetPrimaryChannel: React.State<boolean> = React.createProxyState(
|
||||
[this.primaryChannel, this.primaryChannelInput],
|
||||
() =>
|
||||
this.primaryChannelInput.value == "" ||
|
||||
this.primaryChannelInput.value == this.primaryChannel.value
|
||||
);
|
||||
cannotAddSecondaryChannel: React.State<boolean> = React.createProxyState(
|
||||
[this.newSecondaryChannelInput],
|
||||
() => this.newSecondaryChannelInput.value == ""
|
||||
);
|
||||
cannotSetEncryptionKey: React.State<boolean>;
|
||||
|
||||
// methods
|
||||
setPrimaryChannel = (): void => {
|
||||
this.chatViewModel.chatModel.setPrimaryChannel(
|
||||
this.primaryChannelInput.value
|
||||
);
|
||||
this.primaryChannel.value =
|
||||
this.chatViewModel.chatModel.info.primaryChannel;
|
||||
this.chatViewModel.chatListViewModel.updateIndices();
|
||||
};
|
||||
|
||||
addSecondaryChannel = (): void => {
|
||||
this.secondaryChannels.add(this.newSecondaryChannelInput.value);
|
||||
this.newSecondaryChannelInput.value = "";
|
||||
this.storeSecondaryChannels();
|
||||
this.loadSecondaryChannels();
|
||||
};
|
||||
|
||||
removeSecondaryChannel = (secondaryChannel: string): void => {
|
||||
this.secondaryChannels.remove(secondaryChannel);
|
||||
this.storeSecondaryChannels();
|
||||
};
|
||||
|
||||
storeSecondaryChannels = (): void => {
|
||||
this.chatViewModel.chatModel.setSecondaryChannels([
|
||||
...this.secondaryChannels.value.values(),
|
||||
]);
|
||||
};
|
||||
|
||||
setEncryptionKey = (): void => {
|
||||
this.chatViewModel.chatModel.setEncryptionKey(
|
||||
this.encryptionKeyInput.value
|
||||
);
|
||||
|
||||
// disable button
|
||||
this.encryptionKeyInput.callSubscriptions();
|
||||
};
|
||||
|
||||
setColor = (newColor: Color): void => {
|
||||
this.color.value = newColor;
|
||||
this.chatViewModel.chatModel.setColor(newColor);
|
||||
};
|
||||
|
||||
remove = (): void => {
|
||||
this.chatViewModel.close();
|
||||
this.chatViewModel.chatModel.delete();
|
||||
this.chatViewModel.chatListViewModel.loadChats();
|
||||
};
|
||||
|
||||
// load
|
||||
loadViewRelevantData = (): void => {
|
||||
this.primaryChannel.value =
|
||||
this.chatViewModel.chatModel.info.primaryChannel;
|
||||
|
||||
this.color.value = this.chatViewModel.chatModel.color;
|
||||
|
||||
this.cannotSetEncryptionKey = React.createProxyState(
|
||||
[this.encryptionKeyInput],
|
||||
() =>
|
||||
this.encryptionKeyInput.value ==
|
||||
this.chatViewModel.chatModel.info.encryptionKey
|
||||
);
|
||||
};
|
||||
|
||||
loadData = (): void => {
|
||||
this.primaryChannelInput.value =
|
||||
this.chatViewModel.chatModel.info.primaryChannel;
|
||||
|
||||
this.loadSecondaryChannels();
|
||||
|
||||
this.encryptionKeyInput.value =
|
||||
this.chatViewModel.chatModel.info.encryptionKey;
|
||||
};
|
||||
|
||||
loadSecondaryChannels = (): void => {
|
||||
this.secondaryChannels.clear();
|
||||
for (const secondaryChannel of this.chatViewModel.chatModel
|
||||
.secondaryChannels) {
|
||||
this.secondaryChannels.add(secondaryChannel);
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(chatViewModel: ChatViewModel) {
|
||||
this.chatViewModel = chatViewModel;
|
||||
this.loadViewRelevantData();
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,12 @@ import * as React from "bloatless-react";
|
||||
|
||||
import TaskModel, {
|
||||
BoardInfoFileContent,
|
||||
TaskFileContent,
|
||||
} from "../../Model/Files/taskModel";
|
||||
|
||||
import StorageModel from "../../Model/Global/storageModel";
|
||||
|
||||
export default class TaskViewModel {
|
||||
export default class TaskPageViewModel {
|
||||
taskModel: TaskModel;
|
||||
storageModel: StorageModel;
|
||||
|
||||
// state
|
||||
newBoardNameInput: React.State<string> = new React.State("");
|
||||
@@ -33,6 +31,5 @@ export default class TaskViewModel {
|
||||
// init
|
||||
constructor(taskModel: TaskModel, storageModel: StorageModel) {
|
||||
this.taskModel = taskModel;
|
||||
this.storageModel = storageModel;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user