remodel ChatViewModel
This commit is contained in:
Vendored
+241
-146
@@ -514,7 +514,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/ViewModel/colors.ts
|
// src/colors.ts
|
||||||
var Color = /* @__PURE__ */ ((Color2) => {
|
var Color = /* @__PURE__ */ ((Color2) => {
|
||||||
Color2["Standard"] = "standard";
|
Color2["Standard"] = "standard";
|
||||||
Color2["Coral"] = "coral";
|
Color2["Coral"] = "coral";
|
||||||
@@ -897,6 +897,9 @@
|
|||||||
color;
|
color;
|
||||||
chatMessageHandler = () => {
|
chatMessageHandler = () => {
|
||||||
};
|
};
|
||||||
|
get secondaryChannels() {
|
||||||
|
return this.info.secondaryChannels.sort(localeCompare);
|
||||||
|
}
|
||||||
// paths
|
// paths
|
||||||
getBasePath = () => {
|
getBasePath = () => {
|
||||||
return StorageModel.getPath("chat", filePaths.chat.chatBase(this.id));
|
return StorageModel.getPath("chat", filePaths.chat.chatBase(this.id));
|
||||||
@@ -1195,9 +1198,9 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/ViewModel/chatMessageViewModel.ts
|
// src/ViewModel/Chat/chatMessageViewModel.ts
|
||||||
var ChatMessageViewModel = class {
|
var ChatMessageViewModel = class {
|
||||||
chatViewModel;
|
messagePageViewModel;
|
||||||
// data
|
// data
|
||||||
chatMessage;
|
chatMessage;
|
||||||
channel;
|
channel;
|
||||||
@@ -1211,10 +1214,10 @@
|
|||||||
navigator.clipboard.writeText(this.body.value);
|
navigator.clipboard.writeText(this.body.value);
|
||||||
};
|
};
|
||||||
resendMessage = () => {
|
resendMessage = () => {
|
||||||
this.chatViewModel.sendMessageFromBody(this.body.value);
|
this.messagePageViewModel.sendMessageFromBody(this.body.value);
|
||||||
};
|
};
|
||||||
decryptMessage = () => {
|
decryptMessage = () => {
|
||||||
this.chatViewModel.decryptMessage(this);
|
this.messagePageViewModel.decryptMessage(this);
|
||||||
};
|
};
|
||||||
// load
|
// load
|
||||||
loadData = () => {
|
loadData = () => {
|
||||||
@@ -1225,22 +1228,75 @@
|
|||||||
this.status.value = this.chatMessage.status;
|
this.status.value = this.chatMessage.status;
|
||||||
};
|
};
|
||||||
// init
|
// init
|
||||||
constructor(chatViewModel, chatMessage, sentByUser) {
|
constructor(messagePageViewModel, chatMessage, sentByUser) {
|
||||||
this.chatViewModel = chatViewModel;
|
this.messagePageViewModel = messagePageViewModel;
|
||||||
this.chatMessage = chatMessage;
|
this.chatMessage = chatMessage;
|
||||||
this.sentByUser = sentByUser;
|
this.sentByUser = sentByUser;
|
||||||
this.loadData();
|
this.loadData();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/ViewModel/chatViewModel.ts
|
// src/ViewModel/Pages/messagePageViewModel.ts
|
||||||
var ChatViewModel = class {
|
var MessagePageViewModel = class {
|
||||||
chatModel;
|
chatViewModel;
|
||||||
storageModel;
|
// state
|
||||||
settingsViewModel;
|
chatMessageViewModels = new MapState();
|
||||||
chatListViewModel;
|
composingMessage = new State("");
|
||||||
|
// guards
|
||||||
|
cannotSendMessage;
|
||||||
|
// methods
|
||||||
|
addChatMessage = (chatMessage) => {
|
||||||
|
const chatMessageModel = new ChatMessageViewModel(
|
||||||
|
this,
|
||||||
|
chatMessage,
|
||||||
|
chatMessage.sender == this.chatViewModel.settingsViewModel.username.value
|
||||||
|
);
|
||||||
|
const existingChatMessageViewModel = this.chatMessageViewModels.value.get(chatMessage.id);
|
||||||
|
if (existingChatMessageViewModel != void 0) {
|
||||||
|
existingChatMessageViewModel.body.value = chatMessage.body;
|
||||||
|
existingChatMessageViewModel.status.value = chatMessage.status;
|
||||||
|
} else {
|
||||||
|
this.chatMessageViewModels.set(chatMessage.id, chatMessageModel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// messaging
|
||||||
|
sendMessage = () => {
|
||||||
|
if (this.cannotSendMessage.value == true) return;
|
||||||
|
this.sendMessageFromBody(this.composingMessage.value);
|
||||||
|
this.composingMessage.value = "";
|
||||||
|
};
|
||||||
|
sendMessageFromBody = (body) => {
|
||||||
|
this.chatViewModel.chatModel.sendMessage(body);
|
||||||
|
};
|
||||||
|
decryptMessage = async (messageViewModel) => {
|
||||||
|
const chatMessage = messageViewModel.chatMessage;
|
||||||
|
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
|
||||||
|
this.chatViewModel.chatModel.addMessage(chatMessage);
|
||||||
|
messageViewModel.loadData();
|
||||||
|
};
|
||||||
|
// load
|
||||||
|
loadData = () => {
|
||||||
|
this.cannotSendMessage = createProxyState(
|
||||||
|
[this.chatViewModel.settingsViewModel.username, this.composingMessage],
|
||||||
|
() => this.chatViewModel.settingsViewModel.username.value == "" || this.composingMessage.value == ""
|
||||||
|
);
|
||||||
|
};
|
||||||
|
loadMessages = () => {
|
||||||
|
for (const chatMessage of this.chatViewModel.chatModel.messages) {
|
||||||
|
this.addChatMessage(chatMessage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// init
|
||||||
|
constructor(chatViewModel) {
|
||||||
|
this.chatViewModel = chatViewModel;
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/ViewModel/Pages/settingsPageViewModel.ts
|
||||||
|
var SettingsPageViewModel = class {
|
||||||
|
chatViewModel;
|
||||||
// state
|
// state
|
||||||
index = new State(0);
|
|
||||||
primaryChannel = new State("");
|
primaryChannel = new State("");
|
||||||
primaryChannelInput = new State("");
|
primaryChannelInput = new State("");
|
||||||
secondaryChannels = new ListState();
|
secondaryChannels = new ListState();
|
||||||
@@ -1252,11 +1308,6 @@
|
|||||||
() => this.shouldShowEncryptionKey.value == true ? "text" : "password"
|
() => this.shouldShowEncryptionKey.value == true ? "text" : "password"
|
||||||
);
|
);
|
||||||
color = new State("standard" /* Standard */);
|
color = new State("standard" /* Standard */);
|
||||||
selectedPage = new State(
|
|
||||||
"messages" /* Messages */
|
|
||||||
);
|
|
||||||
chatMessageViewModels = new MapState();
|
|
||||||
composingMessage = new State("");
|
|
||||||
// guards
|
// guards
|
||||||
cannotSetPrimaryChannel = createProxyState(
|
cannotSetPrimaryChannel = createProxyState(
|
||||||
[this.primaryChannel, this.primaryChannelInput],
|
[this.primaryChannel, this.primaryChannelInput],
|
||||||
@@ -1267,39 +1318,13 @@
|
|||||||
() => this.newSecondaryChannelInput.value == ""
|
() => this.newSecondaryChannelInput.value == ""
|
||||||
);
|
);
|
||||||
cannotSetEncryptionKey;
|
cannotSetEncryptionKey;
|
||||||
cannotSendMessage;
|
// methods
|
||||||
// sorting
|
|
||||||
updateIndex = () => {
|
|
||||||
this.index.value = this.chatModel.index;
|
|
||||||
};
|
|
||||||
// view
|
|
||||||
open = () => {
|
|
||||||
this.chatListViewModel.openChat(this);
|
|
||||||
this.loadMessages();
|
|
||||||
};
|
|
||||||
close = () => {
|
|
||||||
this.chatListViewModel.closeChat();
|
|
||||||
};
|
|
||||||
// add
|
|
||||||
addChatMessage = (chatMessage) => {
|
|
||||||
const chatMessageModel = new ChatMessageViewModel(
|
|
||||||
this,
|
|
||||||
chatMessage,
|
|
||||||
chatMessage.sender == this.settingsViewModel.username.value
|
|
||||||
);
|
|
||||||
const existingChatMessageViewModel = this.chatMessageViewModels.value.get(chatMessage.id);
|
|
||||||
if (existingChatMessageViewModel != void 0) {
|
|
||||||
existingChatMessageViewModel.body.value = chatMessage.body;
|
|
||||||
existingChatMessageViewModel.status.value = chatMessage.status;
|
|
||||||
} else {
|
|
||||||
this.chatMessageViewModels.set(chatMessage.id, chatMessageModel);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// settings
|
|
||||||
setPrimaryChannel = () => {
|
setPrimaryChannel = () => {
|
||||||
this.chatModel.setPrimaryChannel(this.primaryChannelInput.value);
|
this.chatViewModel.chatModel.setPrimaryChannel(
|
||||||
this.primaryChannel.value = this.chatModel.info.primaryChannel;
|
this.primaryChannelInput.value
|
||||||
this.chatListViewModel.updateIndices();
|
);
|
||||||
|
this.primaryChannel.value = this.chatViewModel.chatModel.info.primaryChannel;
|
||||||
|
this.chatViewModel.chatListViewModel.updateIndices();
|
||||||
};
|
};
|
||||||
addSecondaryChannel = () => {
|
addSecondaryChannel = () => {
|
||||||
this.secondaryChannels.add(this.newSecondaryChannelInput.value);
|
this.secondaryChannels.add(this.newSecondaryChannelInput.value);
|
||||||
@@ -1312,37 +1337,99 @@
|
|||||||
this.storeSecondaryChannels();
|
this.storeSecondaryChannels();
|
||||||
};
|
};
|
||||||
storeSecondaryChannels = () => {
|
storeSecondaryChannels = () => {
|
||||||
this.chatModel.setSecondaryChannels([
|
this.chatViewModel.chatModel.setSecondaryChannels([
|
||||||
...this.secondaryChannels.value.values()
|
...this.secondaryChannels.value.values()
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
setEncryptionKey = () => {
|
setEncryptionKey = () => {
|
||||||
this.chatModel.setEncryptionKey(this.encryptionKeyInput.value);
|
this.chatViewModel.chatModel.setEncryptionKey(
|
||||||
|
this.encryptionKeyInput.value
|
||||||
|
);
|
||||||
this.encryptionKeyInput.callSubscriptions();
|
this.encryptionKeyInput.callSubscriptions();
|
||||||
};
|
};
|
||||||
setColor = (newColor) => {
|
setColor = (newColor) => {
|
||||||
this.color.value = newColor;
|
this.color.value = newColor;
|
||||||
this.chatModel.setColor(newColor);
|
this.chatViewModel.chatModel.setColor(newColor);
|
||||||
};
|
};
|
||||||
remove = () => {
|
remove = () => {
|
||||||
this.close();
|
this.chatViewModel.close();
|
||||||
this.chatModel.delete();
|
this.chatViewModel.chatModel.delete();
|
||||||
this.chatListViewModel.loadChats();
|
this.chatViewModel.chatListViewModel.loadChats();
|
||||||
};
|
};
|
||||||
// messaging
|
// load
|
||||||
sendMessage = () => {
|
loadViewRelevantData = () => {
|
||||||
if (this.cannotSendMessage.value == true) return;
|
this.primaryChannel.value = this.chatViewModel.chatModel.info.primaryChannel;
|
||||||
this.sendMessageFromBody(this.composingMessage.value);
|
this.color.value = this.chatViewModel.chatModel.color;
|
||||||
this.composingMessage.value = "";
|
this.cannotSetEncryptionKey = createProxyState(
|
||||||
|
[this.encryptionKeyInput],
|
||||||
|
() => this.encryptionKeyInput.value == this.chatViewModel.chatModel.info.encryptionKey
|
||||||
|
);
|
||||||
};
|
};
|
||||||
sendMessageFromBody = (body) => {
|
loadData = () => {
|
||||||
this.chatModel.sendMessage(body);
|
this.primaryChannelInput.value = this.chatViewModel.chatModel.info.primaryChannel;
|
||||||
|
this.loadSecondaryChannels();
|
||||||
|
this.encryptionKeyInput.value = this.chatViewModel.chatModel.info.encryptionKey;
|
||||||
};
|
};
|
||||||
decryptMessage = async (messageViewModel) => {
|
loadSecondaryChannels = () => {
|
||||||
const chatMessage = messageViewModel.chatMessage;
|
this.secondaryChannels.clear();
|
||||||
await this.chatModel.decryptMessage(chatMessage);
|
for (const secondaryChannel of this.chatViewModel.chatModel.secondaryChannels) {
|
||||||
this.chatModel.addMessage(chatMessage);
|
this.secondaryChannels.add(secondaryChannel);
|
||||||
messageViewModel.loadData();
|
}
|
||||||
|
};
|
||||||
|
// init
|
||||||
|
constructor(chatViewModel) {
|
||||||
|
this.chatViewModel = chatViewModel;
|
||||||
|
this.loadViewRelevantData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/ViewModel/Pages/taskPageViewModel.ts
|
||||||
|
var TaskPageViewModel = class {
|
||||||
|
taskModel;
|
||||||
|
// state
|
||||||
|
newBoardNameInput = new State("");
|
||||||
|
boards = new ListState();
|
||||||
|
// guards
|
||||||
|
cannotCreateBoard = createProxyState(
|
||||||
|
[this.newBoardNameInput],
|
||||||
|
() => this.newBoardNameInput.value == ""
|
||||||
|
);
|
||||||
|
// methods
|
||||||
|
createBoard = () => {
|
||||||
|
if (this.cannotCreateBoard.value == true) return;
|
||||||
|
this.taskModel.createBoard(this.newBoardNameInput.value);
|
||||||
|
this.newBoardNameInput.value = "";
|
||||||
|
};
|
||||||
|
// init
|
||||||
|
constructor(taskModel, storageModel2) {
|
||||||
|
this.taskModel = taskModel;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/ViewModel/Chat/chatViewModel.ts
|
||||||
|
var ChatViewModel = class {
|
||||||
|
chatModel;
|
||||||
|
storageModel;
|
||||||
|
settingsViewModel;
|
||||||
|
chatListViewModel;
|
||||||
|
taskPageViewModel;
|
||||||
|
messagePageViewModel;
|
||||||
|
settingsPageViewModel;
|
||||||
|
// state
|
||||||
|
index = new State(0);
|
||||||
|
selectedPage = new State(
|
||||||
|
"messages" /* Messages */
|
||||||
|
);
|
||||||
|
// sorting
|
||||||
|
updateIndex = () => {
|
||||||
|
this.index.value = this.chatModel.index;
|
||||||
|
};
|
||||||
|
// view
|
||||||
|
open = () => {
|
||||||
|
this.chatListViewModel.openChat(this);
|
||||||
|
};
|
||||||
|
close = () => {
|
||||||
|
this.chatListViewModel.closeChat();
|
||||||
};
|
};
|
||||||
// load
|
// load
|
||||||
loadPageSelection = () => {
|
loadPageSelection = () => {
|
||||||
@@ -1358,47 +1445,27 @@
|
|||||||
this.storageModel.write(path, newPage);
|
this.storageModel.write(path, newPage);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
loadSecondaryChannels = () => {
|
|
||||||
this.secondaryChannels.clear();
|
|
||||||
for (const secondaryChannel of this.chatModel.info.secondaryChannels.sort(
|
|
||||||
localeCompare
|
|
||||||
)) {
|
|
||||||
this.secondaryChannels.add(secondaryChannel);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
loadMessages = () => {
|
|
||||||
for (const chatMessage of this.chatModel.messages) {
|
|
||||||
this.addChatMessage(chatMessage);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// init
|
// init
|
||||||
constructor(chatModel, storageModel2, settingsViewModel2, chatListViewModel2) {
|
constructor(chatModel, storageModel2, settingsViewModel2, chatListViewModel2) {
|
||||||
this.chatModel = chatModel;
|
this.chatModel = chatModel;
|
||||||
this.storageModel = storageModel2;
|
this.storageModel = storageModel2;
|
||||||
this.settingsViewModel = settingsViewModel2;
|
this.settingsViewModel = settingsViewModel2;
|
||||||
this.chatListViewModel = chatListViewModel2;
|
this.chatListViewModel = chatListViewModel2;
|
||||||
chatModel.setMessageHandler((chatMessage) => {
|
this.taskPageViewModel = new TaskPageViewModel(
|
||||||
this.addChatMessage(chatMessage);
|
this.chatModel.fileModel.taskModel,
|
||||||
});
|
this.storageModel
|
||||||
this.primaryChannel.value = chatModel.info.primaryChannel;
|
|
||||||
this.primaryChannelInput.value = chatModel.info.primaryChannel;
|
|
||||||
this.encryptionKeyInput.value = chatModel.info.encryptionKey;
|
|
||||||
this.cannotSetEncryptionKey = createProxyState(
|
|
||||||
[this.encryptionKeyInput],
|
|
||||||
() => this.encryptionKeyInput.value == this.chatModel.info.encryptionKey
|
|
||||||
);
|
);
|
||||||
this.color.value = chatModel.color;
|
this.messagePageViewModel = new MessagePageViewModel(this);
|
||||||
this.loadSecondaryChannels();
|
this.settingsPageViewModel = new SettingsPageViewModel(this);
|
||||||
|
chatModel.setMessageHandler((chatMessage) => {
|
||||||
|
this.messagePageViewModel.addChatMessage(chatMessage);
|
||||||
|
});
|
||||||
this.loadPageSelection();
|
this.loadPageSelection();
|
||||||
this.updateIndex();
|
this.updateIndex();
|
||||||
this.cannotSendMessage = createProxyState(
|
|
||||||
[this.settingsViewModel.username, this.composingMessage],
|
|
||||||
() => this.settingsViewModel.username.value == "" || this.composingMessage.value == ""
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/ViewModel/chatListViewModel.ts
|
// src/ViewModel/Chat/chatListViewModel.ts
|
||||||
var ChatListViewModel = class {
|
var ChatListViewModel = class {
|
||||||
chatListModel;
|
chatListModel;
|
||||||
storageModel;
|
storageModel;
|
||||||
@@ -1565,6 +1632,7 @@
|
|||||||
deleteChatButton: "Delete entire chat"
|
deleteChatButton: "Delete entire chat"
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
|
messagesHeadline: "Messages",
|
||||||
composerInputPlaceholder: "Type a message...",
|
composerInputPlaceholder: "Type a message...",
|
||||||
sendMessageButtonAudioLabel: "send message",
|
sendMessageButtonAudioLabel: "send message",
|
||||||
showMessageInfoButtonAudioLabel: "show message info",
|
showMessageInfoButtonAudioLabel: "show message info",
|
||||||
@@ -1661,13 +1729,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// src/View/ChatPages/messagePage.tsx
|
// src/View/ChatPages/messagePage.tsx
|
||||||
function MessagePage(chatViewModel) {
|
function MessagePage(messagePageViewModel) {
|
||||||
|
messagePageViewModel.loadMessages();
|
||||||
const messageContainer = /* @__PURE__ */ createElement(
|
const messageContainer = /* @__PURE__ */ createElement(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
id: "message-container",
|
id: "message-container",
|
||||||
"children:append": [
|
"children:append": [
|
||||||
chatViewModel.chatMessageViewModels,
|
messagePageViewModel.chatMessageViewModels,
|
||||||
ChatMessageViewModelToView
|
ChatMessageViewModelToView
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1680,13 +1749,15 @@
|
|||||||
if (scrollFromBottom > 400) return;
|
if (scrollFromBottom > 400) return;
|
||||||
scrollDown();
|
scrollDown();
|
||||||
}
|
}
|
||||||
chatViewModel.chatMessageViewModels.subscribeSilent(scrollDownIfApplicable);
|
messagePageViewModel.chatMessageViewModels.subscribeSilent(
|
||||||
|
scrollDownIfApplicable
|
||||||
|
);
|
||||||
setTimeout(() => scrollDown(), 100);
|
setTimeout(() => scrollDown(), 100);
|
||||||
return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { "subscribe:innerText": chatViewModel.primaryChannel })), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.messagesHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
"bind:value": chatViewModel.composingMessage,
|
"bind:value": messagePageViewModel.composingMessage,
|
||||||
"on:enter": chatViewModel.sendMessage,
|
"on:enter": messagePageViewModel.sendMessage,
|
||||||
placeholder: translations.chatPage.message.composerInputPlaceholder
|
placeholder: translations.chatPage.message.composerInputPlaceholder
|
||||||
}
|
}
|
||||||
), /* @__PURE__ */ createElement(
|
), /* @__PURE__ */ createElement(
|
||||||
@@ -1694,8 +1765,8 @@
|
|||||||
{
|
{
|
||||||
class: "primary",
|
class: "primary",
|
||||||
"aria-label": translations.chatPage.message.sendMessageButtonAudioLabel,
|
"aria-label": translations.chatPage.message.sendMessageButtonAudioLabel,
|
||||||
"on:click": chatViewModel.sendMessage,
|
"on:click": messagePageViewModel.sendMessage,
|
||||||
"toggle:disabled": chatViewModel.cannotSendMessage
|
"toggle:disabled": messagePageViewModel.cannotSendMessage
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "send")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "send")
|
||||||
)))))));
|
)))))));
|
||||||
@@ -1749,25 +1820,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/View/ChatPages/settingsPage.tsx
|
// src/View/ChatPages/settingsPage.tsx
|
||||||
function SettingsPage(chatViewModel) {
|
function SettingsPage(settingsPageViewModel) {
|
||||||
|
settingsPageViewModel.loadData();
|
||||||
const secondaryChannelConverter = (secondaryChannel) => {
|
const secondaryChannelConverter = (secondaryChannel) => {
|
||||||
return DeletableListItem(secondaryChannel, /* @__PURE__ */ createElement("span", null), () => {
|
return DeletableListItem(secondaryChannel, /* @__PURE__ */ createElement("span", null), () => {
|
||||||
chatViewModel.removeSecondaryChannel(secondaryChannel);
|
settingsPageViewModel.removeSecondaryChannel(secondaryChannel);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return /* @__PURE__ */ createElement("div", { id: "settings-page" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.settingsHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.primaryChannelLabel), /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement("div", { id: "settings-page" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.settingsHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.primaryChannelLabel), /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
"bind:value": chatViewModel.primaryChannelInput,
|
"bind:value": settingsPageViewModel.primaryChannelInput,
|
||||||
"on:enter": chatViewModel.setPrimaryChannel
|
"on:enter": settingsPageViewModel.setPrimaryChannel
|
||||||
}
|
}
|
||||||
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
|
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
class: "width-50",
|
class: "width-50",
|
||||||
"aria-label": translations.chatPage.settings.setPrimaryChannelButtonAudioLabel,
|
"aria-label": translations.chatPage.settings.setPrimaryChannelButtonAudioLabel,
|
||||||
"on:click": chatViewModel.setPrimaryChannel,
|
"on:click": settingsPageViewModel.setPrimaryChannel,
|
||||||
"toggle:disabled": chatViewModel.cannotSetPrimaryChannel
|
"toggle:disabled": settingsPageViewModel.cannotSetPrimaryChannel
|
||||||
},
|
},
|
||||||
translations.general.setButton,
|
translations.general.setButton,
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
|
||||||
@@ -1776,16 +1848,16 @@
|
|||||||
{
|
{
|
||||||
"aria-label": translations.chatPage.settings.newSecondaryChannelAudioLabel,
|
"aria-label": translations.chatPage.settings.newSecondaryChannelAudioLabel,
|
||||||
placeholder: translations.chatPage.settings.newSecondaryChannelPlaceholder,
|
placeholder: translations.chatPage.settings.newSecondaryChannelPlaceholder,
|
||||||
"bind:value": chatViewModel.newSecondaryChannelInput,
|
"bind:value": settingsPageViewModel.newSecondaryChannelInput,
|
||||||
"on:enter": chatViewModel.addSecondaryChannel
|
"on:enter": settingsPageViewModel.addSecondaryChannel
|
||||||
}
|
}
|
||||||
), /* @__PURE__ */ createElement(
|
), /* @__PURE__ */ createElement(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
class: "primary",
|
class: "primary",
|
||||||
"aria-label": translations.chatPage.settings.addSecondaryChannelButtonAudioLabel,
|
"aria-label": translations.chatPage.settings.addSecondaryChannelButtonAudioLabel,
|
||||||
"on:click": chatViewModel.addSecondaryChannel,
|
"on:click": settingsPageViewModel.addSecondaryChannel,
|
||||||
"toggle:disabled": chatViewModel.cannotAddSecondaryChannel
|
"toggle:disabled": settingsPageViewModel.cannotAddSecondaryChannel
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
||||||
)), /* @__PURE__ */ createElement(
|
)), /* @__PURE__ */ createElement(
|
||||||
@@ -1793,24 +1865,24 @@
|
|||||||
{
|
{
|
||||||
class: "flex-column gap width-input",
|
class: "flex-column gap width-input",
|
||||||
"children:append": [
|
"children:append": [
|
||||||
chatViewModel.secondaryChannels,
|
settingsPageViewModel.secondaryChannels,
|
||||||
secondaryChannelConverter
|
secondaryChannelConverter
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.encryptionKeyLabel), /* @__PURE__ */ createElement(
|
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.encryptionKeyLabel), /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
"bind:value": chatViewModel.encryptionKeyInput,
|
"bind:value": settingsPageViewModel.encryptionKeyInput,
|
||||||
"on:enter": chatViewModel.setEncryptionKey,
|
"on:enter": settingsPageViewModel.setEncryptionKey,
|
||||||
"set:type": chatViewModel.encryptionKeyInputType
|
"set:type": settingsPageViewModel.encryptionKeyInputType
|
||||||
}
|
}
|
||||||
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
|
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
class: "width-50",
|
class: "width-50",
|
||||||
"aria-label": translations.chatPage.settings.setEncryptionKeyButtonAudioLabel,
|
"aria-label": translations.chatPage.settings.setEncryptionKeyButtonAudioLabel,
|
||||||
"on:click": chatViewModel.setEncryptionKey,
|
"on:click": settingsPageViewModel.setEncryptionKey,
|
||||||
"toggle:disabled": chatViewModel.cannotSetEncryptionKey
|
"toggle:disabled": settingsPageViewModel.cannotSetEncryptionKey
|
||||||
},
|
},
|
||||||
translations.general.setButton,
|
translations.general.setButton,
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
|
||||||
@@ -1818,15 +1890,15 @@
|
|||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
"bind:checked": chatViewModel.shouldShowEncryptionKey
|
"bind:checked": settingsPageViewModel.shouldShowEncryptionKey
|
||||||
}
|
}
|
||||||
), translations.chatPage.settings.showEncryptionKey), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row gap width-input" }, ...Object.values(Color).map((color) => {
|
), translations.chatPage.settings.showEncryptionKey), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row gap width-input" }, ...Object.values(Color).map((color) => {
|
||||||
const isSelected = createProxyState(
|
const isSelected = createProxyState(
|
||||||
[chatViewModel.color],
|
[settingsPageViewModel.color],
|
||||||
() => chatViewModel.color.value == color
|
() => settingsPageViewModel.color.value == color
|
||||||
);
|
);
|
||||||
function setColor() {
|
function setColor() {
|
||||||
chatViewModel.setColor(color);
|
settingsPageViewModel.setColor(color);
|
||||||
}
|
}
|
||||||
return /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement(
|
||||||
"button",
|
"button",
|
||||||
@@ -1841,25 +1913,29 @@
|
|||||||
})), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
|
})), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
|
||||||
translations.chatPage.settings.deleteChatButton,
|
translations.chatPage.settings.deleteChatButton,
|
||||||
"chat_error",
|
"chat_error",
|
||||||
chatViewModel.remove
|
settingsPageViewModel.remove
|
||||||
)))));
|
)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/View/ChatPages/taskPage.tsx
|
// src/View/ChatPages/taskPage.tsx
|
||||||
function TaskPage(chatViewModel) {
|
function TaskPage(taskPageViewModel) {
|
||||||
return /* @__PURE__ */ createElement("div", { id: "task-page" }, /* @__PURE__ */ createElement("div", { class: "pane side" }, /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement("div", { id: "task-page" }, /* @__PURE__ */ createElement("div", { class: "pane side" }, /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
|
"bind:value": taskPageViewModel.newBoardNameInput,
|
||||||
|
"on:enter": taskPageViewModel.createBoard,
|
||||||
placeholder: translations.chatPage.task.newBoardNamePlaceholder
|
placeholder: translations.chatPage.task.newBoardNamePlaceholder
|
||||||
}
|
}
|
||||||
), /* @__PURE__ */ createElement(
|
), /* @__PURE__ */ createElement(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
class: "primary",
|
class: "primary",
|
||||||
"aria-label": translations.chatPage.task.createBoardButtonAudioLabel
|
"aria-label": translations.chatPage.task.createBoardButtonAudioLabel,
|
||||||
|
"on:click": taskPageViewModel.createBoard,
|
||||||
|
"toggle:disabled": taskPageViewModel.cannotCreateBoard
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
||||||
)), /* @__PURE__ */ createElement("div", { class: "flex-row gap" }))), /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { "subscribe:innerText": chatViewModel.primaryChannel })), /* @__PURE__ */ createElement("div", { class: "content" })));
|
)), /* @__PURE__ */ createElement("div", { class: "flex-row gap" }))), /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }), /* @__PURE__ */ createElement("div", { class: "content" })));
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/View/chatPage.tsx
|
// src/View/chatPage.tsx
|
||||||
@@ -1867,19 +1943,24 @@
|
|||||||
const mainContent = new State(/* @__PURE__ */ createElement("div", null));
|
const mainContent = new State(/* @__PURE__ */ createElement("div", null));
|
||||||
chatViewModel.selectedPage.subscribe((selectedPage) => {
|
chatViewModel.selectedPage.subscribe((selectedPage) => {
|
||||||
switch (selectedPage) {
|
switch (selectedPage) {
|
||||||
case "settings" /* Settings */:
|
case "settings" /* Settings */: {
|
||||||
return mainContent.value = SettingsPage(chatViewModel);
|
mainContent.value = SettingsPage(chatViewModel.settingsPageViewModel);
|
||||||
case "tasks" /* Tasks */:
|
break;
|
||||||
return mainContent.value = TaskPage(chatViewModel);
|
}
|
||||||
default:
|
case "tasks" /* Tasks */: {
|
||||||
return mainContent.value = MessagePage(chatViewModel);
|
mainContent.value = TaskPage(chatViewModel.taskPageViewModel);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
mainContent.value = MessagePage(chatViewModel.messagePageViewModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement(
|
||||||
"article",
|
"article",
|
||||||
{
|
{
|
||||||
id: "chat-page",
|
id: "chat-page",
|
||||||
"set:color": chatViewModel.color,
|
"set:color": chatViewModel.settingsPageViewModel.color,
|
||||||
class: "subtle-background"
|
class: "subtle-background"
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { id: "ribbon" }, /* @__PURE__ */ createElement(
|
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { id: "ribbon" }, /* @__PURE__ */ createElement(
|
||||||
@@ -2300,7 +2381,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/ViewModel/connectionViewModel.ts
|
// src/ViewModel/Global/connectionViewModel.ts
|
||||||
var ConnectionViewModel = class {
|
var ConnectionViewModel = class {
|
||||||
connectionModel;
|
connectionModel;
|
||||||
// state
|
// state
|
||||||
@@ -2374,13 +2455,27 @@
|
|||||||
|
|
||||||
// src/View/Components/chatEntry.tsx
|
// src/View/Components/chatEntry.tsx
|
||||||
function ChatEntry(chatViewModel) {
|
function ChatEntry(chatViewModel) {
|
||||||
const view = /* @__PURE__ */ createElement("button", { "set:color": chatViewModel.color, class: "chat-entry tile", "on:click": chatViewModel.open }, /* @__PURE__ */ createElement(
|
const view = /* @__PURE__ */ createElement(
|
||||||
"span",
|
"button",
|
||||||
{
|
{
|
||||||
class: "shadow",
|
"set:color": chatViewModel.settingsPageViewModel.color,
|
||||||
"subscribe:innerText": chatViewModel.primaryChannel
|
class: "chat-entry tile",
|
||||||
}
|
"on:click": chatViewModel.open
|
||||||
), /* @__PURE__ */ createElement("h2", { "subscribe:innerText": chatViewModel.primaryChannel }));
|
},
|
||||||
|
/* @__PURE__ */ createElement(
|
||||||
|
"span",
|
||||||
|
{
|
||||||
|
class: "shadow",
|
||||||
|
"subscribe:innerText": chatViewModel.settingsPageViewModel.primaryChannel
|
||||||
|
}
|
||||||
|
),
|
||||||
|
/* @__PURE__ */ createElement(
|
||||||
|
"h2",
|
||||||
|
{
|
||||||
|
"subscribe:innerText": chatViewModel.settingsPageViewModel.primaryChannel
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
chatViewModel.index.subscribe((newIndex) => {
|
chatViewModel.index.subscribe((newIndex) => {
|
||||||
view.style.order = newIndex;
|
view.style.order = newIndex;
|
||||||
});
|
});
|
||||||
@@ -2542,7 +2637,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/ViewModel/settingsViewModel.ts
|
// src/ViewModel/Global/settingsViewModel.ts
|
||||||
var SettingsViewModel = class {
|
var SettingsViewModel = class {
|
||||||
settingsModel;
|
settingsModel;
|
||||||
// state
|
// state
|
||||||
@@ -2665,7 +2760,7 @@
|
|||||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": storageViewModel2.isShowingStorageModal }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", { class: "padding-0" }, FileBrowser(storageViewModel2)), /* @__PURE__ */ createElement("button", { "on:click": storageViewModel2.hideStorageModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": storageViewModel2.isShowingStorageModal }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", { class: "padding-0" }, FileBrowser(storageViewModel2)), /* @__PURE__ */ createElement("button", { "on:click": storageViewModel2.hideStorageModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/ViewModel/storageViewModel.ts
|
// src/ViewModel/Global/storageViewModel.ts
|
||||||
var StorageViewModel = class {
|
var StorageViewModel = class {
|
||||||
storageModel;
|
storageModel;
|
||||||
// state
|
// state
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import FileModel, { FileContent } from "../Files/fileModel";
|
|||||||
import StorageModel, { filePaths } from "../Global/storageModel";
|
import StorageModel, { filePaths } from "../Global/storageModel";
|
||||||
import {
|
import {
|
||||||
createTimestamp,
|
createTimestamp,
|
||||||
|
localeCompare,
|
||||||
parseValidObject,
|
parseValidObject,
|
||||||
stringify,
|
stringify,
|
||||||
} from "../Utility/utility";
|
} from "../Utility/utility";
|
||||||
@@ -31,6 +32,10 @@ export default class ChatModel {
|
|||||||
|
|
||||||
chatMessageHandler: (chatMessage: ChatMessage) => void = () => {};
|
chatMessageHandler: (chatMessage: ChatMessage) => void = () => {};
|
||||||
|
|
||||||
|
get secondaryChannels(): string[] {
|
||||||
|
return this.info.secondaryChannels.sort(localeCompare);
|
||||||
|
}
|
||||||
|
|
||||||
// paths
|
// paths
|
||||||
getBasePath = (): string[] => {
|
getBasePath = (): string[] => {
|
||||||
return StorageModel.getPath("chat", filePaths.chat.chatBase(this.id));
|
return StorageModel.getPath("chat", filePaths.chat.chatBase(this.id));
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ import "./messagePage.css";
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import { ChatMessageViewModelToView } from "../Components/chatMessage";
|
import { ChatMessageViewModelToView } from "../Components/chatMessage";
|
||||||
import ChatViewModel from "../../ViewModel/Chat/chatViewModel";
|
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
|
||||||
import { translations } from "../translations";
|
import { translations } from "../translations";
|
||||||
|
|
||||||
export function MessagePage(chatViewModel: ChatViewModel) {
|
export function MessagePage(messagePageViewModel: MessagePageViewModel) {
|
||||||
|
messagePageViewModel.loadMessages();
|
||||||
|
|
||||||
const messageContainer = (
|
const messageContainer = (
|
||||||
<div
|
<div
|
||||||
id="message-container"
|
id="message-container"
|
||||||
children:append={[
|
children:append={[
|
||||||
chatViewModel.chatMessageViewModels,
|
messagePageViewModel.chatMessageViewModels,
|
||||||
ChatMessageViewModelToView,
|
ChatMessageViewModelToView,
|
||||||
]}
|
]}
|
||||||
></div>
|
></div>
|
||||||
@@ -28,14 +30,16 @@ export function MessagePage(chatViewModel: ChatViewModel) {
|
|||||||
|
|
||||||
scrollDown();
|
scrollDown();
|
||||||
}
|
}
|
||||||
chatViewModel.chatMessageViewModels.subscribeSilent(scrollDownIfApplicable);
|
messagePageViewModel.chatMessageViewModels.subscribeSilent(
|
||||||
|
scrollDownIfApplicable
|
||||||
|
);
|
||||||
setTimeout(() => scrollDown(), 100);
|
setTimeout(() => scrollDown(), 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="message-page">
|
<div id="message-page">
|
||||||
<div class="pane">
|
<div class="pane">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<span subscribe:innerText={chatViewModel.primaryChannel}></span>
|
<span>{translations.chatPage.message.messagesHeadline}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{messageContainer}
|
{messageContainer}
|
||||||
@@ -43,8 +47,8 @@ export function MessagePage(chatViewModel: ChatViewModel) {
|
|||||||
<div class="content-width-constraint">
|
<div class="content-width-constraint">
|
||||||
<div class="input-width-constraint">
|
<div class="input-width-constraint">
|
||||||
<input
|
<input
|
||||||
bind:value={chatViewModel.composingMessage}
|
bind:value={messagePageViewModel.composingMessage}
|
||||||
on:enter={chatViewModel.sendMessage}
|
on:enter={messagePageViewModel.sendMessage}
|
||||||
placeholder={
|
placeholder={
|
||||||
translations.chatPage.message.composerInputPlaceholder
|
translations.chatPage.message.composerInputPlaceholder
|
||||||
}
|
}
|
||||||
@@ -54,8 +58,8 @@ export function MessagePage(chatViewModel: ChatViewModel) {
|
|||||||
aria-label={
|
aria-label={
|
||||||
translations.chatPage.message.sendMessageButtonAudioLabel
|
translations.chatPage.message.sendMessageButtonAudioLabel
|
||||||
}
|
}
|
||||||
on:click={chatViewModel.sendMessage}
|
on:click={messagePageViewModel.sendMessage}
|
||||||
toggle:disabled={chatViewModel.cannotSendMessage}
|
toggle:disabled={messagePageViewModel.cannotSendMessage}
|
||||||
>
|
>
|
||||||
<span class="icon">send</span>
|
<span class="icon">send</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import ChatViewModel from "../../ViewModel/chatViewModel";
|
|
||||||
import { Color } from "../../colors";
|
import { Color } from "../../colors";
|
||||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||||
import { DeletableListItem } from "../Components/deletableListItem";
|
import { DeletableListItem } from "../Components/deletableListItem";
|
||||||
|
import SettingsPageViewModel from "../../ViewModel/Pages/settingsPageViewModel";
|
||||||
import { translations } from "../translations";
|
import { translations } from "../translations";
|
||||||
|
|
||||||
export function SettingsPage(chatViewModel: ChatViewModel) {
|
export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||||
|
settingsPageViewModel.loadData();
|
||||||
|
|
||||||
const secondaryChannelConverter: React.StateItemConverter<string> = (
|
const secondaryChannelConverter: React.StateItemConverter<string> = (
|
||||||
secondaryChannel: string
|
secondaryChannel: string
|
||||||
) => {
|
) => {
|
||||||
return DeletableListItem(secondaryChannel, <span></span>, () => {
|
return DeletableListItem(secondaryChannel, <span></span>, () => {
|
||||||
chatViewModel.removeSecondaryChannel(secondaryChannel);
|
settingsPageViewModel.removeSecondaryChannel(secondaryChannel);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,8 +29,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
<div>
|
<div>
|
||||||
<span>{translations.chatPage.settings.primaryChannelLabel}</span>
|
<span>{translations.chatPage.settings.primaryChannelLabel}</span>
|
||||||
<input
|
<input
|
||||||
bind:value={chatViewModel.primaryChannelInput}
|
bind:value={settingsPageViewModel.primaryChannelInput}
|
||||||
on:enter={chatViewModel.setPrimaryChannel}
|
on:enter={settingsPageViewModel.setPrimaryChannel}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
@@ -38,8 +40,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
aria-label={
|
aria-label={
|
||||||
translations.chatPage.settings.setPrimaryChannelButtonAudioLabel
|
translations.chatPage.settings.setPrimaryChannelButtonAudioLabel
|
||||||
}
|
}
|
||||||
on:click={chatViewModel.setPrimaryChannel}
|
on:click={settingsPageViewModel.setPrimaryChannel}
|
||||||
toggle:disabled={chatViewModel.cannotSetPrimaryChannel}
|
toggle:disabled={settingsPageViewModel.cannotSetPrimaryChannel}
|
||||||
>
|
>
|
||||||
{translations.general.setButton}
|
{translations.general.setButton}
|
||||||
<span class="icon">check</span>
|
<span class="icon">check</span>
|
||||||
@@ -56,8 +58,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
placeholder={
|
placeholder={
|
||||||
translations.chatPage.settings.newSecondaryChannelPlaceholder
|
translations.chatPage.settings.newSecondaryChannelPlaceholder
|
||||||
}
|
}
|
||||||
bind:value={chatViewModel.newSecondaryChannelInput}
|
bind:value={settingsPageViewModel.newSecondaryChannelInput}
|
||||||
on:enter={chatViewModel.addSecondaryChannel}
|
on:enter={settingsPageViewModel.addSecondaryChannel}
|
||||||
></input>
|
></input>
|
||||||
<button
|
<button
|
||||||
class="primary"
|
class="primary"
|
||||||
@@ -65,8 +67,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
translations.chatPage.settings
|
translations.chatPage.settings
|
||||||
.addSecondaryChannelButtonAudioLabel
|
.addSecondaryChannelButtonAudioLabel
|
||||||
}
|
}
|
||||||
on:click={chatViewModel.addSecondaryChannel}
|
on:click={settingsPageViewModel.addSecondaryChannel}
|
||||||
toggle:disabled={chatViewModel.cannotAddSecondaryChannel}
|
toggle:disabled={settingsPageViewModel.cannotAddSecondaryChannel}
|
||||||
>
|
>
|
||||||
<span class="icon">add</span>
|
<span class="icon">add</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -75,7 +77,7 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
<div
|
<div
|
||||||
class="flex-column gap width-input"
|
class="flex-column gap width-input"
|
||||||
children:append={[
|
children:append={[
|
||||||
chatViewModel.secondaryChannels,
|
settingsPageViewModel.secondaryChannels,
|
||||||
secondaryChannelConverter,
|
secondaryChannelConverter,
|
||||||
]}
|
]}
|
||||||
></div>
|
></div>
|
||||||
@@ -87,9 +89,9 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
<div>
|
<div>
|
||||||
<span>{translations.chatPage.settings.encryptionKeyLabel}</span>
|
<span>{translations.chatPage.settings.encryptionKeyLabel}</span>
|
||||||
<input
|
<input
|
||||||
bind:value={chatViewModel.encryptionKeyInput}
|
bind:value={settingsPageViewModel.encryptionKeyInput}
|
||||||
on:enter={chatViewModel.setEncryptionKey}
|
on:enter={settingsPageViewModel.setEncryptionKey}
|
||||||
set:type={chatViewModel.encryptionKeyInputType}
|
set:type={settingsPageViewModel.encryptionKeyInputType}
|
||||||
></input>
|
></input>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
@@ -99,8 +101,8 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
aria-label={
|
aria-label={
|
||||||
translations.chatPage.settings.setEncryptionKeyButtonAudioLabel
|
translations.chatPage.settings.setEncryptionKeyButtonAudioLabel
|
||||||
}
|
}
|
||||||
on:click={chatViewModel.setEncryptionKey}
|
on:click={settingsPageViewModel.setEncryptionKey}
|
||||||
toggle:disabled={chatViewModel.cannotSetEncryptionKey}
|
toggle:disabled={settingsPageViewModel.cannotSetEncryptionKey}
|
||||||
>
|
>
|
||||||
{translations.general.setButton}
|
{translations.general.setButton}
|
||||||
<span class="icon">check</span>
|
<span class="icon">check</span>
|
||||||
@@ -110,7 +112,7 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
<label class="inline">
|
<label class="inline">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={chatViewModel.shouldShowEncryptionKey}
|
bind:checked={settingsPageViewModel.shouldShowEncryptionKey}
|
||||||
></input>
|
></input>
|
||||||
{translations.chatPage.settings.showEncryptionKey}
|
{translations.chatPage.settings.showEncryptionKey}
|
||||||
</label>
|
</label>
|
||||||
@@ -120,12 +122,12 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
<div class="flex-row gap width-input">
|
<div class="flex-row gap width-input">
|
||||||
{...Object.values(Color).map((color) => {
|
{...Object.values(Color).map((color) => {
|
||||||
const isSelected = React.createProxyState(
|
const isSelected = React.createProxyState(
|
||||||
[chatViewModel.color],
|
[settingsPageViewModel.color],
|
||||||
() => chatViewModel.color.value == color
|
() => settingsPageViewModel.color.value == color
|
||||||
);
|
);
|
||||||
|
|
||||||
function setColor() {
|
function setColor() {
|
||||||
chatViewModel.setColor(color);
|
settingsPageViewModel.setColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -146,7 +148,7 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
|
|||||||
{DangerousActionButton(
|
{DangerousActionButton(
|
||||||
translations.chatPage.settings.deleteChatButton,
|
translations.chatPage.settings.deleteChatButton,
|
||||||
"chat_error",
|
"chat_error",
|
||||||
chatViewModel.remove
|
settingsPageViewModel.remove
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import TaskViewModel from "../../ViewModel/Files/taskViewModel";
|
import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
||||||
import { translations } from "../translations";
|
import { translations } from "../translations";
|
||||||
|
|
||||||
export function TaskPage(taskViewModel: TaskViewModel) {
|
export function TaskPage(taskPageViewModel: TaskPageViewModel) {
|
||||||
return (
|
return (
|
||||||
<div id="task-page">
|
<div id="task-page">
|
||||||
<div class="pane side">
|
<div class="pane side">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="flex-row width-input">
|
<div class="flex-row width-input">
|
||||||
<input
|
<input
|
||||||
bind:value={taskViewModel.newBoardNameInput}
|
bind:value={taskPageViewModel.newBoardNameInput}
|
||||||
on:enter={taskViewModel.createBoard}
|
on:enter={taskPageViewModel.createBoard}
|
||||||
placeholder={translations.chatPage.task.newBoardNamePlaceholder}
|
placeholder={translations.chatPage.task.newBoardNamePlaceholder}
|
||||||
></input>
|
></input>
|
||||||
<button
|
<button
|
||||||
@@ -19,8 +19,8 @@ export function TaskPage(taskViewModel: TaskViewModel) {
|
|||||||
aria-label={
|
aria-label={
|
||||||
translations.chatPage.task.createBoardButtonAudioLabel
|
translations.chatPage.task.createBoardButtonAudioLabel
|
||||||
}
|
}
|
||||||
on:click={taskViewModel.createBoard}
|
on:click={taskPageViewModel.createBoard}
|
||||||
toggle:disabled={taskViewModel.cannotCreateBoard}
|
toggle:disabled={taskPageViewModel.cannotCreateBoard}
|
||||||
>
|
>
|
||||||
<span class="icon">add</span>
|
<span class="icon">add</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -6,12 +6,18 @@ import ChatViewModel from "../../ViewModel/Chat/chatViewModel";
|
|||||||
|
|
||||||
export function ChatEntry(chatViewModel: ChatViewModel) {
|
export function ChatEntry(chatViewModel: ChatViewModel) {
|
||||||
const view = (
|
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
|
<span
|
||||||
class="shadow"
|
class="shadow"
|
||||||
subscribe:innerText={chatViewModel.primaryChannel}
|
subscribe:innerText={chatViewModel.settingsPageViewModel.primaryChannel}
|
||||||
></span>
|
></span>
|
||||||
<h2 subscribe:innerText={chatViewModel.primaryChannel}></h2>
|
<h2
|
||||||
|
subscribe:innerText={chatViewModel.settingsPageViewModel.primaryChannel}
|
||||||
|
></h2>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import ChatMessageViewModel from "../../Model/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
import { translations } from "../translations";
|
import { translations } from "../translations";
|
||||||
|
|
||||||
export function ChatMessageInfoModal(
|
export function ChatMessageInfoModal(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import ChatViewModel, { ChatPageType } from "../../ViewModel/chatViewModel";
|
import ChatViewModel, { ChatPageType } from "../../ViewModel/Chat/chatViewModel";
|
||||||
|
|
||||||
export function ChatViewToggleButton(
|
export function ChatViewToggleButton(
|
||||||
label: string,
|
label: string,
|
||||||
|
|||||||
+12
-7
@@ -15,19 +15,24 @@ export function ChatPage(chatViewModel: ChatViewModel) {
|
|||||||
|
|
||||||
chatViewModel.selectedPage.subscribe((selectedPage) => {
|
chatViewModel.selectedPage.subscribe((selectedPage) => {
|
||||||
switch (selectedPage) {
|
switch (selectedPage) {
|
||||||
case ChatPageType.Settings:
|
case ChatPageType.Settings: {
|
||||||
return (mainContent.value = SettingsPage(chatViewModel));
|
mainContent.value = SettingsPage(chatViewModel.settingsPageViewModel);
|
||||||
case ChatPageType.Tasks:
|
break;
|
||||||
return (mainContent.value = TaskPage(chatViewModel));
|
}
|
||||||
default:
|
case ChatPageType.Tasks: {
|
||||||
return (mainContent.value = MessagePage(chatViewModel));
|
mainContent.value = TaskPage(chatViewModel.taskPageViewModel);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
mainContent.value = MessagePage(chatViewModel.messagePageViewModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
id="chat-page"
|
id="chat-page"
|
||||||
set:color={chatViewModel.color}
|
set:color={chatViewModel.settingsPageViewModel.color}
|
||||||
class="subtle-background"
|
class="subtle-background"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import "./chatPage.css";
|
|||||||
|
|
||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import ChatListViewModel from "../ViewModel/chatListViewModel";
|
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||||
import { ChatPage } from "./chatPage";
|
import { ChatPage } from "./chatPage";
|
||||||
|
|
||||||
export function ChatPageWrapper(chatListViewModel: ChatListViewModel) {
|
export function ChatPageWrapper(chatListViewModel: ChatListViewModel) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as React from "bloatless-react";
|
|||||||
|
|
||||||
import { Option, StringToOption } from "./Components/option";
|
import { Option, StringToOption } from "./Components/option";
|
||||||
|
|
||||||
import ChatListViewModel from "../ViewModel/chatListViewModel";
|
import ChatListViewModel from "../ViewModel/Chat/chatListViewModel";
|
||||||
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
|
import { ChatViewModelToChatEntry } from "./Components/chatEntry";
|
||||||
import ConnectionViewModel from "../ViewModel/Global/connectionViewModel";
|
import ConnectionViewModel from "../ViewModel/Global/connectionViewModel";
|
||||||
import SettingsViewModel from "../ViewModel/Global/settingsViewModel";
|
import SettingsViewModel from "../ViewModel/Global/settingsViewModel";
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ const englishTranslations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
message: {
|
message: {
|
||||||
|
messagesHeadline: "Messages",
|
||||||
|
|
||||||
composerInputPlaceholder: "Type a message...",
|
composerInputPlaceholder: "Type a message...",
|
||||||
sendMessageButtonAudioLabel: "send message",
|
sendMessageButtonAudioLabel: "send message",
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import * as React from "bloatless-react";
|
|||||||
import { ChatMessage, ChatMessageStatus } from "../../Model/Chat/chatModel";
|
import { ChatMessage, ChatMessageStatus } from "../../Model/Chat/chatModel";
|
||||||
|
|
||||||
import ChatViewModel from "./chatViewModel";
|
import ChatViewModel from "./chatViewModel";
|
||||||
|
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
||||||
|
|
||||||
export default class ChatMessageViewModel {
|
export default class ChatMessageViewModel {
|
||||||
chatViewModel: ChatViewModel;
|
messagePageViewModel: MessagePageViewModel;
|
||||||
|
|
||||||
// data
|
// data
|
||||||
chatMessage: ChatMessage;
|
chatMessage: ChatMessage;
|
||||||
@@ -25,10 +26,10 @@ export default class ChatMessageViewModel {
|
|||||||
navigator.clipboard.writeText(this.body.value);
|
navigator.clipboard.writeText(this.body.value);
|
||||||
};
|
};
|
||||||
resendMessage = (): void => {
|
resendMessage = (): void => {
|
||||||
this.chatViewModel.sendMessageFromBody(this.body.value);
|
this.messagePageViewModel.sendMessageFromBody(this.body.value);
|
||||||
};
|
};
|
||||||
decryptMessage = (): void => {
|
decryptMessage = (): void => {
|
||||||
this.chatViewModel.decryptMessage(this);
|
this.messagePageViewModel.decryptMessage(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
// load
|
// load
|
||||||
@@ -42,11 +43,11 @@ export default class ChatMessageViewModel {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
chatViewModel: ChatViewModel,
|
messagePageViewModel: MessagePageViewModel,
|
||||||
chatMessage: ChatMessage,
|
chatMessage: ChatMessage,
|
||||||
sentByUser: boolean
|
sentByUser: boolean
|
||||||
) {
|
) {
|
||||||
this.chatViewModel = chatViewModel;
|
this.messagePageViewModel = messagePageViewModel;
|
||||||
|
|
||||||
this.chatMessage = chatMessage;
|
this.chatMessage = chatMessage;
|
||||||
this.sentByUser = sentByUser;
|
this.sentByUser = sentByUser;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
|
|||||||
import StorageModel, { filePaths } from "../../Model/Global/storageModel";
|
import StorageModel, { filePaths } from "../../Model/Global/storageModel";
|
||||||
|
|
||||||
import ChatListViewModel from "./chatListViewModel";
|
import ChatListViewModel from "./chatListViewModel";
|
||||||
import ChatMessageViewModel from "./chatMessageViewModel";
|
import MessagePageViewModel from "../Pages/messagePageViewModel";
|
||||||
import { Color } from "../../colors";
|
import SettingsPageViewModel from "../Pages/settingsPageViewModel";
|
||||||
import SettingsViewModel from "../Global/settingsViewModel";
|
import SettingsViewModel from "../Global/settingsViewModel";
|
||||||
import { localeCompare } from "../../Model/Utility/utility";
|
import TaskPageViewModel from "../Pages/taskPageViewModel";
|
||||||
|
|
||||||
export default class ChatViewModel {
|
export default class ChatViewModel {
|
||||||
chatModel: ChatModel;
|
chatModel: ChatModel;
|
||||||
@@ -15,46 +15,17 @@ export default class ChatViewModel {
|
|||||||
settingsViewModel: SettingsViewModel;
|
settingsViewModel: SettingsViewModel;
|
||||||
chatListViewModel: ChatListViewModel;
|
chatListViewModel: ChatListViewModel;
|
||||||
|
|
||||||
|
taskPageViewModel: TaskPageViewModel;
|
||||||
|
messagePageViewModel: MessagePageViewModel;
|
||||||
|
settingsPageViewModel: SettingsPageViewModel;
|
||||||
|
|
||||||
// state
|
// state
|
||||||
index: React.State<number> = new React.State(0);
|
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>(
|
selectedPage: React.State<ChatPageType> = new React.State<ChatPageType>(
|
||||||
ChatPageType.Messages
|
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
|
// sorting
|
||||||
updateIndex = (): void => {
|
updateIndex = (): void => {
|
||||||
this.index.value = this.chatModel.index;
|
this.index.value = this.chatModel.index;
|
||||||
@@ -63,95 +34,12 @@ export default class ChatViewModel {
|
|||||||
// view
|
// view
|
||||||
open = (): void => {
|
open = (): void => {
|
||||||
this.chatListViewModel.openChat(this);
|
this.chatListViewModel.openChat(this);
|
||||||
this.loadMessages();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
close = (): void => {
|
close = (): void => {
|
||||||
this.chatListViewModel.closeChat();
|
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
|
// load
|
||||||
loadPageSelection = (): void => {
|
loadPageSelection = (): void => {
|
||||||
const path: string[] = StorageModel.getPath(
|
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
|
// init
|
||||||
constructor(
|
constructor(
|
||||||
chatModel: ChatModel,
|
chatModel: ChatModel,
|
||||||
@@ -196,34 +69,22 @@ export default class ChatViewModel {
|
|||||||
this.settingsViewModel = settingsViewModel;
|
this.settingsViewModel = settingsViewModel;
|
||||||
this.chatListViewModel = chatListViewModel;
|
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
|
// handlers
|
||||||
chatModel.setMessageHandler((chatMessage) => {
|
chatModel.setMessageHandler((chatMessage) => {
|
||||||
this.addChatMessage(chatMessage);
|
this.messagePageViewModel.addChatMessage(chatMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// load
|
// 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.loadPageSelection();
|
||||||
this.updateIndex();
|
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, {
|
import TaskModel, {
|
||||||
BoardInfoFileContent,
|
BoardInfoFileContent,
|
||||||
TaskFileContent,
|
|
||||||
} from "../../Model/Files/taskModel";
|
} from "../../Model/Files/taskModel";
|
||||||
|
|
||||||
import StorageModel from "../../Model/Global/storageModel";
|
import StorageModel from "../../Model/Global/storageModel";
|
||||||
|
|
||||||
export default class TaskViewModel {
|
export default class TaskPageViewModel {
|
||||||
taskModel: TaskModel;
|
taskModel: TaskModel;
|
||||||
storageModel: StorageModel;
|
|
||||||
|
|
||||||
// state
|
// state
|
||||||
newBoardNameInput: React.State<string> = new React.State("");
|
newBoardNameInput: React.State<string> = new React.State("");
|
||||||
@@ -33,6 +31,5 @@ export default class TaskViewModel {
|
|||||||
// init
|
// init
|
||||||
constructor(taskModel: TaskModel, storageModel: StorageModel) {
|
constructor(taskModel: TaskModel, storageModel: StorageModel) {
|
||||||
this.taskModel = taskModel;
|
this.taskModel = taskModel;
|
||||||
this.storageModel = storageModel;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user