implement auto reconnect
This commit is contained in:
Vendored
+1
-1
@@ -827,7 +827,7 @@ body[showing-chat] #chat-page-wrapper {
|
||||
.file-browser code {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: .9rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
.file-browser > div {
|
||||
|
||||
Vendored
+316
-86
@@ -532,7 +532,10 @@
|
||||
this.initializeTree();
|
||||
};
|
||||
this.removeRecursively = (pathComponents) => {
|
||||
this.recurse(pathComponents, (path) => this.remove(path, false));
|
||||
this.recurse(
|
||||
pathComponents,
|
||||
(path) => this.remove(path, false)
|
||||
);
|
||||
this.initializeTree();
|
||||
};
|
||||
this.renameRecursively = (sourcePathComponents, destinationPathComponents) => {
|
||||
@@ -658,7 +661,9 @@
|
||||
constructor(storageModel2, settingsModel2, fileModel) {
|
||||
// paths
|
||||
this.getBasePath = () => {
|
||||
return this.fileModel.getModelContainerPath("calendar" /* ModelCalendar */);
|
||||
return this.fileModel.getModelContainerPath(
|
||||
"calendar" /* ModelCalendar */
|
||||
);
|
||||
};
|
||||
this.getViewPath = () => {
|
||||
return [...this.getBasePath(), "view" /* ModelView */];
|
||||
@@ -806,9 +811,15 @@
|
||||
};
|
||||
// handlers
|
||||
this.handleFileContent = (fileContent) => {
|
||||
if (checkMatchesObjectStructure(fileContent, BoardInfoFileContentReference) == true) {
|
||||
if (checkMatchesObjectStructure(
|
||||
fileContent,
|
||||
BoardInfoFileContentReference
|
||||
) == true) {
|
||||
this.handleBoard(fileContent);
|
||||
} else if (checkMatchesObjectStructure(fileContent, TaskFileContentReference) == true) {
|
||||
} else if (checkMatchesObjectStructure(
|
||||
fileContent,
|
||||
TaskFileContentReference
|
||||
) == true) {
|
||||
this.handleTask(fileContent);
|
||||
}
|
||||
};
|
||||
@@ -897,7 +908,10 @@
|
||||
return versionIds;
|
||||
};
|
||||
this.getLatestTaskFileContent = (taskId) => {
|
||||
const taskFileContentOrNull = this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
|
||||
const taskFileContentOrNull = this.fileModel.getLatestFileContent(
|
||||
taskId,
|
||||
TaskFileContentReference
|
||||
);
|
||||
return taskFileContentOrNull;
|
||||
};
|
||||
this.getSpecificTaskFileContent = (taskId, versionId) => {
|
||||
@@ -933,10 +947,7 @@
|
||||
static {
|
||||
// utility
|
||||
this.createBoardInfoFileContent = (fileId, name, color) => {
|
||||
const fileContent = FileModel2.createFileContent(
|
||||
fileId,
|
||||
"board-info"
|
||||
);
|
||||
const fileContent = FileModel2.createFileContent(fileId, "board-info");
|
||||
return {
|
||||
...fileContent,
|
||||
name,
|
||||
@@ -1043,7 +1054,10 @@
|
||||
return fileContentIds[fileContentIds.length - 1];
|
||||
};
|
||||
this.getFileContent = (fileId, fileContentName, reference) => {
|
||||
const filePath = this.getFileContentPath(fileId, fileContentName);
|
||||
const filePath = this.getFileContentPath(
|
||||
fileId,
|
||||
fileContentName
|
||||
);
|
||||
const fileContentOrNull = this.storageModel.readStringifiable(
|
||||
filePath,
|
||||
reference
|
||||
@@ -1247,7 +1261,9 @@
|
||||
this.storageModel.writeStringifiable(messagePath, chatMessage);
|
||||
this.chatMessageHandlerManager.trigger(chatMessage);
|
||||
}
|
||||
this.fileModel.handleStringifiedFileContent(chatMessage.stringifiedFile);
|
||||
this.fileModel.handleStringifiedFileContent(
|
||||
chatMessage.stringifiedFile
|
||||
);
|
||||
};
|
||||
this.sendMessage = async (body, fileContent) => {
|
||||
const senderName = this.settingsModel.username;
|
||||
@@ -1328,7 +1344,11 @@
|
||||
this.loadInfo();
|
||||
this.loadColor();
|
||||
this.subscribe();
|
||||
this.fileModel = new FileModel2(this.storageModel, this.settingsModel, this);
|
||||
this.fileModel = new FileModel2(
|
||||
this.storageModel,
|
||||
this.settingsModel,
|
||||
this
|
||||
);
|
||||
}
|
||||
get secondaryChannels() {
|
||||
return this.info.secondaryChannels.sort(localeCompare);
|
||||
@@ -1341,7 +1361,10 @@
|
||||
const chatMessages = [];
|
||||
for (const messageId of messageIds) {
|
||||
const messagePath = this.getMessagePath(messageId);
|
||||
const chatMessage = this.storageModel.readStringifiable(messagePath, ChatMessageReference);
|
||||
const chatMessage = this.storageModel.readStringifiable(
|
||||
messagePath,
|
||||
ChatMessageReference
|
||||
);
|
||||
chatMessages.push(chatMessage);
|
||||
}
|
||||
const sorted = chatMessages.sort(
|
||||
@@ -1378,7 +1401,10 @@
|
||||
chatMessage.stringifiedFile = stringifiedFile;
|
||||
}
|
||||
if (encryptionKey != "") {
|
||||
chatMessage.body = await encryptString(chatMessage.body, encryptionKey);
|
||||
chatMessage.body = await encryptString(
|
||||
chatMessage.body,
|
||||
encryptionKey
|
||||
);
|
||||
chatMessage.stringifiedFile = await encryptString(
|
||||
chatMessage.stringifiedFile,
|
||||
encryptionKey
|
||||
@@ -1463,7 +1489,10 @@
|
||||
};
|
||||
// load
|
||||
this.loadChats = () => {
|
||||
const chatDir = StorageModel.getPath("chat" /* Chat */, filePaths.chat.base);
|
||||
const chatDir = StorageModel.getPath(
|
||||
"chat" /* Chat */,
|
||||
filePaths.chat.base
|
||||
);
|
||||
const chatIds = this.storageModel.list(chatDir);
|
||||
for (const chatId of chatIds) {
|
||||
const chatModel = new ChatModel(
|
||||
@@ -1563,7 +1592,9 @@
|
||||
) == false) {
|
||||
this.coreViewModel.taskCategorySuggestions.add(this.category.value);
|
||||
}
|
||||
if (this.coreViewModel.taskStatusSuggestions.value.has(this.status.value) == false) {
|
||||
if (this.coreViewModel.taskStatusSuggestions.value.has(
|
||||
this.status.value
|
||||
) == false) {
|
||||
this.coreViewModel.taskStatusSuggestions.add(this.status.value);
|
||||
}
|
||||
};
|
||||
@@ -1588,14 +1619,15 @@
|
||||
};
|
||||
this.deleteTask = () => {
|
||||
this.close();
|
||||
this.boardsAndTasksModel.deleteTask(this.task.boardId, this.task.fileId);
|
||||
this.boardsAndTasksModel.deleteTask(
|
||||
this.task.boardId,
|
||||
this.task.fileId
|
||||
);
|
||||
this.containingModel.removeTaskFromView(this.task);
|
||||
};
|
||||
// load
|
||||
this.loadVersionIds = () => {
|
||||
const versionIds = this.boardsAndTasksModel.listTaskVersionIds(
|
||||
this.task.fileId
|
||||
);
|
||||
const versionIds = this.boardsAndTasksModel.listTaskVersionIds(this.task.fileId);
|
||||
const sortedVersionIds = versionIds.sort(localeCompare).reverse();
|
||||
this.versionIds.clear();
|
||||
this.versionIds.add(...sortedVersionIds);
|
||||
@@ -1810,7 +1842,11 @@
|
||||
}
|
||||
};
|
||||
this.handleDrop = (year, month, date) => {
|
||||
const ISOString = CalendarModel.getISODateString(year, month, date);
|
||||
const ISOString = CalendarModel.getISODateString(
|
||||
year,
|
||||
month,
|
||||
date
|
||||
);
|
||||
const draggedObject = this.coreViewModel.draggedObject.value;
|
||||
if (draggedObject instanceof TaskViewModel == false) return;
|
||||
draggedObject.setDate(ISOString);
|
||||
@@ -1822,7 +1858,9 @@
|
||||
this.selectedMonth.value,
|
||||
() => new MapState()
|
||||
);
|
||||
const taskIds = this.calendarModel.listTaskIds(this.monthString);
|
||||
const taskIds = this.calendarModel.listTaskIds(
|
||||
this.monthString
|
||||
);
|
||||
for (const taskId of taskIds) {
|
||||
const taskFileContent = this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
|
||||
if (taskFileContent == null) continue;
|
||||
@@ -1935,7 +1973,10 @@
|
||||
existingChatMessageViewModel.body.value = chatMessage.body;
|
||||
existingChatMessageViewModel.status.value = chatMessage.status;
|
||||
} else {
|
||||
this.chatMessageViewModels.set(chatMessage.id, chatMessageViewModel);
|
||||
this.chatMessageViewModels.set(
|
||||
chatMessage.id,
|
||||
chatMessageViewModel
|
||||
);
|
||||
}
|
||||
};
|
||||
// load
|
||||
@@ -1947,7 +1988,10 @@
|
||||
};
|
||||
this.chatViewModel = chatViewModel;
|
||||
this.cannotSendMessage = createProxyState(
|
||||
[this.chatViewModel.settingsViewModel.username, this.composingMessage],
|
||||
[
|
||||
this.chatViewModel.settingsViewModel.username,
|
||||
this.composingMessage
|
||||
],
|
||||
() => this.chatViewModel.settingsViewModel.username.value == "" || this.composingMessage.value == ""
|
||||
);
|
||||
}
|
||||
@@ -2116,7 +2160,9 @@
|
||||
this.filteredTaskViewModels = new ListState();
|
||||
// paths
|
||||
this.getBasePath = () => {
|
||||
return [...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)];
|
||||
return [
|
||||
...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)
|
||||
];
|
||||
};
|
||||
this.getLastUsedBoardPath = () => {
|
||||
return [...this.getBasePath(), "last-used-view" /* LastUsedView */];
|
||||
@@ -2137,7 +2183,9 @@
|
||||
this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
|
||||
};
|
||||
this.applyColor = () => {
|
||||
this.taskPageViewModel.chatViewModel.setDisplayedColor(this.color.value);
|
||||
this.taskPageViewModel.chatViewModel.setDisplayedColor(
|
||||
this.color.value
|
||||
);
|
||||
};
|
||||
this.deleteBoard = () => {
|
||||
this.taskPageViewModel.deleteBoard(this.boardInfo);
|
||||
@@ -3266,7 +3314,10 @@
|
||||
"select",
|
||||
{
|
||||
"bind:value": taskViewModel.selectedVersionId,
|
||||
"children:append": [taskViewModel.versionIds, VersionIdToOption]
|
||||
"children:append": [
|
||||
taskViewModel.versionIds,
|
||||
VersionIdToOption
|
||||
]
|
||||
}
|
||||
), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskNameLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.name }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "category"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskBoardLabel), /* @__PURE__ */ createElement(
|
||||
"select",
|
||||
@@ -3315,11 +3366,44 @@
|
||||
StringToOption
|
||||
]
|
||||
}
|
||||
), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "priority_high"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskPriorityLabel), /* @__PURE__ */ createElement("input", { type: "number", "bind:value": taskViewModel.priority }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDateLabel), /* @__PURE__ */ createElement("input", { type: "date", "bind:value": taskViewModel.date }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskTimeLabel), /* @__PURE__ */ createElement("input", { type: "time", "bind:value": taskViewModel.time }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
|
||||
), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "priority_high"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskPriorityLabel), /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
type: "number",
|
||||
"bind:value": taskViewModel.priority
|
||||
}
|
||||
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDateLabel), /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
type: "date",
|
||||
"bind:value": taskViewModel.date
|
||||
}
|
||||
))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskTimeLabel), /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
type: "time",
|
||||
"bind:value": taskViewModel.time
|
||||
}
|
||||
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
|
||||
translations.chatPage.task.deleteTaskButton,
|
||||
"delete_forever",
|
||||
taskViewModel.deleteTask
|
||||
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement("button", { class: "flex", "on:click": taskViewModel.closeAndDiscard }, translations.general.closeButton), /* @__PURE__ */ createElement("button", { class: "flex primary", "on:click": taskViewModel.closeAndSave }, translations.general.saveButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "save")))));
|
||||
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
|
||||
"button",
|
||||
{
|
||||
class: "flex",
|
||||
"on:click": taskViewModel.closeAndDiscard
|
||||
},
|
||||
translations.general.closeButton
|
||||
), /* @__PURE__ */ createElement(
|
||||
"button",
|
||||
{
|
||||
class: "flex primary",
|
||||
"on:click": taskViewModel.closeAndSave
|
||||
},
|
||||
translations.general.saveButton,
|
||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "save")
|
||||
))));
|
||||
}
|
||||
|
||||
// src/View/Components/taskEntry.tsx
|
||||
@@ -3341,7 +3425,13 @@
|
||||
"on:click": taskViewModel.open,
|
||||
"on:dragstart": taskViewModel.dragStart
|
||||
},
|
||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", { class: "ellipsis", "subscribe:innerText": taskViewModel.name }), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement(
|
||||
"b",
|
||||
{
|
||||
class: "ellipsis",
|
||||
"subscribe:innerText": taskViewModel.name
|
||||
}
|
||||
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "grid secondary",
|
||||
@@ -3384,7 +3474,11 @@
|
||||
date
|
||||
);
|
||||
};
|
||||
return MonthGrid2(monthGrid, calendarPageViewModel.selectedDate, drop);
|
||||
return MonthGrid2(
|
||||
monthGrid,
|
||||
calendarPageViewModel.selectedDate,
|
||||
drop
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -3399,7 +3493,9 @@
|
||||
if (listState == void 0) {
|
||||
return /* @__PURE__ */ createElement("div", null);
|
||||
} else {
|
||||
const sidePaneContent = createProxyState([listState], () => {
|
||||
const sidePaneContent = createProxyState(
|
||||
[listState],
|
||||
() => {
|
||||
if (listState.value.size == 0) {
|
||||
return /* @__PURE__ */ createElement("div", { class: "width-100 height-100 flex-column justify-center align-center" }, /* @__PURE__ */ createElement("span", { class: "secondary slide-up" }, translations.chatPage.calendar.noEvents));
|
||||
} else {
|
||||
@@ -3407,11 +3503,15 @@
|
||||
"div",
|
||||
{
|
||||
class: "flex-column gap slide-up padding-bottom",
|
||||
"children:append": [listState, TaskViewModelToEntry]
|
||||
"children:append": [
|
||||
listState,
|
||||
TaskViewModelToEntry
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
return /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
@@ -3484,13 +3584,25 @@
|
||||
"on:click": calendarPageViewModel.createEvent
|
||||
},
|
||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
||||
))), /* @__PURE__ */ createElement("div", { class: "content padding-0", "children:set": mainContent }))), /* @__PURE__ */ createElement(
|
||||
))), /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "content padding-0",
|
||||
"children:set": mainContent
|
||||
}
|
||||
))), /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "pane-wrapper side background",
|
||||
"set:color": calendarPageViewModel.chatViewModel.displayedColor
|
||||
},
|
||||
/* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "content", "children:set": sidePaneContentWrapper }))
|
||||
/* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "content",
|
||||
"children:set": sidePaneContentWrapper
|
||||
}
|
||||
))
|
||||
), /* @__PURE__ */ createElement("div", { "children:set": taskSettingsModal }));
|
||||
}
|
||||
|
||||
@@ -3522,13 +3634,20 @@
|
||||
|
||||
// src/View/Modals/chatMessageInfoModal.tsx
|
||||
function ChatMessageInfoModal(chatMessageViewModel) {
|
||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": chatMessageViewModel.isPresentingInfoModal }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.message.messageInfoHeadline), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.sentBy), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.sender))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.timeSent), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.dateSent))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.channel), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.channel))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.messageContent), /* @__PURE__ */ createElement(
|
||||
return /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "modal",
|
||||
"toggle:open": chatMessageViewModel.isPresentingInfoModal
|
||||
},
|
||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.message.messageInfoHeadline), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.sentBy), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.sender))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.timeSent), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.dateSent))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.channel), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.channel))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.messageContent), /* @__PURE__ */ createElement(
|
||||
"b",
|
||||
{
|
||||
class: "break-word",
|
||||
"subscribe:innerText": chatMessageViewModel.body
|
||||
}
|
||||
)))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.copyMessage }, translations.chatPage.message.copyMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "content_copy")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.resendMessage }, translations.chatPage.message.resendMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "redo")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.decryptMessage }, translations.chatPage.message.decryptMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "key")))), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.hideInfoModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||
)))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.copyMessage }, translations.chatPage.message.copyMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "content_copy")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.resendMessage }, translations.chatPage.message.resendMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "redo")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.decryptMessage }, translations.chatPage.message.decryptMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "key")))), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.hideInfoModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close")))
|
||||
);
|
||||
}
|
||||
|
||||
// src/View/Components/chatMessage.tsx
|
||||
@@ -3560,7 +3679,13 @@
|
||||
class: "body",
|
||||
"subscribe:innerText": chatMessageViewModel.body
|
||||
}
|
||||
), /* @__PURE__ */ createElement("span", { class: "timestamp ellipsis" }, /* @__PURE__ */ createElement("span", { class: "icon", "subscribe:innerText": statusIcon }), chatMessageViewModel.dateSent)), /* @__PURE__ */ createElement("div", { class: "button-container" }, /* @__PURE__ */ createElement(
|
||||
), /* @__PURE__ */ createElement("span", { class: "timestamp ellipsis" }, /* @__PURE__ */ createElement(
|
||||
"span",
|
||||
{
|
||||
class: "icon",
|
||||
"subscribe:innerText": statusIcon
|
||||
}
|
||||
), chatMessageViewModel.dateSent)), /* @__PURE__ */ createElement("div", { class: "button-container" }, /* @__PURE__ */ createElement(
|
||||
"button",
|
||||
{
|
||||
"on:click": chatMessageViewModel.showInfoModal,
|
||||
@@ -3872,11 +3997,21 @@
|
||||
(taskViewModel) => taskViewModel.task,
|
||||
boardViewModel.filteredTaskViewModels,
|
||||
(taskViewModels) => {
|
||||
const viewModel = new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
|
||||
const viewModel = new TaskCategoryBulkChangeViewModel(
|
||||
taskViewModels,
|
||||
categoryName
|
||||
);
|
||||
function drop() {
|
||||
boardViewModel.handleDropWithinBoard(categoryName);
|
||||
}
|
||||
const view = /* @__PURE__ */ createElement("div", { class: "flex-column flex-no", "on:dragover": allowDrop, "on:drop": drop }, /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
||||
const view = /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "flex-column flex-no",
|
||||
"on:dragover": allowDrop,
|
||||
"on:drop": drop
|
||||
},
|
||||
/* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
|
||||
@@ -3891,13 +4026,16 @@
|
||||
"toggle:disabled": viewModel.cannotSet
|
||||
},
|
||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
|
||||
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
||||
)),
|
||||
/* @__PURE__ */ createElement("hr", null),
|
||||
/* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "kanban-column",
|
||||
"children:append": [taskViewModels, TaskViewModelToEntry]
|
||||
}
|
||||
));
|
||||
)
|
||||
);
|
||||
index.subscribe((newIndex) => {
|
||||
view.style.order = newIndex;
|
||||
});
|
||||
@@ -3908,7 +4046,13 @@
|
||||
|
||||
// src/View/Modals/boardSettingsModal.tsx
|
||||
function BoardSettingsModal(boardViewModel) {
|
||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": boardViewModel.isPresentingSettingsModal }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.boardSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.boardNameInputLabel), /* @__PURE__ */ createElement(
|
||||
return /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "modal",
|
||||
"toggle:open": boardViewModel.isPresentingSettingsModal
|
||||
},
|
||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.boardSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.boardNameInputLabel), /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
"on:enter": boardViewModel.saveSettings,
|
||||
@@ -3918,7 +4062,8 @@
|
||||
translations.chatPage.task.deleteBoardButton,
|
||||
"delete_forever",
|
||||
boardViewModel.deleteBoard
|
||||
))), /* @__PURE__ */ createElement("button", { "on:click": boardViewModel.hideSettings }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||
))), /* @__PURE__ */ createElement("button", { "on:click": boardViewModel.hideSettings }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close")))
|
||||
);
|
||||
}
|
||||
|
||||
// src/View/ChatPages/boardStatusGridPage.tsx
|
||||
@@ -3978,9 +4123,12 @@
|
||||
const doesMatchStatus = taskViewModel.task.status == statusName;
|
||||
if (doesMatchStatus == false) return;
|
||||
taskViewModelsWithMatchingStatus.add(taskViewModel);
|
||||
boardViewModel.filteredTaskViewModels.handleRemoval(taskViewModel, () => {
|
||||
boardViewModel.filteredTaskViewModels.handleRemoval(
|
||||
taskViewModel,
|
||||
() => {
|
||||
taskViewModelsWithMatchingStatus.remove(taskViewModel);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
const viewModel = new TaskStatusBulkChangeViewModel(
|
||||
@@ -4015,7 +4163,10 @@
|
||||
boardViewModel.filteredTaskViewModels,
|
||||
(taskViewModels) => {
|
||||
const statusNameConverter = (statusName) => {
|
||||
const index2 = createPropertyValueIndexState(sortedStatuses, statusName);
|
||||
const index2 = createPropertyValueIndexState(
|
||||
sortedStatuses,
|
||||
statusName
|
||||
);
|
||||
return CategoryStatusColumn(
|
||||
categoryName,
|
||||
statusName,
|
||||
@@ -4024,7 +4175,10 @@
|
||||
taskViewModels
|
||||
);
|
||||
};
|
||||
const viewModel = new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
|
||||
const viewModel = new TaskCategoryBulkChangeViewModel(
|
||||
taskViewModels,
|
||||
categoryName
|
||||
);
|
||||
const view = /* @__PURE__ */ createElement("div", { class: "flex-row flex-no large-gap" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
@@ -4113,7 +4267,10 @@
|
||||
{
|
||||
hidden: true,
|
||||
id: suggestionId,
|
||||
"children:append": [searchViewModel.suggestions, StringToOption]
|
||||
"children:append": [
|
||||
searchViewModel.suggestions,
|
||||
StringToOption
|
||||
]
|
||||
}
|
||||
), /* @__PURE__ */ createElement(
|
||||
"button",
|
||||
@@ -4129,7 +4286,10 @@
|
||||
{
|
||||
class: "grid gap",
|
||||
style: "grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))",
|
||||
"children:append": [searchViewModel.matchingObjects, converter]
|
||||
"children:append": [
|
||||
searchViewModel.matchingObjects,
|
||||
converter
|
||||
]
|
||||
}
|
||||
)), /* @__PURE__ */ createElement("button", { "on:click": close }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||
}
|
||||
@@ -4168,7 +4328,9 @@
|
||||
if (boardViewModel.selectedTaskViewModel.value == void 0) {
|
||||
return /* @__PURE__ */ createElement("div", null);
|
||||
} else {
|
||||
return TaskSettingsModal(boardViewModel.selectedTaskViewModel.value);
|
||||
return TaskSettingsModal(
|
||||
boardViewModel.selectedTaskViewModel.value
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -4248,7 +4410,13 @@
|
||||
"on:dragover": allowDrop,
|
||||
"on:drop": boardViewModel.handleDropBetweenBoards
|
||||
},
|
||||
/* @__PURE__ */ createElement("span", { class: "shadow", "subscribe:innerText": boardViewModel.name }),
|
||||
/* @__PURE__ */ createElement(
|
||||
"span",
|
||||
{
|
||||
class: "shadow",
|
||||
"subscribe:innerText": boardViewModel.name
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ createElement("b", { "subscribe:innerText": boardViewModel.name })
|
||||
);
|
||||
boardViewModel.index.subscribe((newIndex) => {
|
||||
@@ -4281,7 +4449,14 @@
|
||||
return BoardPage(selectedBoard);
|
||||
}
|
||||
);
|
||||
return /* @__PURE__ */ createElement("div", { id: "task-page", "toggle:isshowingboard": isShowingBoard, "set:showingboardlist": taskPageViewModel.isShowingBoadList }, /* @__PURE__ */ createElement(
|
||||
return /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
id: "task-page",
|
||||
"toggle:isshowingboard": isShowingBoard,
|
||||
"set:showingboardlist": taskPageViewModel.isShowingBoadList
|
||||
},
|
||||
/* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
id: "board-list",
|
||||
@@ -4316,14 +4491,16 @@
|
||||
]
|
||||
}
|
||||
)))
|
||||
), /* @__PURE__ */ createElement(
|
||||
),
|
||||
/* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
id: "board-content",
|
||||
class: "pane-wrapper",
|
||||
"children:set": paneContent
|
||||
}
|
||||
));
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// src/View/chatPage.tsx
|
||||
@@ -4399,13 +4576,7 @@
|
||||
}
|
||||
}
|
||||
);
|
||||
return /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
id: "chat-page-wrapper",
|
||||
"children:set": chatPageContent
|
||||
}
|
||||
);
|
||||
return /* @__PURE__ */ createElement("div", { id: "chat-page-wrapper", "children:set": chatPageContent });
|
||||
}
|
||||
|
||||
// src/View/Modals/connectionModal.tsx
|
||||
@@ -4572,6 +4743,8 @@
|
||||
var ConnectionModel = class {
|
||||
// init
|
||||
constructor(storageModel2) {
|
||||
this.reconnectInterval = void 0;
|
||||
this.shouldAttemptReconnect = false;
|
||||
this.connectionChangeHandlerManager = new HandlerManager();
|
||||
this.messageHandlerManager = new HandlerManager();
|
||||
this.messageSentHandlerManager = new HandlerManager();
|
||||
@@ -4583,11 +4756,22 @@
|
||||
this.handleConnectionChange = () => {
|
||||
console.log("connection status:", this.isConnected, this.address);
|
||||
this.connectionChangeHandlerManager.trigger();
|
||||
if (this.isConnected == false) return;
|
||||
if (this.isConnected == false) {
|
||||
if (this.shouldAttemptReconnect == false) return;
|
||||
this.reconnectInterval = setInterval(() => {
|
||||
if (this.isConnected == true) {
|
||||
clearInterval(this.reconnectInterval);
|
||||
return;
|
||||
}
|
||||
this.reconnect();
|
||||
}, 5e3);
|
||||
return;
|
||||
}
|
||||
if (this.address == void 0) return;
|
||||
this.storeAddress(this.address);
|
||||
this.sendSubscriptionRequest();
|
||||
this.sendMessagesInOutbox();
|
||||
this.shouldAttemptReconnect = true;
|
||||
};
|
||||
// connection
|
||||
this.connect = (address) => {
|
||||
@@ -4600,6 +4784,14 @@
|
||||
filePaths.connectionModel.reconnectAddress
|
||||
);
|
||||
this.storageModel.remove(reconnectAddressPath);
|
||||
this.shouldAttemptReconnect = false;
|
||||
};
|
||||
this.reconnect = () => {
|
||||
const reconnectAddressPath = this.getReconnectAddressPath();
|
||||
const reconnectAddress = this.storageModel.read(reconnectAddressPath);
|
||||
if (reconnectAddress == null) return;
|
||||
console.log("reconnecting...");
|
||||
this.connect(reconnectAddress);
|
||||
};
|
||||
// mailbox
|
||||
this.getMailboxPath = (address) => {
|
||||
@@ -4616,7 +4808,9 @@
|
||||
};
|
||||
this.connectMailbox = () => {
|
||||
if (this.address == void 0) return;
|
||||
const mailboxId = this.storageModel.read(this.getMailboxPath(this.address));
|
||||
const mailboxId = this.storageModel.read(
|
||||
this.getMailboxPath(this.address)
|
||||
);
|
||||
console.log("connecting mailbox", mailboxId);
|
||||
if (mailboxId == null) return this.requestNewMailbox();
|
||||
this.udn.connectMailbox(mailboxId);
|
||||
@@ -4742,11 +4936,7 @@
|
||||
this.udn.onmailboxconnect = (mailboxId) => {
|
||||
console.log(`using mailbox ${mailboxId}`);
|
||||
};
|
||||
const reconnectAddressPath = this.getReconnectAddressPath();
|
||||
const reconnectAddress = storageModel2.read(reconnectAddressPath);
|
||||
if (reconnectAddress != null) {
|
||||
this.connect(reconnectAddress);
|
||||
}
|
||||
this.reconnect();
|
||||
}
|
||||
// data
|
||||
get isConnected() {
|
||||
@@ -4860,7 +5050,9 @@
|
||||
);
|
||||
this.filesSentText = createProxyState(
|
||||
[this.filesSentCount],
|
||||
() => translations.dataTransferModal.filesSentCount(this.filesSentCount.value)
|
||||
() => translations.dataTransferModal.filesSentCount(
|
||||
this.filesSentCount.value
|
||||
)
|
||||
);
|
||||
this.filePathsReceived = new ListState();
|
||||
this.filesReceivedCount = createProxyState(
|
||||
@@ -4902,7 +5094,10 @@
|
||||
},
|
||||
{
|
||||
label: translations.dataTransferModal.settingsData,
|
||||
path: StorageModel.getPath("settings" /* SettingsModel */, [])
|
||||
path: StorageModel.getPath(
|
||||
"settings" /* SettingsModel */,
|
||||
[]
|
||||
)
|
||||
}
|
||||
);
|
||||
const chatModels = this.chatListModel.chatModels;
|
||||
@@ -4973,7 +5168,10 @@
|
||||
|
||||
// src/View/Modals/dataTransferModal.tsx
|
||||
function DataTransferModalWrapper(connectionViewModel2, fileTransferViewModel2) {
|
||||
return /* @__PURE__ */ createElement("div", null, DirectionSelectionModal(connectionViewModel2, fileTransferViewModel2), FileSelectionModal(fileTransferViewModel2), TransferDataDisplayModal(fileTransferViewModel2), TransferDisplayModal(fileTransferViewModel2), TransferDataInputModal(fileTransferViewModel2), DataReceptionModal(fileTransferViewModel2));
|
||||
return /* @__PURE__ */ createElement("div", null, DirectionSelectionModal(
|
||||
connectionViewModel2,
|
||||
fileTransferViewModel2
|
||||
), FileSelectionModal(fileTransferViewModel2), TransferDataDisplayModal(fileTransferViewModel2), TransferDisplayModal(fileTransferViewModel2), TransferDataInputModal(fileTransferViewModel2), DataReceptionModal(fileTransferViewModel2));
|
||||
}
|
||||
function DirectionSelectionModal(connectionViewModel2, fileTransferViewModel2) {
|
||||
const isPresented = createProxyState(
|
||||
@@ -4984,7 +5182,14 @@
|
||||
[connectionViewModel2.isConnected],
|
||||
() => connectionViewModel2.isConnected.value == false
|
||||
);
|
||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("p", { class: "error", "toggle:hidden": connectionViewModel2.isConnected }, translations.dataTransferModal.notConnectedError), /* @__PURE__ */ createElement(
|
||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement(
|
||||
"p",
|
||||
{
|
||||
class: "error",
|
||||
"toggle:hidden": connectionViewModel2.isConnected
|
||||
},
|
||||
translations.dataTransferModal.notConnectedError
|
||||
), /* @__PURE__ */ createElement(
|
||||
"div",
|
||||
{
|
||||
class: "flex-column gap content-margin-bottom",
|
||||
@@ -5031,7 +5236,9 @@
|
||||
function toggle() {
|
||||
isSelected.value = !isSelected.value;
|
||||
}
|
||||
return /* @__PURE__ */ createElement("button", { class: "tile", "toggle:selected": isSelected, "on:click": toggle }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", { class: "ellipsis" }, fileOption.label), /* @__PURE__ */ createElement("span", { class: "secondary ellipsis" }, StorageModel.pathComponentsToString(...fileOption.path))));
|
||||
return /* @__PURE__ */ createElement("button", { class: "tile", "toggle:selected": isSelected, "on:click": toggle }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", { class: "ellipsis" }, fileOption.label), /* @__PURE__ */ createElement("span", { class: "secondary ellipsis" }, StorageModel.pathComponentsToString(
|
||||
...fileOption.path
|
||||
))));
|
||||
}
|
||||
const isPresented = createProxyState(
|
||||
[fileTransferViewModel2.presentedModal],
|
||||
@@ -5083,7 +5290,12 @@
|
||||
{
|
||||
"subscribe:innerText": fileTransferViewModel2.transferChannel
|
||||
}
|
||||
))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement("b", { "subscribe:innerText": fileTransferViewModel2.transferKey }))))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
|
||||
))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement(
|
||||
"b",
|
||||
{
|
||||
"subscribe:innerText": fileTransferViewModel2.transferKey
|
||||
}
|
||||
))))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
|
||||
"button",
|
||||
{
|
||||
class: "flex",
|
||||
@@ -5279,7 +5491,9 @@
|
||||
};
|
||||
this.storageModel = storageModel2;
|
||||
this.connectionModel = connectionModel2;
|
||||
this.connectionModel.messageHandlerManager.addHandler(this.handleMessage);
|
||||
this.connectionModel.messageHandlerManager.addHandler(
|
||||
this.handleMessage
|
||||
);
|
||||
}
|
||||
};
|
||||
var FileDataReference = {
|
||||
@@ -5399,7 +5613,15 @@
|
||||
},
|
||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "sync_alt"),
|
||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.transferDataButton))
|
||||
), /* @__PURE__ */ createElement("button", { class: "tile flex-no", "on:click": storageViewModel2.showStorageModal }, /* @__PURE__ */ createElement("span", { class: "icon" }, "hard_drive_2"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.manageStorageButton))), /* @__PURE__ */ createElement("div", { class: "mobile-only" }, /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "ghost width-50", "on:click": scrollToChat }, translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
|
||||
), /* @__PURE__ */ createElement(
|
||||
"button",
|
||||
{
|
||||
class: "tile flex-no",
|
||||
"on:click": storageViewModel2.showStorageModal
|
||||
},
|
||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "hard_drive_2"),
|
||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.manageStorageButton))
|
||||
), /* @__PURE__ */ createElement("div", { class: "mobile-only" }, /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "ghost width-50", "on:click": scrollToChat }, translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
|
||||
const chatSection = /* @__PURE__ */ createElement("div", { id: "chat-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.chatsHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
||||
"input",
|
||||
{
|
||||
@@ -5611,17 +5833,23 @@
|
||||
this.coreViewModel = coreViewModel;
|
||||
// state
|
||||
this.isShowingStorageModal = new State(false);
|
||||
this.selectedPath = new State(PATH_COMPONENT_SEPARATOR);
|
||||
this.selectedPath = new State(
|
||||
PATH_COMPONENT_SEPARATOR
|
||||
);
|
||||
this.didMakeChanges = new State(false);
|
||||
this.lastDeletedItemPath = new State("");
|
||||
// methods
|
||||
this.getSelectedItemContent = () => {
|
||||
const path = StorageModel.stringToPathComponents(this.selectedPath.value);
|
||||
const path = StorageModel.stringToPathComponents(
|
||||
this.selectedPath.value
|
||||
);
|
||||
const content = this.storageModel.read(path);
|
||||
return (content ?? translations.storage.notAFile) || translations.storage.contentEmpty;
|
||||
};
|
||||
this.deleteSelectedItem = () => {
|
||||
const path = StorageModel.stringToPathComponents(this.selectedPath.value);
|
||||
const path = StorageModel.stringToPathComponents(
|
||||
this.selectedPath.value
|
||||
);
|
||||
this.lastDeletedItemPath.value = this.selectedPath.value;
|
||||
this.storageModel.removeRecursively(path);
|
||||
this.didMakeChanges.value = true;
|
||||
@@ -5699,9 +5927,7 @@
|
||||
if (primaryChannel == null) return;
|
||||
const parsedPriamryChannel = parseOrFallback(primaryChannel);
|
||||
const chatModel = this.chatListModel.createChat(parsedPriamryChannel);
|
||||
const secondaryChannelString = getLocalStorageItemAndClear(
|
||||
storageKeys.secondaryChannels(id)
|
||||
);
|
||||
const secondaryChannelString = getLocalStorageItemAndClear(storageKeys.secondaryChannels(id));
|
||||
if (secondaryChannelString != null) {
|
||||
const potentialSecondaryChannels = parseArray(
|
||||
secondaryChannelString
|
||||
@@ -5744,7 +5970,9 @@
|
||||
};
|
||||
chatModel.addMessage(convertedChatMessage);
|
||||
if (status == "outbox" /* Outbox */) {
|
||||
this.connectionModel.sendMessageOrStore(convertedChatMessage);
|
||||
this.connectionModel.sendMessageOrStore(
|
||||
convertedChatMessage
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -5789,7 +6017,9 @@
|
||||
time: potentialVersion.time ?? ""
|
||||
};
|
||||
console.log(convertedTaskFileContent);
|
||||
chatModel.fileModel.handleFileContent(convertedTaskFileContent);
|
||||
chatModel.fileModel.handleFileContent(
|
||||
convertedTaskFileContent
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Comms",
|
||||
"display": "standalone",
|
||||
"display": "fullscreen",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon.png",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import ChatModel, { ChatMessage } from "./chatModel";
|
||||
import StorageModel, { StorageModelSubPath, filePaths } from "../Global/storageModel";
|
||||
import StorageModel, {
|
||||
StorageModelSubPath,
|
||||
filePaths,
|
||||
} from "../Global/storageModel";
|
||||
|
||||
import ConnectionModel from "../Global/connectionModel";
|
||||
import { Message } from "udn-frontend";
|
||||
@@ -22,7 +25,7 @@ export default class ChatListModel {
|
||||
if (body == undefined) return;
|
||||
|
||||
this.routeMessageToCorrectChatModel(channel, (chatModel: ChatModel) =>
|
||||
chatModel.handleMessage(body)
|
||||
chatModel.handleMessage(body),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,14 +33,14 @@ export default class ChatListModel {
|
||||
const channel: string = chatMessage.channel;
|
||||
|
||||
this.routeMessageToCorrectChatModel(channel, (chatModel: ChatModel) =>
|
||||
chatModel.handleMessageSent(chatMessage)
|
||||
chatModel.handleMessageSent(chatMessage),
|
||||
);
|
||||
};
|
||||
|
||||
// methods
|
||||
routeMessageToCorrectChatModel = (
|
||||
channel: string,
|
||||
fn: (chatModel: ChatModel) => void
|
||||
fn: (chatModel: ChatModel) => void,
|
||||
): void => {
|
||||
const allChannels = channel.split("/");
|
||||
|
||||
@@ -63,7 +66,7 @@ export default class ChatListModel {
|
||||
this.connectionModel,
|
||||
this.settingsModel,
|
||||
this,
|
||||
id
|
||||
id,
|
||||
);
|
||||
chatModel.setPrimaryChannel(primaryChannel);
|
||||
|
||||
@@ -77,7 +80,10 @@ export default class ChatListModel {
|
||||
|
||||
// load
|
||||
loadChats = (): void => {
|
||||
const chatDir = StorageModel.getPath(StorageModelSubPath.Chat, filePaths.chat.base);
|
||||
const chatDir = StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.base,
|
||||
);
|
||||
const chatIds = this.storageModel.list(chatDir);
|
||||
for (const chatId of chatIds) {
|
||||
const chatModel = new ChatModel(
|
||||
@@ -85,7 +91,7 @@ export default class ChatListModel {
|
||||
this.connectionModel,
|
||||
this.settingsModel,
|
||||
this,
|
||||
chatId
|
||||
chatId,
|
||||
);
|
||||
this.addChatModel(chatModel);
|
||||
}
|
||||
@@ -95,7 +101,7 @@ export default class ChatListModel {
|
||||
constructor(
|
||||
storageModel: StorageModel,
|
||||
settingsModel: SettingsModel,
|
||||
connectionModel: ConnectionModel
|
||||
connectionModel: ConnectionModel,
|
||||
) {
|
||||
this.storageModel = storageModel;
|
||||
this.settingsModel = settingsModel;
|
||||
@@ -104,7 +110,7 @@ export default class ChatListModel {
|
||||
|
||||
connectionModel.messageHandlerManager.addHandler(this.messageHandler);
|
||||
connectionModel.messageSentHandlerManager.addHandler(
|
||||
this.messageSentHandler
|
||||
this.messageSentHandler,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+34
-21
@@ -34,7 +34,8 @@ export default class ChatModel {
|
||||
info: ChatInfo;
|
||||
color: Color;
|
||||
|
||||
chatMessageHandlerManager: HandlerManager<ChatMessage> = new HandlerManager();
|
||||
chatMessageHandlerManager: HandlerManager<ChatMessage> =
|
||||
new HandlerManager();
|
||||
|
||||
get secondaryChannels(): string[] {
|
||||
return this.info.secondaryChannels.sort(localeCompare);
|
||||
@@ -44,28 +45,28 @@ export default class ChatModel {
|
||||
getBasePath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.chatBase(this.id)
|
||||
filePaths.chat.chatBase(this.id),
|
||||
);
|
||||
};
|
||||
|
||||
getInfoPath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.info(this.id)
|
||||
filePaths.chat.info(this.id),
|
||||
);
|
||||
};
|
||||
|
||||
getColorPath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.color(this.id)
|
||||
filePaths.chat.color(this.id),
|
||||
);
|
||||
};
|
||||
|
||||
getMessageDirPath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.messages(this.id)
|
||||
filePaths.chat.messages(this.id),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -77,7 +78,7 @@ export default class ChatModel {
|
||||
handleMessage = (body: string): void => {
|
||||
const chatMessage: ChatMessage | null = parseValidObject(
|
||||
body,
|
||||
ChatMessageReference
|
||||
ChatMessageReference,
|
||||
);
|
||||
if (chatMessage == null) return;
|
||||
|
||||
@@ -127,12 +128,14 @@ export default class ChatModel {
|
||||
}
|
||||
|
||||
// file
|
||||
this.fileModel.handleStringifiedFileContent(chatMessage.stringifiedFile);
|
||||
this.fileModel.handleStringifiedFileContent(
|
||||
chatMessage.stringifiedFile,
|
||||
);
|
||||
};
|
||||
|
||||
sendMessage = async (
|
||||
body: string,
|
||||
fileContent?: FileContent<string>
|
||||
fileContent?: FileContent<string>,
|
||||
): Promise<boolean> => {
|
||||
const senderName = this.settingsModel.username;
|
||||
if (senderName == "") return false;
|
||||
@@ -149,7 +152,7 @@ export default class ChatModel {
|
||||
senderName,
|
||||
this.info.encryptionKey,
|
||||
body,
|
||||
fileContent
|
||||
fileContent,
|
||||
);
|
||||
|
||||
this.addMessage(chatMessage);
|
||||
@@ -160,11 +163,11 @@ export default class ChatModel {
|
||||
decryptMessage = async (chatMessage: ChatMessage): Promise<void> => {
|
||||
const decryptedBody: string = await decryptString(
|
||||
chatMessage.body,
|
||||
this.info.encryptionKey
|
||||
this.info.encryptionKey,
|
||||
);
|
||||
const decryptedFile: string = await decryptString(
|
||||
chatMessage.stringifiedFile ?? "",
|
||||
this.info.encryptionKey
|
||||
this.info.encryptionKey,
|
||||
);
|
||||
chatMessage.body = decryptedBody;
|
||||
chatMessage.stringifiedFile = decryptedFile;
|
||||
@@ -177,7 +180,7 @@ export default class ChatModel {
|
||||
markRead = (): void => {
|
||||
this.info.hasUnreadMessages = false;
|
||||
this.storeInfo();
|
||||
}
|
||||
};
|
||||
|
||||
// storage
|
||||
storeInfo = (): void => {
|
||||
@@ -201,7 +204,7 @@ export default class ChatModel {
|
||||
loadInfo = (): void => {
|
||||
const info: ChatInfo | null = this.storageModel.readStringifiable(
|
||||
this.getInfoPath(),
|
||||
ChatInfoReference
|
||||
ChatInfoReference,
|
||||
);
|
||||
if (info != null) {
|
||||
this.info = info;
|
||||
@@ -222,7 +225,7 @@ export default class ChatModel {
|
||||
|
||||
get messages(): ChatMessage[] {
|
||||
const messageIds: string[] = this.storageModel.list(
|
||||
this.getMessageDirPath()
|
||||
this.getMessageDirPath(),
|
||||
);
|
||||
if (!Array.isArray(messageIds)) return [];
|
||||
|
||||
@@ -230,12 +233,15 @@ export default class ChatModel {
|
||||
for (const messageId of messageIds) {
|
||||
const messagePath: string[] = this.getMessagePath(messageId);
|
||||
const chatMessage: ChatMessage | any =
|
||||
this.storageModel.readStringifiable(messagePath, ChatMessageReference);
|
||||
this.storageModel.readStringifiable(
|
||||
messagePath,
|
||||
ChatMessageReference,
|
||||
);
|
||||
chatMessages.push(chatMessage);
|
||||
}
|
||||
|
||||
const sorted = chatMessages.sort((a, b) =>
|
||||
a.dateSent.localeCompare(b.dateSent)
|
||||
a.dateSent.localeCompare(b.dateSent),
|
||||
);
|
||||
return sorted;
|
||||
}
|
||||
@@ -246,7 +252,7 @@ export default class ChatModel {
|
||||
connectionModel: ConnectionModel,
|
||||
settingsModel: SettingsModel,
|
||||
chatListModel: ChatListModel,
|
||||
chatId: string
|
||||
chatId: string,
|
||||
) {
|
||||
this.id = chatId;
|
||||
this.connectionModel = connectionModel;
|
||||
@@ -258,7 +264,11 @@ export default class ChatModel {
|
||||
this.loadColor();
|
||||
this.subscribe();
|
||||
|
||||
this.fileModel = new FileModel(this.storageModel, this.settingsModel, this);
|
||||
this.fileModel = new FileModel(
|
||||
this.storageModel,
|
||||
this.settingsModel,
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
// utility
|
||||
@@ -278,7 +288,7 @@ export default class ChatModel {
|
||||
sender: string,
|
||||
encryptionKey: string,
|
||||
body: string,
|
||||
fileContent?: FileContent<string>
|
||||
fileContent?: FileContent<string>,
|
||||
): Promise<ChatMessage> => {
|
||||
const chatMessage: ChatMessage = {
|
||||
dataVersion: DATA_VERSION,
|
||||
@@ -299,10 +309,13 @@ export default class ChatModel {
|
||||
}
|
||||
|
||||
if (encryptionKey != "") {
|
||||
chatMessage.body = await encryptString(chatMessage.body, encryptionKey);
|
||||
chatMessage.body = await encryptString(
|
||||
chatMessage.body,
|
||||
encryptionKey,
|
||||
);
|
||||
chatMessage.stringifiedFile = await encryptString(
|
||||
chatMessage.stringifiedFile,
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,12 +66,17 @@ export default class BoardsAndTasksModel {
|
||||
// handlers
|
||||
handleFileContent = (fileContent: FileContent<string>): void => {
|
||||
if (
|
||||
checkMatchesObjectStructure(fileContent, BoardInfoFileContentReference) ==
|
||||
true
|
||||
checkMatchesObjectStructure(
|
||||
fileContent,
|
||||
BoardInfoFileContentReference,
|
||||
) == true
|
||||
) {
|
||||
this.handleBoard(fileContent as BoardInfoFileContent);
|
||||
} else if (
|
||||
checkMatchesObjectStructure(fileContent, TaskFileContentReference) == true
|
||||
checkMatchesObjectStructure(
|
||||
fileContent,
|
||||
TaskFileContentReference,
|
||||
) == true
|
||||
) {
|
||||
this.handleTask(fileContent as TaskFileContent);
|
||||
}
|
||||
@@ -91,7 +96,7 @@ export default class BoardsAndTasksModel {
|
||||
BoardsAndTasksModel.createBoardInfoFileContent(
|
||||
v4(),
|
||||
name,
|
||||
Color.Standard
|
||||
Color.Standard,
|
||||
);
|
||||
return boardInfoFileContent;
|
||||
};
|
||||
@@ -112,14 +117,15 @@ export default class BoardsAndTasksModel {
|
||||
|
||||
// add to list
|
||||
const boardDirectoryPath: string[] = this.getBoardDirectoryPath(
|
||||
boardInfoFileContent.fileId
|
||||
boardInfoFileContent.fileId,
|
||||
);
|
||||
this.storageModel.write(boardDirectoryPath, "");
|
||||
};
|
||||
|
||||
deleteBoard = (boardId: string): void => {
|
||||
const boardFilePath: string[] = this.getBoardFilePath(boardId);
|
||||
const boardDirectoryPath: string[] = this.getBoardDirectoryPath(boardId);
|
||||
const boardDirectoryPath: string[] =
|
||||
this.getBoardDirectoryPath(boardId);
|
||||
|
||||
this.storageModel.removeRecursively(boardFilePath);
|
||||
this.storageModel.removeRecursively(boardDirectoryPath);
|
||||
@@ -135,16 +141,17 @@ export default class BoardsAndTasksModel {
|
||||
const boardInfoFileContentOrNull: BoardInfoFileContent | null =
|
||||
this.fileModel.getLatestFileContent(
|
||||
fileId,
|
||||
BoardInfoFileContentReference
|
||||
BoardInfoFileContentReference,
|
||||
);
|
||||
return boardInfoFileContentOrNull;
|
||||
};
|
||||
|
||||
getBoardName = (boardId: string): string => {
|
||||
const boardInfo: BoardInfoFileContent|null = this.getBoardInfo(boardId);
|
||||
const boardInfo: BoardInfoFileContent | null =
|
||||
this.getBoardInfo(boardId);
|
||||
if (boardInfo == null) return "";
|
||||
return boardInfo.name;
|
||||
}
|
||||
};
|
||||
|
||||
//tasks
|
||||
createTask = (boardId: string): TaskFileContent => {
|
||||
@@ -170,7 +177,7 @@ export default class BoardsAndTasksModel {
|
||||
// add to board
|
||||
const taskReferencePath: string[] = this.getTaskReferencePath(
|
||||
taskFileContent.boardId,
|
||||
taskFileContent.fileId
|
||||
taskFileContent.fileId,
|
||||
);
|
||||
this.storageModel.write(taskReferencePath, "");
|
||||
|
||||
@@ -191,19 +198,22 @@ export default class BoardsAndTasksModel {
|
||||
|
||||
getLatestTaskFileContent = (taskId: string): TaskFileContent | null => {
|
||||
const taskFileContentOrNull: TaskFileContent | null =
|
||||
this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
|
||||
this.fileModel.getLatestFileContent(
|
||||
taskId,
|
||||
TaskFileContentReference,
|
||||
);
|
||||
return taskFileContentOrNull;
|
||||
};
|
||||
|
||||
getSpecificTaskFileContent = (
|
||||
taskId: string,
|
||||
versionId: string
|
||||
versionId: string,
|
||||
): TaskFileContent | null => {
|
||||
const taskFileContentOrNull: TaskFileContent | null =
|
||||
this.fileModel.getFileContent(
|
||||
taskId,
|
||||
versionId,
|
||||
TaskFileContentReference
|
||||
TaskFileContentReference,
|
||||
);
|
||||
return taskFileContentOrNull;
|
||||
};
|
||||
@@ -218,7 +228,7 @@ export default class BoardsAndTasksModel {
|
||||
deleteTaskReference = (boardId: string, taskId: string): void => {
|
||||
const taskReferencePath: string[] = this.getTaskReferencePath(
|
||||
boardId,
|
||||
taskId
|
||||
taskId,
|
||||
);
|
||||
this.storageModel.removeRecursively(taskReferencePath);
|
||||
};
|
||||
@@ -228,7 +238,7 @@ export default class BoardsAndTasksModel {
|
||||
storageModel: StorageModel,
|
||||
settingsModel: SettingsModel,
|
||||
chatModel: ChatModel,
|
||||
fileModel: FileModel
|
||||
fileModel: FileModel,
|
||||
) {
|
||||
this.storageModel = storageModel;
|
||||
this.settingsModel = settingsModel;
|
||||
@@ -238,7 +248,7 @@ export default class BoardsAndTasksModel {
|
||||
this.calendarModel = new CalendarModel(
|
||||
this.storageModel,
|
||||
this.settingsModel,
|
||||
this.fileModel
|
||||
this.fileModel,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -246,12 +256,10 @@ export default class BoardsAndTasksModel {
|
||||
static createBoardInfoFileContent = (
|
||||
fileId: string,
|
||||
name: string,
|
||||
color: Color
|
||||
color: Color,
|
||||
): BoardInfoFileContent => {
|
||||
const fileContent: FileContent<"board-info"> = FileModel.createFileContent(
|
||||
fileId,
|
||||
"board-info"
|
||||
);
|
||||
const fileContent: FileContent<"board-info"> =
|
||||
FileModel.createFileContent(fileId, "board-info");
|
||||
return {
|
||||
...fileContent,
|
||||
|
||||
@@ -263,11 +271,11 @@ export default class BoardsAndTasksModel {
|
||||
static createTaskFileContent = (
|
||||
fileId: string,
|
||||
name: string,
|
||||
boardId: string
|
||||
boardId: string,
|
||||
): TaskFileContent => {
|
||||
const fileContent: FileContent<"task"> = FileModel.createFileContent(
|
||||
fileId,
|
||||
"task"
|
||||
"task",
|
||||
);
|
||||
return {
|
||||
...fileContent,
|
||||
|
||||
@@ -13,7 +13,9 @@ export default class CalendarModel {
|
||||
|
||||
// paths
|
||||
getBasePath = (): string[] => {
|
||||
return this.fileModel.getModelContainerPath(FileModelSubPath.ModelCalendar);
|
||||
return this.fileModel.getModelContainerPath(
|
||||
FileModelSubPath.ModelCalendar,
|
||||
);
|
||||
};
|
||||
|
||||
getViewPath = (): string[] => {
|
||||
@@ -33,7 +35,7 @@ export default class CalendarModel {
|
||||
if (taskFileContent.date == undefined) return;
|
||||
|
||||
const monthString: string = CalendarModel.isoToMonthString(
|
||||
taskFileContent.date
|
||||
taskFileContent.date,
|
||||
);
|
||||
|
||||
const monthPath: string[] = this.getMonthPath(monthString);
|
||||
@@ -57,7 +59,7 @@ export default class CalendarModel {
|
||||
generateMonthGrid = <T>(
|
||||
year: number,
|
||||
month: number,
|
||||
defaultValueCreator: () => T
|
||||
defaultValueCreator: () => T,
|
||||
): MonthGrid<T> => {
|
||||
// get today
|
||||
const date: Date = new Date();
|
||||
@@ -71,7 +73,7 @@ export default class CalendarModel {
|
||||
|
||||
const firstWeekdayOfMonth = date.getDay();
|
||||
const firstDayOfWeekSetting: number = parseInt(
|
||||
this.settingsModel.firstDayOfWeek
|
||||
this.settingsModel.firstDayOfWeek,
|
||||
);
|
||||
|
||||
const offset: number =
|
||||
@@ -95,7 +97,7 @@ export default class CalendarModel {
|
||||
|
||||
for (let i = 0; i < daysInMonth; i++) {
|
||||
const paddedDate: string = CalendarModel.padDateOrMonth(
|
||||
(i + 1).toString()
|
||||
(i + 1).toString(),
|
||||
);
|
||||
grid.days[paddedDate] = defaultValueCreator();
|
||||
}
|
||||
@@ -107,7 +109,7 @@ export default class CalendarModel {
|
||||
constructor(
|
||||
storageModel: StorageModel,
|
||||
settingsModel: SettingsModel,
|
||||
fileModel: FileModel
|
||||
fileModel: FileModel,
|
||||
) {
|
||||
this.storageModel = storageModel;
|
||||
this.settingsModel = settingsModel;
|
||||
@@ -135,7 +137,7 @@ export default class CalendarModel {
|
||||
static getISODateString = (
|
||||
year: string,
|
||||
month: string,
|
||||
date: string
|
||||
date: string,
|
||||
): string => {
|
||||
const monthString: string = CalendarModel.getMonthString(year, month);
|
||||
const paddedDate: string = CalendarModel.padDateOrMonth(date);
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class FileModel {
|
||||
getBasePath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.files(this.chatModel.id)
|
||||
filePaths.chat.files(this.chatModel.id),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ export default class FileModel {
|
||||
handleStringifiedFileContent = (stringifiedFileContent: string): void => {
|
||||
const fileContent: FileContent<string> | null = parseValidObject(
|
||||
stringifiedFileContent,
|
||||
FileContentReference
|
||||
FileContentReference,
|
||||
);
|
||||
if (fileContent == null) return;
|
||||
|
||||
@@ -77,7 +77,7 @@ export default class FileModel {
|
||||
storeFileContent = (fileContent: FileContent<string>): boolean => {
|
||||
const fileContentPath: string[] = this.getFileContentPath(
|
||||
fileContent.fileId,
|
||||
fileContent.fileContentId
|
||||
fileContent.fileContentId,
|
||||
);
|
||||
|
||||
// check if fileContent already exists
|
||||
@@ -100,7 +100,7 @@ export default class FileModel {
|
||||
};
|
||||
|
||||
selectLatestFileContentId = (
|
||||
fileContentIds: string[]
|
||||
fileContentIds: string[],
|
||||
): string | undefined => {
|
||||
return fileContentIds[fileContentIds.length - 1];
|
||||
};
|
||||
@@ -108,19 +108,22 @@ export default class FileModel {
|
||||
getFileContent = <T extends FileContent<string>>(
|
||||
fileId: string,
|
||||
fileContentName: string,
|
||||
reference: T
|
||||
reference: T,
|
||||
): T | null => {
|
||||
const filePath: string[] = this.getFileContentPath(fileId, fileContentName);
|
||||
const filePath: string[] = this.getFileContentPath(
|
||||
fileId,
|
||||
fileContentName,
|
||||
);
|
||||
const fileContentOrNull: T | null = this.storageModel.readStringifiable(
|
||||
filePath,
|
||||
reference
|
||||
reference,
|
||||
);
|
||||
return fileContentOrNull;
|
||||
};
|
||||
|
||||
getLatestFileContent = <T extends FileContent<string>>(
|
||||
fileId: string,
|
||||
reference: T
|
||||
reference: T,
|
||||
): T | null => {
|
||||
const fileContentsIds: string[] = this.listFileContentIds(fileId);
|
||||
const latestFileContentId: string | undefined =
|
||||
@@ -130,7 +133,7 @@ export default class FileModel {
|
||||
const fileContent: T | null = this.getFileContent(
|
||||
fileId,
|
||||
latestFileContentId,
|
||||
reference
|
||||
reference,
|
||||
);
|
||||
return fileContent;
|
||||
};
|
||||
@@ -139,7 +142,7 @@ export default class FileModel {
|
||||
constructor(
|
||||
storageModel: StorageModel,
|
||||
settingsModel: SettingsModel,
|
||||
chatModel: ChatModel
|
||||
chatModel: ChatModel,
|
||||
) {
|
||||
this.chatModel = chatModel;
|
||||
this.settingsModel = settingsModel;
|
||||
@@ -149,7 +152,7 @@ export default class FileModel {
|
||||
this.storageModel,
|
||||
this.settingsModel,
|
||||
chatModel,
|
||||
this
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,10 +163,11 @@ export default class FileModel {
|
||||
|
||||
static createFileContent = <T extends string>(
|
||||
fileId: string,
|
||||
type: T
|
||||
type: T,
|
||||
): FileContent<T> => {
|
||||
const creationDate: string = createTimestamp();
|
||||
const fileContentId: string = FileModel.generateFileContentId(creationDate);
|
||||
const fileContentId: string =
|
||||
FileModel.generateFileContentId(creationDate);
|
||||
|
||||
return {
|
||||
dataVersion: DATA_VERSION,
|
||||
@@ -187,8 +191,7 @@ export enum FileModelSubPath {
|
||||
|
||||
// types
|
||||
export interface FileContent<T extends string>
|
||||
extends ValidObject,
|
||||
StringEntryObject {
|
||||
extends ValidObject, StringEntryObject {
|
||||
fileId: string;
|
||||
fileContentId: string;
|
||||
creationDate: string;
|
||||
|
||||
@@ -8,6 +8,8 @@ import UDNFrontend, { Message } from "udn-frontend";
|
||||
export default class ConnectionModel {
|
||||
udn: UDNFrontend;
|
||||
storageModel: StorageModel;
|
||||
reconnectInterval: number | undefined = undefined;
|
||||
shouldAttemptReconnect: boolean = false;
|
||||
|
||||
// data
|
||||
get isConnected(): boolean {
|
||||
@@ -20,7 +22,8 @@ export default class ConnectionModel {
|
||||
|
||||
connectionChangeHandlerManager: HandlerManager<void> = new HandlerManager();
|
||||
messageHandlerManager: HandlerManager<Message> = new HandlerManager();
|
||||
messageSentHandlerManager: HandlerManager<ChatMessage> = new HandlerManager();
|
||||
messageSentHandlerManager: HandlerManager<ChatMessage> =
|
||||
new HandlerManager();
|
||||
|
||||
channelsToSubscribe: Set<string> = new Set();
|
||||
|
||||
@@ -33,12 +36,25 @@ export default class ConnectionModel {
|
||||
console.log("connection status:", this.isConnected, this.address);
|
||||
this.connectionChangeHandlerManager.trigger();
|
||||
|
||||
if (this.isConnected == false) return;
|
||||
if (this.isConnected == false) {
|
||||
if (this.shouldAttemptReconnect == false) return;
|
||||
|
||||
this.reconnectInterval = setInterval(() => {
|
||||
if (this.isConnected == true) {
|
||||
clearInterval(this.reconnectInterval);
|
||||
return;
|
||||
}
|
||||
this.reconnect();
|
||||
}, 5000);
|
||||
|
||||
return;
|
||||
}
|
||||
if (this.address == undefined) return;
|
||||
|
||||
this.storeAddress(this.address);
|
||||
this.sendSubscriptionRequest();
|
||||
this.sendMessagesInOutbox();
|
||||
this.shouldAttemptReconnect = true;
|
||||
};
|
||||
|
||||
// connection
|
||||
@@ -52,16 +68,27 @@ export default class ConnectionModel {
|
||||
// do not reconnect
|
||||
const reconnectAddressPath: string[] = StorageModel.getPath(
|
||||
StorageModelSubPath.ConnectionModel,
|
||||
filePaths.connectionModel.reconnectAddress
|
||||
filePaths.connectionModel.reconnectAddress,
|
||||
);
|
||||
this.storageModel.remove(reconnectAddressPath);
|
||||
this.shouldAttemptReconnect = false;
|
||||
};
|
||||
|
||||
reconnect = (): void => {
|
||||
const reconnectAddressPath: string[] = this.getReconnectAddressPath();
|
||||
const reconnectAddress: string | null =
|
||||
this.storageModel.read(reconnectAddressPath);
|
||||
if (reconnectAddress == null) return;
|
||||
|
||||
console.log("reconnecting...");
|
||||
this.connect(reconnectAddress);
|
||||
};
|
||||
|
||||
// mailbox
|
||||
getMailboxPath = (address: string): string[] => {
|
||||
const mailboxDirPath = StorageModel.getPath(
|
||||
StorageModelSubPath.ConnectionModel,
|
||||
filePaths.connectionModel.mailboxes
|
||||
filePaths.connectionModel.mailboxes,
|
||||
);
|
||||
const mailboxFilePath = [...mailboxDirPath, address];
|
||||
return mailboxFilePath;
|
||||
@@ -75,7 +102,9 @@ export default class ConnectionModel {
|
||||
connectMailbox = (): void => {
|
||||
if (this.address == undefined) return;
|
||||
|
||||
const mailboxId = this.storageModel.read(this.getMailboxPath(this.address));
|
||||
const mailboxId = this.storageModel.read(
|
||||
this.getMailboxPath(this.address),
|
||||
);
|
||||
console.log("connecting mailbox", mailboxId);
|
||||
if (mailboxId == null) return this.requestNewMailbox();
|
||||
|
||||
@@ -107,7 +136,7 @@ export default class ConnectionModel {
|
||||
getOutboxPath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.ConnectionModel,
|
||||
filePaths.connectionModel.outbox
|
||||
filePaths.connectionModel.outbox,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -120,7 +149,7 @@ export default class ConnectionModel {
|
||||
const chatMessage: ChatMessage | null =
|
||||
this.storageModel.readStringifiable(
|
||||
[...outboxPath, messageId],
|
||||
ChatMessageReference
|
||||
ChatMessageReference,
|
||||
);
|
||||
|
||||
if (chatMessage == null) continue;
|
||||
@@ -163,7 +192,7 @@ export default class ConnectionModel {
|
||||
const stringifiedBody: string = stringify(chatMessage);
|
||||
const isSent: boolean = this.sendPlainMessage(
|
||||
chatMessage.channel,
|
||||
stringifiedBody
|
||||
stringifiedBody,
|
||||
);
|
||||
if (isSent) this.messageSentHandlerManager.trigger(chatMessage);
|
||||
return isSent;
|
||||
@@ -177,7 +206,7 @@ export default class ConnectionModel {
|
||||
getPreviousAddressPath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.ConnectionModel,
|
||||
filePaths.connectionModel.previousAddresses
|
||||
filePaths.connectionModel.previousAddresses,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -189,7 +218,7 @@ export default class ConnectionModel {
|
||||
getReconnectAddressPath = (): string[] => {
|
||||
return StorageModel.getPath(
|
||||
StorageModelSubPath.ConnectionModel,
|
||||
filePaths.connectionModel.reconnectAddress
|
||||
filePaths.connectionModel.reconnectAddress,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -243,13 +272,7 @@ export default class ConnectionModel {
|
||||
console.log(`using mailbox ${mailboxId}`);
|
||||
};
|
||||
|
||||
// reconnect
|
||||
const reconnectAddressPath: string[] = this.getReconnectAddressPath();
|
||||
const reconnectAddress: string | null =
|
||||
storageModel.read(reconnectAddressPath);
|
||||
if (reconnectAddress != null) {
|
||||
this.connect(reconnectAddress);
|
||||
}
|
||||
this.reconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ export default class FileTransferModel {
|
||||
|
||||
const decrypted: string = await decryptString(
|
||||
encryptedFileData,
|
||||
this.transferData.key
|
||||
this.transferData.key,
|
||||
);
|
||||
console.log(decrypted);
|
||||
|
||||
const parsed: any = parse(decrypted);
|
||||
const isFileData: boolean = checkMatchesObjectStructure(
|
||||
parsed,
|
||||
FileDataReference
|
||||
FileDataReference,
|
||||
);
|
||||
if (isFileData == false) return;
|
||||
|
||||
@@ -65,7 +65,7 @@ export default class FileTransferModel {
|
||||
this.storageModel.write(fileData.path, fileData.body);
|
||||
|
||||
const pathString: string = StorageModel.pathComponentsToString(
|
||||
...fileData.path
|
||||
...fileData.path,
|
||||
);
|
||||
this.fileHandlerManager.trigger(pathString);
|
||||
};
|
||||
@@ -73,13 +73,13 @@ export default class FileTransferModel {
|
||||
// sending
|
||||
sendFiles = (
|
||||
directoryPaths: IteratorObject<string[]>,
|
||||
callback: (path: string) => void
|
||||
callback: (path: string) => void,
|
||||
): void => {
|
||||
for (const directoryPath of directoryPaths) {
|
||||
this.storageModel.recurse(directoryPath, (filePath: string[]) => {
|
||||
this.sendFile(filePath);
|
||||
const pathString: string = StorageModel.pathComponentsToString(
|
||||
...filePath
|
||||
...filePath,
|
||||
);
|
||||
callback(pathString);
|
||||
});
|
||||
@@ -98,11 +98,11 @@ export default class FileTransferModel {
|
||||
const stringifiedFileData: string = stringify(fileData);
|
||||
const encryptedFileData: string = await encryptString(
|
||||
stringifiedFileData,
|
||||
this.transferData.key
|
||||
this.transferData.key,
|
||||
);
|
||||
this.connectionModel.sendPlainMessage(
|
||||
this.transferData.channel,
|
||||
encryptedFileData
|
||||
encryptedFileData,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -111,7 +111,9 @@ export default class FileTransferModel {
|
||||
this.storageModel = storageModel;
|
||||
this.connectionModel = connectionModel;
|
||||
|
||||
this.connectionModel.messageHandlerManager.addHandler(this.handleMessage);
|
||||
this.connectionModel.messageHandlerManager.addHandler(
|
||||
this.handleMessage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class SettingsModel {
|
||||
this.username = newValue;
|
||||
const path = StorageModel.getPath(
|
||||
StorageModelSubPath.SettingsModel,
|
||||
filePaths.settingsModel.username
|
||||
filePaths.settingsModel.username,
|
||||
);
|
||||
this.storageModel.write(path, newValue);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export default class SettingsModel {
|
||||
this.firstDayOfWeek = newValue;
|
||||
const path = StorageModel.getPath(
|
||||
StorageModelSubPath.SettingsModel,
|
||||
filePaths.settingsModel.firstDayOfWeek
|
||||
filePaths.settingsModel.firstDayOfWeek,
|
||||
);
|
||||
this.storageModel.write(path, newValue);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export default class SettingsModel {
|
||||
loadUsernam(): void {
|
||||
const path = StorageModel.getPath(
|
||||
StorageModelSubPath.SettingsModel,
|
||||
filePaths.settingsModel.username
|
||||
filePaths.settingsModel.username,
|
||||
);
|
||||
const content = this.storageModel.read(path);
|
||||
this.username = content ?? "";
|
||||
@@ -41,7 +41,7 @@ export default class SettingsModel {
|
||||
loadFirstDayofWeek(): void {
|
||||
const path = StorageModel.getPath(
|
||||
StorageModelSubPath.SettingsModel,
|
||||
filePaths.settingsModel.firstDayOfWeek
|
||||
filePaths.settingsModel.firstDayOfWeek,
|
||||
);
|
||||
const content = this.storageModel.read(path);
|
||||
this.firstDayOfWeek = content ?? "0";
|
||||
|
||||
@@ -11,7 +11,7 @@ export default class StorageModel {
|
||||
// read
|
||||
read = (pathComponents: string[]): string | null => {
|
||||
const pathString: string = StorageModel.pathComponentsToString(
|
||||
...pathComponents
|
||||
...pathComponents,
|
||||
);
|
||||
return localStorage.getItem(pathString);
|
||||
};
|
||||
@@ -19,7 +19,8 @@ export default class StorageModel {
|
||||
list = (pathComponents: string[]): string[] => {
|
||||
let currentParent: StorageEntry = this.storageEntryTree;
|
||||
for (const component of pathComponents) {
|
||||
const nextParent: StorageEntry | undefined = currentParent[component];
|
||||
const nextParent: StorageEntry | undefined =
|
||||
currentParent[component];
|
||||
if (nextParent == undefined) return [];
|
||||
currentParent = nextParent;
|
||||
}
|
||||
@@ -30,7 +31,7 @@ export default class StorageModel {
|
||||
// write
|
||||
write = (pathComponents: string[], value: string): void => {
|
||||
const pathString: string = StorageModel.pathComponentsToString(
|
||||
...pathComponents
|
||||
...pathComponents,
|
||||
);
|
||||
localStorage.setItem(pathString, value);
|
||||
this.updateTree(...pathComponents);
|
||||
@@ -38,10 +39,10 @@ export default class StorageModel {
|
||||
|
||||
remove = (
|
||||
pathComponents: string[],
|
||||
shouldInitialize: boolean = true
|
||||
shouldInitialize: boolean = true,
|
||||
): void => {
|
||||
const pathString: string = StorageModel.pathComponentsToString(
|
||||
...pathComponents
|
||||
...pathComponents,
|
||||
);
|
||||
localStorage.removeItem(pathString);
|
||||
|
||||
@@ -53,7 +54,7 @@ export default class StorageModel {
|
||||
rename = (
|
||||
sourcePathComponents: string[],
|
||||
destinationPathComponents: string[],
|
||||
shouldInitialize: boolean = true
|
||||
shouldInitialize: boolean = true,
|
||||
): boolean => {
|
||||
const content: string | null = this.read(sourcePathComponents);
|
||||
if (content == null) return false;
|
||||
@@ -92,18 +93,20 @@ export default class StorageModel {
|
||||
};
|
||||
|
||||
removeRecursively = (pathComponents: string[]): void => {
|
||||
this.recurse(pathComponents, (path: string[]) => this.remove(path, false));
|
||||
this.recurse(pathComponents, (path: string[]) =>
|
||||
this.remove(path, false),
|
||||
);
|
||||
this.initializeTree();
|
||||
};
|
||||
|
||||
renameRecursively = (
|
||||
sourcePathComponents: string[],
|
||||
destinationPathComponents: string[]
|
||||
destinationPathComponents: string[],
|
||||
): void => {
|
||||
this.recurse(sourcePathComponents, (path: string[]) => {
|
||||
// assemble destinationPath
|
||||
const relativePathOfCurrentEntity = path.slice(
|
||||
sourcePathComponents.length
|
||||
sourcePathComponents.length,
|
||||
);
|
||||
const destinationPathComponentsOfCurrentEntity = [
|
||||
...destinationPathComponents,
|
||||
@@ -117,14 +120,17 @@ export default class StorageModel {
|
||||
};
|
||||
|
||||
// stringifiable
|
||||
writeStringifiable = (pathComponents: string[], value: ValidObject): void => {
|
||||
writeStringifiable = (
|
||||
pathComponents: string[],
|
||||
value: ValidObject,
|
||||
): void => {
|
||||
const valueString: string = stringify(value);
|
||||
this.write(pathComponents, valueString);
|
||||
};
|
||||
|
||||
readStringifiable = <T extends ValidObject>(
|
||||
pathComponents: string[],
|
||||
reference: T
|
||||
reference: T,
|
||||
): T | null => {
|
||||
const valueString: string | null = this.read(pathComponents);
|
||||
if (!valueString) return null;
|
||||
@@ -140,8 +146,8 @@ export default class StorageModel {
|
||||
this.recurse([], (path: string[]) => {
|
||||
if (path[0] == DATA_VERSION) return;
|
||||
this.remove(path);
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// tree
|
||||
initializeTree = (): void => {
|
||||
@@ -149,7 +155,8 @@ export default class StorageModel {
|
||||
|
||||
this.storageEntryTree = {};
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
const components: string[] = StorageModel.stringToPathComponents(key);
|
||||
const components: string[] =
|
||||
StorageModel.stringToPathComponents(key);
|
||||
this.updateTree(...components);
|
||||
}
|
||||
};
|
||||
@@ -180,12 +187,15 @@ export default class StorageModel {
|
||||
};
|
||||
|
||||
static getFileNameFromString = (pathString: string): string => {
|
||||
const pathComponents: string[] = this.stringToPathComponents(pathString);
|
||||
const pathComponents: string[] =
|
||||
this.stringToPathComponents(pathString);
|
||||
return pathComponents[pathComponents.length - 1] || "\\";
|
||||
};
|
||||
|
||||
static pathComponentsToString = (...pathComponents: string[]): string => {
|
||||
return pathComponents.filter((x) => x != "").join(PATH_COMPONENT_SEPARATOR);
|
||||
return pathComponents
|
||||
.filter((x) => x != "")
|
||||
.join(PATH_COMPONENT_SEPARATOR);
|
||||
};
|
||||
|
||||
static stringToPathComponents = (string: string): string[] => {
|
||||
@@ -203,7 +213,7 @@ export default class StorageModel {
|
||||
|
||||
static getPath(
|
||||
locationName: StorageModelSubPath,
|
||||
filePath: string[]
|
||||
filePath: string[],
|
||||
): string[] {
|
||||
return [DATA_VERSION, locationName, ...filePath];
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ const ENCRYPTION_ALG = "AES-GCM";
|
||||
// minimal
|
||||
export async function encryptString(
|
||||
plaintext: string,
|
||||
passphrase: string
|
||||
passphrase: string,
|
||||
): Promise<string> {
|
||||
if (!window.crypto.subtle) return plaintext;
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function encryptString(
|
||||
|
||||
export async function decryptString(
|
||||
cyphertext: string,
|
||||
passphrase: string
|
||||
passphrase: string,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const encrypionData = JSON.parse(atob(cyphertext));
|
||||
@@ -47,12 +47,12 @@ export function decode(array: Uint8Array): string {
|
||||
export async function encrypt(
|
||||
iv: Uint8Array,
|
||||
key: CryptoKey,
|
||||
message: string
|
||||
message: string,
|
||||
): Promise<Uint8Array> {
|
||||
const arrayBuffer = await window.crypto.subtle.encrypt(
|
||||
{ name: ENCRYPTION_ALG, iv },
|
||||
key,
|
||||
encode(message)
|
||||
encode(message),
|
||||
);
|
||||
return new Uint8Array(arrayBuffer);
|
||||
}
|
||||
@@ -60,12 +60,12 @@ export async function encrypt(
|
||||
export async function decrypt(
|
||||
iv: Uint8Array,
|
||||
key: CryptoKey,
|
||||
cyphertext: Uint8Array
|
||||
cyphertext: Uint8Array,
|
||||
): Promise<string> {
|
||||
const arrayBuffer = await crypto.subtle.decrypt(
|
||||
{ name: ENCRYPTION_ALG, iv },
|
||||
key,
|
||||
cyphertext
|
||||
cyphertext,
|
||||
);
|
||||
return arrayBufferToString(arrayBuffer);
|
||||
}
|
||||
@@ -89,14 +89,14 @@ export function splitMergedString(mergedString: string): [Uint8Array, string] {
|
||||
// key
|
||||
export async function importKey(
|
||||
passphrase: string,
|
||||
purpose: "encrypt" | "decrypt"
|
||||
purpose: "encrypt" | "decrypt",
|
||||
): Promise<CryptoKey> {
|
||||
return await crypto.subtle.importKey(
|
||||
"raw",
|
||||
await hash(encode(passphrase)),
|
||||
{ name: ENCRYPTION_ALG },
|
||||
false,
|
||||
[purpose]
|
||||
[purpose],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ export function checkIsValidObject(object: any): boolean {
|
||||
|
||||
export function checkMatchesObjectStructure(
|
||||
objectToCheck: any,
|
||||
reference: any
|
||||
reference: any,
|
||||
): boolean {
|
||||
if (typeof reference != "object") {
|
||||
return typeof objectToCheck == typeof reference;
|
||||
@@ -27,14 +27,14 @@ export function checkMatchesObjectStructure(
|
||||
const [fisrtOfReference] = reference[key];
|
||||
const doesFirstItemMatch = checkMatchesObjectStructure(
|
||||
firstOfObjectToCheck,
|
||||
fisrtOfReference
|
||||
fisrtOfReference,
|
||||
);
|
||||
if (doesFirstItemMatch == false) return false;
|
||||
} else if (requiredType == "object") {
|
||||
// recurse into objects
|
||||
const doesNestedObjectMatch = checkMatchesObjectStructure(
|
||||
objectToCheck[key],
|
||||
reference[key]
|
||||
reference[key],
|
||||
);
|
||||
if (doesNestedObjectMatch == false) return false;
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ export type StringEntryObject = { [key: string]: Stringifiable | undefined };
|
||||
export function filterObjectsByStringEntries<T>(
|
||||
reference: StringEntryObject,
|
||||
converter: (object: T) => StringEntryObject,
|
||||
objects: T[]
|
||||
objects: T[],
|
||||
): Set<T> {
|
||||
const matches: Set<T> = new Set();
|
||||
|
||||
object_loop: for (const object of objects) {
|
||||
const doesMatch: boolean = checkDoesObjectMatchReference(
|
||||
reference,
|
||||
converter(object)
|
||||
converter(object),
|
||||
);
|
||||
if (doesMatch) matches.add(object);
|
||||
}
|
||||
@@ -39,10 +39,10 @@ export function filterObjectsByStringEntries<T>(
|
||||
|
||||
export function checkDoesObjectMatchReference(
|
||||
reference: StringEntryObject,
|
||||
stringEntryObject: StringEntryObject
|
||||
stringEntryObject: StringEntryObject,
|
||||
): boolean {
|
||||
reference_entry_loop: for (const referenceEntry of Object.entries(
|
||||
reference
|
||||
reference,
|
||||
)) {
|
||||
const [referenceKey, referenceValue] = referenceEntry;
|
||||
const stringEntryObjectValue: Stringifiable | undefined =
|
||||
@@ -71,7 +71,8 @@ export function checkDoesObjectMatchReference(
|
||||
// property must exist but be anything
|
||||
if (
|
||||
referenceValue == "" &&
|
||||
(stringEntryObjectValue == undefined || stringEntryObjectValue == "")
|
||||
(stringEntryObjectValue == undefined ||
|
||||
stringEntryObjectValue == "")
|
||||
) {
|
||||
return false;
|
||||
} else if (referenceValue == "") {
|
||||
@@ -90,7 +91,7 @@ export function checkDoesObjectMatchReference(
|
||||
export function collectObjectValuesForKey<T>(
|
||||
key: string,
|
||||
converter: (object: T) => StringEntryObject,
|
||||
objects: T[]
|
||||
objects: T[],
|
||||
): string[] {
|
||||
const values: Set<string> = new Set();
|
||||
|
||||
@@ -110,7 +111,7 @@ export function collectObjectValuesForKey<T>(
|
||||
export function filterObjectsByWords<T>(
|
||||
query: string,
|
||||
getStringsOfObject: (object: T) => string[],
|
||||
objects: T[]
|
||||
objects: T[],
|
||||
): Set<T> {
|
||||
const matches: Set<T> = new Set();
|
||||
|
||||
@@ -118,7 +119,7 @@ export function filterObjectsByWords<T>(
|
||||
const doesMatch: boolean = checkDoesObjectMatchSearch(
|
||||
query,
|
||||
getStringsOfObject,
|
||||
object
|
||||
object,
|
||||
);
|
||||
if (doesMatch) matches.add(object);
|
||||
}
|
||||
@@ -129,7 +130,7 @@ export function filterObjectsByWords<T>(
|
||||
export function checkDoesObjectMatchSearch<T>(
|
||||
query: string,
|
||||
getStringsOfObject: (object: T) => string[],
|
||||
object: T
|
||||
object: T,
|
||||
): boolean {
|
||||
if (query == "") return true;
|
||||
|
||||
@@ -250,7 +251,7 @@ export function parseValidObject<T>(string: string, reference: T): T | null {
|
||||
|
||||
const doesMatchReference: boolean = checkMatchesObjectStructure(
|
||||
parsed,
|
||||
reference
|
||||
reference,
|
||||
);
|
||||
if (doesMatchReference == false) return null;
|
||||
|
||||
|
||||
+30
-19
@@ -8,7 +8,11 @@ import ChatModel, {
|
||||
ChatMessage,
|
||||
ChatMessageStatus,
|
||||
} from "../Model/Chat/chatModel";
|
||||
import { getLocalStorageItemAndClear, parseArray, parseOrFallback } from "../Model/Utility/utility";
|
||||
import {
|
||||
getLocalStorageItemAndClear,
|
||||
parseArray,
|
||||
parseOrFallback,
|
||||
} from "../Model/Utility/utility";
|
||||
|
||||
import ChatListModel from "../Model/Chat/chatListModel";
|
||||
import ConnectionModel from "../Model/Global/connectionModel";
|
||||
@@ -30,7 +34,8 @@ export default class v1Upgrader {
|
||||
const firstDayOfWeek: string | null =
|
||||
getLocalStorageItemAndClear("first-day-of-week");
|
||||
if (firstDayOfWeek != null) {
|
||||
const parsedFirstDayOfWeek: string = parseOrFallback(firstDayOfWeek);
|
||||
const parsedFirstDayOfWeek: string =
|
||||
parseOrFallback(firstDayOfWeek);
|
||||
this.settingsModel.setFirstDayOfWeek(parsedFirstDayOfWeek);
|
||||
}
|
||||
};
|
||||
@@ -49,7 +54,8 @@ export default class v1Upgrader {
|
||||
|
||||
// chats
|
||||
migrateChats = (): void => {
|
||||
const chatIdString: string | null = getLocalStorageItemAndClear("chat-ids");
|
||||
const chatIdString: string | null =
|
||||
getLocalStorageItemAndClear("chat-ids");
|
||||
if (chatIdString == null) return;
|
||||
|
||||
const chatIds: any[] = parseArray(chatIdString);
|
||||
@@ -62,7 +68,7 @@ export default class v1Upgrader {
|
||||
migrateChatById = (id: string): void => {
|
||||
// get primary channel
|
||||
const primaryChannel: string | null = getLocalStorageItemAndClear(
|
||||
storageKeys.primaryChannel(id)
|
||||
storageKeys.primaryChannel(id),
|
||||
);
|
||||
if (primaryChannel == null) return;
|
||||
const parsedPriamryChannel: string = parseOrFallback(primaryChannel);
|
||||
@@ -72,12 +78,11 @@ export default class v1Upgrader {
|
||||
this.chatListModel.createChat(parsedPriamryChannel);
|
||||
|
||||
// secondary channels
|
||||
const secondaryChannelString: string | null = getLocalStorageItemAndClear(
|
||||
storageKeys.secondaryChannels(id)
|
||||
);
|
||||
const secondaryChannelString: string | null =
|
||||
getLocalStorageItemAndClear(storageKeys.secondaryChannels(id));
|
||||
if (secondaryChannelString != null) {
|
||||
const potentialSecondaryChannels: any[] = parseArray(
|
||||
secondaryChannelString
|
||||
secondaryChannelString,
|
||||
);
|
||||
|
||||
const confirmedSecondaryChannels: string[] = [];
|
||||
@@ -91,7 +96,7 @@ export default class v1Upgrader {
|
||||
|
||||
// encryption key
|
||||
const encryptionKey: string | null = getLocalStorageItemAndClear(
|
||||
storageKeys.encyptionKey(id)
|
||||
storageKeys.encyptionKey(id),
|
||||
);
|
||||
if (encryptionKey != null) {
|
||||
const parsedEncryptionKey: string = parseOrFallback(encryptionKey);
|
||||
@@ -104,19 +109,21 @@ export default class v1Upgrader {
|
||||
const messageOutboxString: string =
|
||||
getLocalStorageItemAndClear(storageKeys.outbox(id)) ?? "";
|
||||
const potentialMessages: any[] = parseArray(messagesString);
|
||||
const potentialMessagesInOutbox: any[] = parseArray(messageOutboxString);
|
||||
const potentialMessagesInOutbox: any[] =
|
||||
parseArray(messageOutboxString);
|
||||
|
||||
const addMessages = (
|
||||
potentialMessages: any[],
|
||||
status: ChatMessageStatus
|
||||
status: ChatMessageStatus,
|
||||
) => {
|
||||
for (const potentialMessage of potentialMessages) {
|
||||
const isV1ChatMessage: boolean = checkMatchesObjectStructure(
|
||||
potentialMessage,
|
||||
V1ChatMessageReference
|
||||
V1ChatMessageReference,
|
||||
);
|
||||
if (isV1ChatMessage == false) continue;
|
||||
const v1ChatMessage: typeof V1ChatMessageReference = potentialMessage;
|
||||
const v1ChatMessage: typeof V1ChatMessageReference =
|
||||
potentialMessage;
|
||||
|
||||
const convertedChatMessage: ChatMessage = {
|
||||
dataVersion: "v2",
|
||||
@@ -133,7 +140,9 @@ export default class v1Upgrader {
|
||||
};
|
||||
chatModel.addMessage(convertedChatMessage);
|
||||
if (status == ChatMessageStatus.Outbox) {
|
||||
this.connectionModel.sendMessageOrStore(convertedChatMessage);
|
||||
this.connectionModel.sendMessageOrStore(
|
||||
convertedChatMessage,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -152,7 +161,7 @@ export default class v1Upgrader {
|
||||
const addObjects = (potentialObjects: any[]): void => {
|
||||
const board: BoardInfoFileContent =
|
||||
chatModel.fileModel.boardsAndTasksModel.createBoard(
|
||||
translations.updater.migrated
|
||||
translations.updater.migrated,
|
||||
);
|
||||
chatModel.fileModel.boardsAndTasksModel.updateBoard(board);
|
||||
|
||||
@@ -166,7 +175,7 @@ export default class v1Upgrader {
|
||||
const objectName: string = potentialObject.title;
|
||||
|
||||
const contentVersions: any[] = Object.values(
|
||||
potentialObject.contentVersions
|
||||
potentialObject.contentVersions,
|
||||
);
|
||||
for (const potentialVersion of contentVersions) {
|
||||
const isV1MessageObjectContent: boolean =
|
||||
@@ -178,7 +187,7 @@ export default class v1Upgrader {
|
||||
|
||||
fileId: objectId,
|
||||
fileContentId: FileModel.generateFileContentId(
|
||||
potentialVersion.isoDateVersionCreated
|
||||
potentialVersion.isoDateVersionCreated,
|
||||
),
|
||||
creationDate: potentialVersion.isoDateVersionCreated,
|
||||
type: "task",
|
||||
@@ -196,7 +205,9 @@ export default class v1Upgrader {
|
||||
time: potentialVersion.time ?? "",
|
||||
};
|
||||
console.log(convertedTaskFileContent);
|
||||
chatModel.fileModel.handleFileContent(convertedTaskFileContent);
|
||||
chatModel.fileModel.handleFileContent(
|
||||
convertedTaskFileContent,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -208,7 +219,7 @@ export default class v1Upgrader {
|
||||
constructor(
|
||||
public settingsModel: SettingsModel,
|
||||
public connectionModel: ConnectionModel,
|
||||
public chatListModel: ChatListModel
|
||||
public chatListModel: ChatListModel,
|
||||
) {
|
||||
this.migrateSettings();
|
||||
this.migrateConnections();
|
||||
|
||||
@@ -20,14 +20,15 @@ export function BoardKanbanPage(boardViewModel: BoardViewModel) {
|
||||
boardViewModel.taskViewModels,
|
||||
(
|
||||
categories: React.ListState<string>,
|
||||
sortedCategories: React.State<string[]>
|
||||
sortedCategories: React.State<string[]>,
|
||||
) => {
|
||||
const categoryNameConverter: React.StateItemConverter<string> = (
|
||||
categoryName: string
|
||||
categoryName: string,
|
||||
) => {
|
||||
const index: React.State<number> = createPropertyValueIndexState(
|
||||
const index: React.State<number> =
|
||||
createPropertyValueIndexState(
|
||||
sortedCategories,
|
||||
categoryName
|
||||
categoryName,
|
||||
);
|
||||
return KanbanBoard(categoryName, index, boardViewModel);
|
||||
};
|
||||
@@ -38,14 +39,14 @@ export function BoardKanbanPage(boardViewModel: BoardViewModel) {
|
||||
children:append={[categories, categoryNameConverter]}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function KanbanBoard(
|
||||
categoryName: string,
|
||||
index: React.State<number>,
|
||||
boardViewModel: BoardViewModel
|
||||
boardViewModel: BoardViewModel,
|
||||
) {
|
||||
return FilteredList(
|
||||
{ category: categoryName },
|
||||
@@ -53,18 +54,26 @@ function KanbanBoard(
|
||||
boardViewModel.filteredTaskViewModels,
|
||||
(taskViewModels: React.ListState<TaskViewModel>) => {
|
||||
const viewModel: TaskCategoryBulkChangeViewModel =
|
||||
new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
|
||||
new TaskCategoryBulkChangeViewModel(
|
||||
taskViewModels,
|
||||
categoryName,
|
||||
);
|
||||
|
||||
function drop() {
|
||||
boardViewModel.handleDropWithinBoard(categoryName);
|
||||
}
|
||||
|
||||
const view = (
|
||||
<div class="flex-column flex-no" on:dragover={allowDrop} on:drop={drop}>
|
||||
<div
|
||||
class="flex-column flex-no"
|
||||
on:dragover={allowDrop}
|
||||
on:drop={drop}
|
||||
>
|
||||
<div class="flex-row width-input">
|
||||
<input
|
||||
placeholder={
|
||||
translations.chatPage.task.renameCategoryInputPlaceholder
|
||||
translations.chatPage.task
|
||||
.renameCategoryInputPlaceholder
|
||||
}
|
||||
bind:value={viewModel.inputValue}
|
||||
on:enter={viewModel.set}
|
||||
@@ -92,6 +101,6 @@ function KanbanBoard(
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function BoardPage(boardViewModel: BoardViewModel) {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// task modal
|
||||
@@ -49,9 +49,11 @@ export function BoardPage(boardViewModel: BoardViewModel) {
|
||||
if (boardViewModel.selectedTaskViewModel.value == undefined) {
|
||||
return <div></div>;
|
||||
} else {
|
||||
return TaskSettingsModal(boardViewModel.selectedTaskViewModel.value);
|
||||
}
|
||||
return TaskSettingsModal(
|
||||
boardViewModel.selectedTaskViewModel.value,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -60,22 +62,34 @@ export function BoardPage(boardViewModel: BoardViewModel) {
|
||||
<span>
|
||||
<button
|
||||
class="ghost board-close-button"
|
||||
aria-label={translations.chatPage.task.closeBoardButtonAudioLabel}
|
||||
aria-label={
|
||||
translations.chatPage.task
|
||||
.closeBoardButtonAudioLabel
|
||||
}
|
||||
on:click={boardViewModel.close}
|
||||
>
|
||||
<span class="icon">arrow_back</span>
|
||||
</button>
|
||||
<button
|
||||
class="ghost board-toggle-button inset-outline"
|
||||
aria-label={translations.chatPage.task.toggleBoardButtonAudioLabel}
|
||||
on:click={boardViewModel.taskPageViewModel.toggleBoardList}
|
||||
toggle:selected={boardViewModel.taskPageViewModel.isShowingBoadList}
|
||||
aria-label={
|
||||
translations.chatPage.task
|
||||
.toggleBoardButtonAudioLabel
|
||||
}
|
||||
on:click={
|
||||
boardViewModel.taskPageViewModel.toggleBoardList
|
||||
}
|
||||
toggle:selected={
|
||||
boardViewModel.taskPageViewModel.isShowingBoadList
|
||||
}
|
||||
>
|
||||
<span class="icon">dock_to_right</span>
|
||||
</button>
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={translations.chatPage.task.boardSettingsHeadline}
|
||||
aria-label={
|
||||
translations.chatPage.task.boardSettingsHeadline
|
||||
}
|
||||
on:click={boardViewModel.showSettings}
|
||||
>
|
||||
<span class="icon">settings</span>
|
||||
@@ -86,32 +100,38 @@ export function BoardPage(boardViewModel: BoardViewModel) {
|
||||
translations.chatPage.task.listViewButtonAudioLabel,
|
||||
"view_list",
|
||||
BoardPageType.List,
|
||||
boardViewModel
|
||||
boardViewModel,
|
||||
)}
|
||||
{BoardViewToggleButton(
|
||||
translations.chatPage.task.kanbanViewButtonAudioLabel,
|
||||
"view_kanban",
|
||||
BoardPageType.Kanban,
|
||||
boardViewModel
|
||||
boardViewModel,
|
||||
)}
|
||||
{BoardViewToggleButton(
|
||||
translations.chatPage.task.statusViewButtonAudioLabel,
|
||||
"grid_view",
|
||||
BoardPageType.StatusGrid,
|
||||
boardViewModel
|
||||
boardViewModel,
|
||||
)}
|
||||
</span>
|
||||
<span>
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={translations.chatPage.task.filterTasksButtonAudioLabel}
|
||||
aria-label={
|
||||
translations.chatPage.task
|
||||
.filterTasksButtonAudioLabel
|
||||
}
|
||||
on:click={boardViewModel.showFilterModal}
|
||||
>
|
||||
<span class="icon">filter_alt</span>
|
||||
</button>
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={translations.chatPage.task.createTaskButtonAudioLabel}
|
||||
aria-label={
|
||||
translations.chatPage.task
|
||||
.createTaskButtonAudioLabel
|
||||
}
|
||||
on:click={boardViewModel.createTask}
|
||||
>
|
||||
<span class="icon">add</span>
|
||||
@@ -125,7 +145,7 @@ export function BoardPage(boardViewModel: BoardViewModel) {
|
||||
boardViewModel.searchViewModel,
|
||||
translations.chatPage.task.filterTasksHeadline,
|
||||
TaskViewModelToEntry,
|
||||
boardViewModel.isPresentingFilterModal
|
||||
boardViewModel.isPresentingFilterModal,
|
||||
)}
|
||||
<div children:set={taskSettingsModal}></div>
|
||||
</div>
|
||||
|
||||
@@ -28,12 +28,12 @@ export function BoardStatusGridPage(boardViewModel: BoardViewModel) {
|
||||
"status",
|
||||
(taskViewModel: TaskViewModel) => taskViewModel.task,
|
||||
boardViewModel.taskViewModels,
|
||||
statuses
|
||||
statuses,
|
||||
);
|
||||
});
|
||||
|
||||
const statusNameCellConverter: React.StateItemConverter<string> = (
|
||||
statusName: string
|
||||
statusName: string,
|
||||
) => {
|
||||
const index = createPropertyValueIndexState(sortedStatuses, statusName);
|
||||
return StatusNameCell(statusName, index, boardViewModel);
|
||||
@@ -51,14 +51,15 @@ export function BoardStatusGridPage(boardViewModel: BoardViewModel) {
|
||||
boardViewModel.filteredTaskViewModels,
|
||||
(
|
||||
categories: React.ListState<string>,
|
||||
sortedCategories: React.State<string[]>
|
||||
sortedCategories: React.State<string[]>,
|
||||
) => {
|
||||
const categoryRowConverter: React.StateItemConverter<string> = (
|
||||
categoryName: string
|
||||
) => {
|
||||
const index: React.State<number> = createPropertyValueIndexState(
|
||||
const categoryRowConverter: React.StateItemConverter<
|
||||
string
|
||||
> = (categoryName: string) => {
|
||||
const index: React.State<number> =
|
||||
createPropertyValueIndexState(
|
||||
sortedCategories,
|
||||
categoryName
|
||||
categoryName,
|
||||
);
|
||||
|
||||
return CategoryRow(
|
||||
@@ -66,7 +67,7 @@ export function BoardStatusGridPage(boardViewModel: BoardViewModel) {
|
||||
index,
|
||||
statuses,
|
||||
sortedStatuses,
|
||||
boardViewModel
|
||||
boardViewModel,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -76,7 +77,7 @@ export function BoardStatusGridPage(boardViewModel: BoardViewModel) {
|
||||
children:append={[categories, categoryRowConverter]}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -85,27 +86,31 @@ export function BoardStatusGridPage(boardViewModel: BoardViewModel) {
|
||||
function StatusNameCell(
|
||||
statusName: string,
|
||||
index: React.State<number>,
|
||||
boardViewModel: BoardViewModel
|
||||
boardViewModel: BoardViewModel,
|
||||
) {
|
||||
const taskViewModelsWithMatchingStatus: React.ListState<TaskViewModel> =
|
||||
new React.ListState();
|
||||
|
||||
boardViewModel.filteredTaskViewModels.handleAddition(
|
||||
(taskViewModel: TaskViewModel) => {
|
||||
const doesMatchStatus: boolean = taskViewModel.task.status == statusName;
|
||||
const doesMatchStatus: boolean =
|
||||
taskViewModel.task.status == statusName;
|
||||
if (doesMatchStatus == false) return;
|
||||
|
||||
taskViewModelsWithMatchingStatus.add(taskViewModel);
|
||||
boardViewModel.filteredTaskViewModels.handleRemoval(taskViewModel, () => {
|
||||
boardViewModel.filteredTaskViewModels.handleRemoval(
|
||||
taskViewModel,
|
||||
() => {
|
||||
taskViewModelsWithMatchingStatus.remove(taskViewModel);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
const viewModel: TaskStatusBulkChangeViewModel =
|
||||
new TaskStatusBulkChangeViewModel(
|
||||
taskViewModelsWithMatchingStatus,
|
||||
statusName
|
||||
statusName,
|
||||
);
|
||||
|
||||
const view = (
|
||||
@@ -113,7 +118,8 @@ function StatusNameCell(
|
||||
<div class="property-input-wrapper">
|
||||
<input
|
||||
placeholder={
|
||||
translations.chatPage.task.renameCategoryInputPlaceholder
|
||||
translations.chatPage.task
|
||||
.renameCategoryInputPlaceholder
|
||||
}
|
||||
bind:value={viewModel.inputValue}
|
||||
on:enter={viewModel.set}
|
||||
@@ -141,7 +147,7 @@ function CategoryRow(
|
||||
index: React.State<number>,
|
||||
allStatuses: React.ListState<string>,
|
||||
sortedStatuses: React.State<string[]>,
|
||||
boardViewModel: BoardViewModel
|
||||
boardViewModel: BoardViewModel,
|
||||
) {
|
||||
return FilteredList(
|
||||
{ category: categoryName },
|
||||
@@ -149,27 +155,34 @@ function CategoryRow(
|
||||
boardViewModel.filteredTaskViewModels,
|
||||
(taskViewModels: React.ListState<TaskViewModel>) => {
|
||||
const statusNameConverter: React.StateItemConverter<string> = (
|
||||
statusName: string
|
||||
statusName: string,
|
||||
) => {
|
||||
const index = createPropertyValueIndexState(sortedStatuses, statusName);
|
||||
const index = createPropertyValueIndexState(
|
||||
sortedStatuses,
|
||||
statusName,
|
||||
);
|
||||
return CategoryStatusColumn(
|
||||
categoryName,
|
||||
statusName,
|
||||
index,
|
||||
boardViewModel,
|
||||
taskViewModels
|
||||
taskViewModels,
|
||||
);
|
||||
};
|
||||
|
||||
const viewModel: TaskCategoryBulkChangeViewModel =
|
||||
new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
|
||||
new TaskCategoryBulkChangeViewModel(
|
||||
taskViewModels,
|
||||
categoryName,
|
||||
);
|
||||
|
||||
const view = (
|
||||
<div class="flex-row flex-no large-gap">
|
||||
<div class="property-input-wrapper">
|
||||
<input
|
||||
placeholder={
|
||||
translations.chatPage.task.renameCategoryInputPlaceholder
|
||||
translations.chatPage.task
|
||||
.renameCategoryInputPlaceholder
|
||||
}
|
||||
bind:value={viewModel.inputValue}
|
||||
on:enter={viewModel.set}
|
||||
@@ -195,7 +208,7 @@ function CategoryRow(
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -204,12 +217,14 @@ function CategoryStatusColumn(
|
||||
statusName: string,
|
||||
index: React.State<number>,
|
||||
boardViewModel: BoardViewModel,
|
||||
taskViewModelsWithMatchingCategory: React.ListState<TaskViewModel>
|
||||
taskViewModelsWithMatchingCategory: React.ListState<TaskViewModel>,
|
||||
) {
|
||||
const taskViewModels: React.ListState<TaskViewModel> = new React.ListState();
|
||||
const taskViewModels: React.ListState<TaskViewModel> =
|
||||
new React.ListState();
|
||||
|
||||
taskViewModelsWithMatchingCategory.handleAddition((taskViewModel) => {
|
||||
const doesMatchStatus: boolean = taskViewModel.status.value == statusName;
|
||||
const doesMatchStatus: boolean =
|
||||
taskViewModel.status.value == statusName;
|
||||
if (doesMatchStatus == false) return;
|
||||
|
||||
taskViewModels.add(taskViewModel);
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
width: 3.1rem;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
#chat-page > div > #main > #calendar-page {
|
||||
flex-direction: column;
|
||||
|
||||
@@ -24,13 +24,17 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
calendarPageViewModel.handleDrop(
|
||||
monthGrid!.year.toString(),
|
||||
monthGrid!.month.toString(),
|
||||
date
|
||||
date,
|
||||
);
|
||||
}
|
||||
|
||||
return MonthGrid(monthGrid, calendarPageViewModel.selectedDate, drop);
|
||||
}
|
||||
return MonthGrid(
|
||||
monthGrid,
|
||||
calendarPageViewModel.selectedDate,
|
||||
drop,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const sidePaneContentWrapper = React.createProxyState(
|
||||
@@ -45,12 +49,17 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
if (listState == undefined) {
|
||||
return <div></div>;
|
||||
} else {
|
||||
const sidePaneContent = React.createProxyState([listState], () => {
|
||||
const sidePaneContent = React.createProxyState(
|
||||
[listState],
|
||||
() => {
|
||||
if (listState.value.size == 0) {
|
||||
return (
|
||||
<div class="width-100 height-100 flex-column justify-center align-center">
|
||||
<span class="secondary slide-up">
|
||||
{translations.chatPage.calendar.noEvents}
|
||||
{
|
||||
translations.chatPage.calendar
|
||||
.noEvents
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
@@ -58,11 +67,15 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
return (
|
||||
<div
|
||||
class="flex-column gap slide-up padding-bottom"
|
||||
children:append={[listState, TaskViewModelToEntry]}
|
||||
children:append={[
|
||||
listState,
|
||||
TaskViewModelToEntry,
|
||||
]}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -71,20 +84,22 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
></div>
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const taskSettingsModal = React.createProxyState(
|
||||
[calendarPageViewModel.selectedTaskViewModel],
|
||||
() => {
|
||||
if (calendarPageViewModel.selectedTaskViewModel.value == undefined) {
|
||||
if (
|
||||
calendarPageViewModel.selectedTaskViewModel.value == undefined
|
||||
) {
|
||||
return <div></div>;
|
||||
} else {
|
||||
return TaskSettingsModal(
|
||||
calendarPageViewModel.selectedTaskViewModel.value
|
||||
calendarPageViewModel.selectedTaskViewModel.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -96,7 +111,8 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={
|
||||
translations.chatPage.calendar.todayButtonAudioLabel
|
||||
translations.chatPage.calendar
|
||||
.todayButtonAudioLabel
|
||||
}
|
||||
on:click={calendarPageViewModel.showToday}
|
||||
>
|
||||
@@ -107,9 +123,12 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={
|
||||
translations.chatPage.calendar.previousMonthButtonAudioLabel
|
||||
translations.chatPage.calendar
|
||||
.previousMonthButtonAudioLabel
|
||||
}
|
||||
on:click={
|
||||
calendarPageViewModel.showPreviousMonth
|
||||
}
|
||||
on:click={calendarPageViewModel.showPreviousMonth}
|
||||
>
|
||||
<span class="icon">arrow_back</span>
|
||||
</button>
|
||||
@@ -118,29 +137,38 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
class="year-input"
|
||||
type="number"
|
||||
aria-label={
|
||||
translations.chatPage.calendar.yearInputAudioLabel
|
||||
translations.chatPage.calendar
|
||||
.yearInputAudioLabel
|
||||
}
|
||||
placeholder={
|
||||
translations.chatPage.calendar.yearInputPlaceholder
|
||||
translations.chatPage.calendar
|
||||
.yearInputPlaceholder
|
||||
}
|
||||
bind:value={
|
||||
calendarPageViewModel.selectedYear
|
||||
}
|
||||
bind:value={calendarPageViewModel.selectedYear}
|
||||
></input>
|
||||
<input
|
||||
class="month-input"
|
||||
type="number"
|
||||
aria-label={
|
||||
translations.chatPage.calendar.monthInputAudioLabel
|
||||
translations.chatPage.calendar
|
||||
.monthInputAudioLabel
|
||||
}
|
||||
placeholder={
|
||||
translations.chatPage.calendar.monthInputPlaceholder
|
||||
translations.chatPage.calendar
|
||||
.monthInputPlaceholder
|
||||
}
|
||||
bind:value={
|
||||
calendarPageViewModel.selectedMonth
|
||||
}
|
||||
bind:value={calendarPageViewModel.selectedMonth}
|
||||
></input>
|
||||
</span>
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={
|
||||
translations.chatPage.calendar.nextMonthButtonAudioLabel
|
||||
translations.chatPage.calendar
|
||||
.nextMonthButtonAudioLabel
|
||||
}
|
||||
on:click={calendarPageViewModel.showNextMonth}
|
||||
>
|
||||
@@ -151,7 +179,8 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
<button
|
||||
class="ghost"
|
||||
aria-label={
|
||||
translations.chatPage.task.createTaskButtonAudioLabel
|
||||
translations.chatPage.task
|
||||
.createTaskButtonAudioLabel
|
||||
}
|
||||
on:click={calendarPageViewModel.createEvent}
|
||||
>
|
||||
@@ -159,7 +188,10 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="content padding-0" children:set={mainContent}></div>
|
||||
<div
|
||||
class="content padding-0"
|
||||
children:set={mainContent}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -167,7 +199,10 @@ export function CalendarPage(calendarPageViewModel: CalendarPageViewModel) {
|
||||
set:color={calendarPageViewModel.chatViewModel.displayedColor}
|
||||
>
|
||||
<div class="pane">
|
||||
<div class="content" children:set={sidePaneContentWrapper}></div>
|
||||
<div
|
||||
class="content"
|
||||
children:set={sidePaneContentWrapper}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export function MessagePage(messagePageViewModel: MessagePageViewModel) {
|
||||
scrollDown();
|
||||
}
|
||||
messagePageViewModel.chatMessageViewModels.subscribeSilent(
|
||||
scrollDownIfApplicable
|
||||
scrollDownIfApplicable,
|
||||
);
|
||||
setTimeout(() => scrollDown(), 100);
|
||||
|
||||
@@ -40,7 +40,9 @@ export function MessagePage(messagePageViewModel: MessagePageViewModel) {
|
||||
<div class="pane-wrapper">
|
||||
<div class="pane">
|
||||
<div class="toolbar">
|
||||
<span class="title">{translations.chatPage.message.messagesHeadline}</span>
|
||||
<span class="title">
|
||||
{translations.chatPage.message.messagesHeadline}
|
||||
</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
{messageContainer}
|
||||
@@ -48,19 +50,29 @@ export function MessagePage(messagePageViewModel: MessagePageViewModel) {
|
||||
<div class="content-width-constraint">
|
||||
<div class="input-width-constraint">
|
||||
<input
|
||||
bind:value={messagePageViewModel.composingMessage}
|
||||
on:enter={messagePageViewModel.sendMessage}
|
||||
bind:value={
|
||||
messagePageViewModel.composingMessage
|
||||
}
|
||||
on:enter={
|
||||
messagePageViewModel.sendMessage
|
||||
}
|
||||
placeholder={
|
||||
translations.chatPage.message.composerInputPlaceholder
|
||||
translations.chatPage.message
|
||||
.composerInputPlaceholder
|
||||
}
|
||||
></input>
|
||||
<button
|
||||
class="primary"
|
||||
aria-label={
|
||||
translations.chatPage.message.sendMessageButtonAudioLabel
|
||||
translations.chatPage.message
|
||||
.sendMessageButtonAudioLabel
|
||||
}
|
||||
on:click={
|
||||
messagePageViewModel.sendMessage
|
||||
}
|
||||
toggle:disabled={
|
||||
messagePageViewModel.cannotSendMessage
|
||||
}
|
||||
on:click={messagePageViewModel.sendMessage}
|
||||
toggle:disabled={messagePageViewModel.cannotSendMessage}
|
||||
>
|
||||
<span class="icon">send</span>
|
||||
</button>
|
||||
|
||||
@@ -11,7 +11,7 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
settingsPageViewModel.loadData();
|
||||
|
||||
const secondaryChannelConverter: React.StateItemConverter<string> = (
|
||||
secondaryChannel: string
|
||||
secondaryChannel: string,
|
||||
) => {
|
||||
return DeletableListItem(secondaryChannel, <span></span>, () => {
|
||||
settingsPageViewModel.removeSecondaryChannel(secondaryChannel);
|
||||
@@ -32,11 +32,18 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
<span class="icon">forum</span>
|
||||
<div>
|
||||
<span>
|
||||
{translations.chatPage.settings.primaryChannelLabel}
|
||||
{
|
||||
translations.chatPage.settings
|
||||
.primaryChannelLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
bind:value={settingsPageViewModel.primaryChannelInput}
|
||||
on:enter={settingsPageViewModel.setPrimaryChannel}
|
||||
bind:value={
|
||||
settingsPageViewModel.primaryChannelInput
|
||||
}
|
||||
on:enter={
|
||||
settingsPageViewModel.setPrimaryChannel
|
||||
}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -47,8 +54,12 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
translations.chatPage.settings
|
||||
.setPrimaryChannelButtonAudioLabel
|
||||
}
|
||||
on:click={settingsPageViewModel.setPrimaryChannel}
|
||||
toggle:disabled={settingsPageViewModel.cannotSetPrimaryChannel}
|
||||
on:click={
|
||||
settingsPageViewModel.setPrimaryChannel
|
||||
}
|
||||
toggle:disabled={
|
||||
settingsPageViewModel.cannotSetPrimaryChannel
|
||||
}
|
||||
>
|
||||
{translations.general.setButton}
|
||||
<span class="icon">check</span>
|
||||
@@ -60,13 +71,19 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
<div class="flex-row width-input margin-bottom">
|
||||
<input
|
||||
aria-label={
|
||||
translations.chatPage.settings.newSecondaryChannelAudioLabel
|
||||
translations.chatPage.settings
|
||||
.newSecondaryChannelAudioLabel
|
||||
}
|
||||
placeholder={
|
||||
translations.chatPage.settings.newSecondaryChannelPlaceholder
|
||||
translations.chatPage.settings
|
||||
.newSecondaryChannelPlaceholder
|
||||
}
|
||||
bind:value={
|
||||
settingsPageViewModel.newSecondaryChannelInput
|
||||
}
|
||||
on:enter={
|
||||
settingsPageViewModel.addSecondaryChannel
|
||||
}
|
||||
bind:value={settingsPageViewModel.newSecondaryChannelInput}
|
||||
on:enter={settingsPageViewModel.addSecondaryChannel}
|
||||
></input>
|
||||
<button
|
||||
class="primary"
|
||||
@@ -74,7 +91,9 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
translations.chatPage.settings
|
||||
.addSecondaryChannelButtonAudioLabel
|
||||
}
|
||||
on:click={settingsPageViewModel.addSecondaryChannel}
|
||||
on:click={
|
||||
settingsPageViewModel.addSecondaryChannel
|
||||
}
|
||||
toggle:disabled={
|
||||
settingsPageViewModel.cannotAddSecondaryChannel
|
||||
}
|
||||
@@ -96,11 +115,22 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">key</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.settings.encryptionKeyLabel}</span>
|
||||
<span>
|
||||
{
|
||||
translations.chatPage.settings
|
||||
.encryptionKeyLabel
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
bind:value={settingsPageViewModel.encryptionKeyInput}
|
||||
on:enter={settingsPageViewModel.setEncryptionKey}
|
||||
set:type={settingsPageViewModel.encryptionKeyInputType}
|
||||
bind:value={
|
||||
settingsPageViewModel.encryptionKeyInput
|
||||
}
|
||||
on:enter={
|
||||
settingsPageViewModel.setEncryptionKey
|
||||
}
|
||||
set:type={
|
||||
settingsPageViewModel.encryptionKeyInputType
|
||||
}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -111,8 +141,12 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
translations.chatPage.settings
|
||||
.setEncryptionKeyButtonAudioLabel
|
||||
}
|
||||
on:click={settingsPageViewModel.setEncryptionKey}
|
||||
toggle:disabled={settingsPageViewModel.cannotSetEncryptionKey}
|
||||
on:click={
|
||||
settingsPageViewModel.setEncryptionKey
|
||||
}
|
||||
toggle:disabled={
|
||||
settingsPageViewModel.cannotSetEncryptionKey
|
||||
}
|
||||
>
|
||||
{translations.general.setButton}
|
||||
<span class="icon">check</span>
|
||||
@@ -122,7 +156,9 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
<label class="inline">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={settingsPageViewModel.shouldShowEncryptionKey}
|
||||
bind:checked={
|
||||
settingsPageViewModel.shouldShowEncryptionKey
|
||||
}
|
||||
></input>
|
||||
{translations.chatPage.settings.showEncryptionKey}
|
||||
</label>
|
||||
@@ -137,7 +173,7 @@ export function SettingsPage(settingsPageViewModel: SettingsPageViewModel) {
|
||||
{DangerousActionButton(
|
||||
translations.chatPage.settings.deleteChatButton,
|
||||
"chat_error",
|
||||
settingsPageViewModel.remove
|
||||
settingsPageViewModel.remove,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ export function TaskPage(taskPageViewModel: TaskPageViewModel) {
|
||||
|
||||
const isShowingBoard = React.createProxyState(
|
||||
[taskPageViewModel.selectedBoardId],
|
||||
() => taskPageViewModel.selectedBoardId.value != undefined
|
||||
() => taskPageViewModel.selectedBoardId.value != undefined,
|
||||
);
|
||||
|
||||
const paneContent = React.createProxyState(
|
||||
@@ -41,11 +41,15 @@ export function TaskPage(taskPageViewModel: TaskPageViewModel) {
|
||||
}
|
||||
|
||||
return BoardPage(selectedBoard);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div id="task-page" toggle:isshowingboard={isShowingBoard} set:showingboardlist={taskPageViewModel.isShowingBoadList}>
|
||||
<div
|
||||
id="task-page"
|
||||
toggle:isshowingboard={isShowingBoard}
|
||||
set:showingboardlist={taskPageViewModel.isShowingBoadList}
|
||||
>
|
||||
<div
|
||||
id="board-list"
|
||||
class="pane-wrapper side background"
|
||||
@@ -58,15 +62,21 @@ export function TaskPage(taskPageViewModel: TaskPageViewModel) {
|
||||
class="no-outline"
|
||||
bind:value={taskPageViewModel.newBoardNameInput}
|
||||
on:enter={taskPageViewModel.createBoard}
|
||||
placeholder={translations.chatPage.task.newBoardNamePlaceholder}
|
||||
placeholder={
|
||||
translations.chatPage.task
|
||||
.newBoardNamePlaceholder
|
||||
}
|
||||
></input>
|
||||
<button
|
||||
class="primary"
|
||||
aria-label={
|
||||
translations.chatPage.task.createBoardButtonAudioLabel
|
||||
translations.chatPage.task
|
||||
.createBoardButtonAudioLabel
|
||||
}
|
||||
on:click={taskPageViewModel.createBoard}
|
||||
toggle:disabled={taskPageViewModel.cannotCreateBoard}
|
||||
toggle:disabled={
|
||||
taskPageViewModel.cannotCreateBoard
|
||||
}
|
||||
>
|
||||
<span class="icon">add</span>
|
||||
</button>
|
||||
|
||||
@@ -13,7 +13,10 @@ export function BoardEntry(boardViewModel: BoardViewModel) {
|
||||
on:dragover={allowDrop}
|
||||
on:drop={boardViewModel.handleDropBetweenBoards}
|
||||
>
|
||||
<span class="shadow" subscribe:innerText={boardViewModel.name}></span>
|
||||
<span
|
||||
class="shadow"
|
||||
subscribe:innerText={boardViewModel.name}
|
||||
></span>
|
||||
<b subscribe:innerText={boardViewModel.name}></b>
|
||||
</button>
|
||||
);
|
||||
@@ -26,7 +29,7 @@ export function BoardEntry(boardViewModel: BoardViewModel) {
|
||||
}
|
||||
|
||||
export const BoardViewModelToEntry: React.StateItemConverter<BoardViewModel> = (
|
||||
boardViewModel: BoardViewModel
|
||||
boardViewModel: BoardViewModel,
|
||||
) => {
|
||||
return BoardEntry(boardViewModel);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ export function BoardViewToggleButton(
|
||||
label: string,
|
||||
icon: string,
|
||||
page: BoardPageType,
|
||||
boardViewModel: BoardViewModel
|
||||
boardViewModel: BoardViewModel,
|
||||
) {
|
||||
function select() {
|
||||
boardViewModel.selectedPage.value = page;
|
||||
@@ -18,7 +18,7 @@ export function BoardViewToggleButton(
|
||||
|
||||
const isSelected = React.createProxyState(
|
||||
[boardViewModel.selectedPage],
|
||||
() => boardViewModel.selectedPage.value == page
|
||||
() => boardViewModel.selectedPage.value == page,
|
||||
);
|
||||
|
||||
return RibbonButton(label, icon, isSelected, select);
|
||||
|
||||
@@ -13,10 +13,14 @@ export function ChatEntry(chatViewModel: ChatViewModel) {
|
||||
>
|
||||
<span
|
||||
class="shadow"
|
||||
subscribe:innerText={chatViewModel.settingsPageViewModel.primaryChannel}
|
||||
subscribe:innerText={
|
||||
chatViewModel.settingsPageViewModel.primaryChannel
|
||||
}
|
||||
></span>
|
||||
<h2
|
||||
subscribe:innerText={chatViewModel.settingsPageViewModel.primaryChannel}
|
||||
subscribe:innerText={
|
||||
chatViewModel.settingsPageViewModel.primaryChannel
|
||||
}
|
||||
></h2>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ export function ChatMessage(chatMessageViewModel: ChatMessageViewModel) {
|
||||
default:
|
||||
return "warning";
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -37,7 +37,10 @@ export function ChatMessage(chatMessageViewModel: ChatMessageViewModel) {
|
||||
subscribe:innerText={chatMessageViewModel.body}
|
||||
></span>
|
||||
<span class="timestamp ellipsis">
|
||||
<span class="icon" subscribe:innerText={statusIcon}></span>
|
||||
<span
|
||||
class="icon"
|
||||
subscribe:innerText={statusIcon}
|
||||
></span>
|
||||
{chatMessageViewModel.dateSent}
|
||||
</span>
|
||||
</div>
|
||||
@@ -46,7 +49,8 @@ export function ChatMessage(chatMessageViewModel: ChatMessageViewModel) {
|
||||
<button
|
||||
on:click={chatMessageViewModel.showInfoModal}
|
||||
aria-label={
|
||||
translations.chatPage.message.showMessageInfoButtonAudioLabel
|
||||
translations.chatPage.message
|
||||
.showMessageInfoButtonAudioLabel
|
||||
}
|
||||
>
|
||||
<span class="icon">info</span>
|
||||
|
||||
@@ -10,7 +10,7 @@ export function ChatViewToggleButton(
|
||||
label: string,
|
||||
icon: string,
|
||||
page: ChatPageType,
|
||||
chatViewModel: ChatViewModel
|
||||
chatViewModel: ChatViewModel,
|
||||
) {
|
||||
function select() {
|
||||
chatViewModel.selectedPage.value = page;
|
||||
@@ -18,7 +18,7 @@ export function ChatViewToggleButton(
|
||||
|
||||
const isSelected = React.createProxyState(
|
||||
[chatViewModel.selectedPage],
|
||||
() => chatViewModel.selectedPage.value == page
|
||||
() => chatViewModel.selectedPage.value == page,
|
||||
);
|
||||
|
||||
return RibbonButton(label, icon, isSelected, select);
|
||||
|
||||
@@ -8,7 +8,7 @@ export function ColorPicker(selectedColor: React.State<Color>) {
|
||||
{...Object.values(Color).map((color) => {
|
||||
const isSelected = React.createProxyState(
|
||||
[selectedColor],
|
||||
() => selectedColor.value == color
|
||||
() => selectedColor.value == color,
|
||||
);
|
||||
|
||||
function setColor() {
|
||||
|
||||
@@ -5,12 +5,12 @@ import { translations } from "../translations";
|
||||
export function DangerousActionButton(
|
||||
label: string,
|
||||
icon: string,
|
||||
action: () => void
|
||||
action: () => void,
|
||||
) {
|
||||
const isActionRequested = new React.State(false);
|
||||
const cannotConfirm = React.createProxyState(
|
||||
[isActionRequested],
|
||||
() => isActionRequested.value == false
|
||||
() => isActionRequested.value == false,
|
||||
);
|
||||
|
||||
function requestAction() {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { translations } from "../translations";
|
||||
export function DeletableListItem(
|
||||
text: string,
|
||||
primaryButton: HTMLElement,
|
||||
ondelete: () => void
|
||||
ondelete: () => void,
|
||||
) {
|
||||
return (
|
||||
<div class="tile flex-row justify-apart align-center padding-0">
|
||||
|
||||
@@ -8,11 +8,11 @@ import StorageViewModel from "../../ViewModel/Global/storageViewModel";
|
||||
|
||||
export function DirectoryItemList(
|
||||
storageViewModel: StorageViewModel,
|
||||
pathString: string = PATH_COMPONENT_SEPARATOR
|
||||
pathString: string = PATH_COMPONENT_SEPARATOR,
|
||||
) {
|
||||
// converter
|
||||
const StringToDirectoryItemList: React.StateItemConverter<string> = (
|
||||
pathString: string
|
||||
pathString: string,
|
||||
) => DirectoryItemList(storageViewModel, pathString);
|
||||
|
||||
// data
|
||||
@@ -50,7 +50,7 @@ export function DirectoryItemList(
|
||||
// state
|
||||
const isSelected = React.createProxyState(
|
||||
[storageViewModel.selectedPath],
|
||||
() => storageViewModel.selectedPath.value == pathString
|
||||
() => storageViewModel.selectedPath.value == pathString,
|
||||
);
|
||||
|
||||
isSelected.subscribe(() => {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
font-size: .9rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
|
||||
@@ -15,14 +15,16 @@ export function FileBrowser(storageViewModel: StorageViewModel) {
|
||||
if (storageViewModel.selectedPath.value == PATH_COMPONENT_SEPARATOR)
|
||||
return (
|
||||
<div>
|
||||
<span class="secondary">{translations.storage.noItemSelected}</span>
|
||||
<span class="secondary">
|
||||
{translations.storage.noItemSelected}
|
||||
</span>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
{DangerousActionButton(
|
||||
translations.storage.removeJunkButton,
|
||||
"delete_forever",
|
||||
storageViewModel.removeJunk
|
||||
storageViewModel.removeJunk,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -34,7 +36,9 @@ export function FileBrowser(storageViewModel: StorageViewModel) {
|
||||
<b>{translations.storage.path}</b>
|
||||
<span
|
||||
class="break-all"
|
||||
subscribe:innerText={storageViewModel.selectedPath}
|
||||
subscribe:innerText={
|
||||
storageViewModel.selectedPath
|
||||
}
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,7 +46,9 @@ export function FileBrowser(storageViewModel: StorageViewModel) {
|
||||
<div>
|
||||
<b>{translations.storage.content}</b>
|
||||
<code
|
||||
subscribe:innerText={storageViewModel.selectedFileContent}
|
||||
subscribe:innerText={
|
||||
storageViewModel.selectedFileContent
|
||||
}
|
||||
></code>
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,22 +56,26 @@ export function FileBrowser(storageViewModel: StorageViewModel) {
|
||||
{DangerousActionButton(
|
||||
translations.storage.deleteItem,
|
||||
"delete_forever",
|
||||
storageViewModel.deleteSelectedItem
|
||||
storageViewModel.deleteSelectedItem,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const view = (
|
||||
<div class="file-browser">
|
||||
<div>
|
||||
<div class="scroll-area">{DirectoryItemList(storageViewModel)}</div>
|
||||
<div class="scroll-area">
|
||||
{DirectoryItemList(storageViewModel)}
|
||||
</div>
|
||||
<div class="detail-button-wrapper">
|
||||
<button class="ghost" on:click={scrollToDetails}>
|
||||
<span
|
||||
class="ellipsis"
|
||||
subscribe:innerText={storageViewModel.selectedFileName}
|
||||
subscribe:innerText={
|
||||
storageViewModel.selectedFileName
|
||||
}
|
||||
></span>
|
||||
<span class="icon">arrow_forward</span>
|
||||
</button>
|
||||
|
||||
@@ -9,14 +9,14 @@ export function FilteredList<T>(
|
||||
reference: StringEntryObject,
|
||||
stringEntryObjectConverter: (object: T) => StringEntryObject,
|
||||
objects: React.ListState<T> | React.MapState<T>,
|
||||
viewBuilder: (matchingObjects: React.ListState<T>) => HTMLElement
|
||||
viewBuilder: (matchingObjects: React.ListState<T>) => HTMLElement,
|
||||
) {
|
||||
const matchingObjects: React.ListState<T> = new React.ListState();
|
||||
|
||||
objects.handleAddition((newObject) => {
|
||||
const doesMatch: boolean = checkDoesObjectMatchReference(
|
||||
reference,
|
||||
stringEntryObjectConverter(newObject)
|
||||
stringEntryObjectConverter(newObject),
|
||||
);
|
||||
if (doesMatch == false) return;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { localeCompare } from "../../Model/Utility/utility";
|
||||
export function MonthGrid<T>(
|
||||
monthGrid: MonthGrid<React.MapState<T>>,
|
||||
selectedDate: React.State<number>,
|
||||
handleDrop: (date: string) => void
|
||||
handleDrop: (date: string) => void,
|
||||
) {
|
||||
const dayLabels: HTMLElement[] = [];
|
||||
let currentWeekday = monthGrid.firstDayOfWeek;
|
||||
@@ -18,7 +18,7 @@ export function MonthGrid<T>(
|
||||
dayLabels.push(
|
||||
<span class="secondary">
|
||||
{translations.regional.weekdays.abbreviated[currentWeekday]}
|
||||
</span>
|
||||
</span>,
|
||||
);
|
||||
currentWeekday++;
|
||||
if (currentWeekday == 7) currentWeekday = 0;
|
||||
@@ -30,7 +30,7 @@ export function MonthGrid<T>(
|
||||
}
|
||||
|
||||
const converter: React.StateItemConverter<TaskViewModel> = (
|
||||
taskViewModel
|
||||
taskViewModel,
|
||||
) => {
|
||||
const view = (
|
||||
<span class="ellipsis secondary">{taskViewModel.task.name}</span>
|
||||
@@ -55,21 +55,23 @@ export function MonthGrid<T>(
|
||||
|
||||
const isSelected = React.createProxyState(
|
||||
[selectedDate],
|
||||
() => selectedDate.value == parseInt(date)
|
||||
() => selectedDate.value == parseInt(date),
|
||||
);
|
||||
|
||||
const isToday: boolean =
|
||||
monthGrid.isCurrentMonth == true &&
|
||||
parseInt(date) == new Date().getDate();
|
||||
|
||||
const eventCount: React.State<number> = React.createProxyState(
|
||||
const eventCount: React.State<number> =
|
||||
React.createProxyState(
|
||||
[mapState],
|
||||
() => mapState.value.size
|
||||
() => mapState.value.size,
|
||||
);
|
||||
|
||||
const hasEvents: React.State<boolean> = React.createProxyState(
|
||||
const hasEvents: React.State<boolean> =
|
||||
React.createProxyState(
|
||||
[eventCount],
|
||||
() => eventCount.value != 0
|
||||
() => eventCount.value != 0,
|
||||
);
|
||||
|
||||
function select() {
|
||||
|
||||
@@ -9,19 +9,19 @@ export function Option(text: string, value: string, selectedOnCreate: boolean) {
|
||||
}
|
||||
|
||||
export const StringToOption: React.StateItemConverter<string> = (
|
||||
string: string
|
||||
string: string,
|
||||
) => {
|
||||
return Option(string, string, false);
|
||||
};
|
||||
|
||||
export const EntryToOption: React.StateItemConverter<Entry> = (
|
||||
entry: Entry
|
||||
entry: Entry,
|
||||
) => {
|
||||
return Option(entry[1], entry[0], false);
|
||||
};
|
||||
|
||||
export const VersionIdToOption: React.StateItemConverter<string> = (
|
||||
versionId: string
|
||||
versionId: string,
|
||||
) => {
|
||||
const [date, rest] = versionId.split("T");
|
||||
const [time] = rest.split(".");
|
||||
|
||||
@@ -12,17 +12,18 @@ export function PropertyValueList<T>(
|
||||
objects: React.ListState<T> | React.MapState<T>,
|
||||
viewBuilder: (
|
||||
keys: React.ListState<string>,
|
||||
sortedKeys: React.State<string[]>
|
||||
) => HTMLElement
|
||||
sortedKeys: React.State<string[]>,
|
||||
) => HTMLElement,
|
||||
) {
|
||||
const propertyValues: React.ListState<string> = new React.ListState();
|
||||
const sortedPropertyValues: React.State<string[]> = createSortedPropertyValueState(propertyValues)
|
||||
const sortedPropertyValues: React.State<string[]> =
|
||||
createSortedPropertyValueState(propertyValues);
|
||||
objects.subscribe(() => {
|
||||
collectPropertyValuesToState(
|
||||
propertyKey,
|
||||
stringEntryObjectConverter,
|
||||
objects,
|
||||
propertyValues
|
||||
propertyValues,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -33,12 +34,12 @@ export function collectPropertyValuesToState<T>(
|
||||
propertyKey: string,
|
||||
stringEntryObjectConverter: (object: T) => StringEntryObject,
|
||||
objects: React.ListState<T> | React.MapState<T>,
|
||||
propertyValues: React.ListState<string>
|
||||
propertyValues: React.ListState<string>,
|
||||
) {
|
||||
const values: string[] = collectObjectValuesForKey(
|
||||
propertyKey,
|
||||
stringEntryObjectConverter,
|
||||
[...objects.value.values()]
|
||||
[...objects.value.values()],
|
||||
);
|
||||
for (const existingValue of values) {
|
||||
if (propertyValues.value.has(existingValue)) continue;
|
||||
@@ -53,18 +54,18 @@ export function collectPropertyValuesToState<T>(
|
||||
}
|
||||
|
||||
export function createSortedPropertyValueState(
|
||||
propertyValues: React.ListState<string>
|
||||
propertyValues: React.ListState<string>,
|
||||
): React.State<string[]> {
|
||||
return React.createProxyState([propertyValues], () =>
|
||||
[...propertyValues.value.values()].sort(localeCompare)
|
||||
[...propertyValues.value.values()].sort(localeCompare),
|
||||
);
|
||||
}
|
||||
|
||||
export function createPropertyValueIndexState(
|
||||
sortedKeys: React.State<string[]>,
|
||||
key: string
|
||||
key: string,
|
||||
): React.State<number> {
|
||||
return React.createProxyState([sortedKeys], () =>
|
||||
sortedKeys.value.indexOf(key)
|
||||
sortedKeys.value.indexOf(key),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function RibbonButton(
|
||||
label: string,
|
||||
icon: string,
|
||||
isSelected: React.State<boolean>,
|
||||
select: () => void
|
||||
select: () => void,
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -21,7 +21,10 @@ export function TaskEntry(taskViewModel: TaskViewModel) {
|
||||
on:dragstart={taskViewModel.dragStart}
|
||||
>
|
||||
<div>
|
||||
<b class="ellipsis" subscribe:innerText={taskViewModel.name}></b>
|
||||
<b
|
||||
class="ellipsis"
|
||||
subscribe:innerText={taskViewModel.name}
|
||||
></b>
|
||||
<hr></hr>
|
||||
<div
|
||||
class="grid secondary"
|
||||
@@ -51,7 +54,7 @@ export function TaskEntry(taskViewModel: TaskViewModel) {
|
||||
}
|
||||
|
||||
export const TaskViewModelToEntry: React.StateItemConverter<TaskViewModel> = (
|
||||
taskViewModel: TaskViewModel
|
||||
taskViewModel: TaskViewModel,
|
||||
) => {
|
||||
return TaskEntry(taskViewModel);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
export const StringToTextSpan: React.StateItemConverter<string> = (
|
||||
string: string
|
||||
string: string,
|
||||
) => {
|
||||
return <span class="ellipsis">{string}</span>
|
||||
return <span class="ellipsis">{string}</span>;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,10 @@ import { translations } from "../translations";
|
||||
|
||||
export function BoardSettingsModal(boardViewModel: BoardViewModel) {
|
||||
return (
|
||||
<div class="modal" toggle:open={boardViewModel.isPresentingSettingsModal}>
|
||||
<div
|
||||
class="modal"
|
||||
toggle:open={boardViewModel.isPresentingSettingsModal}
|
||||
>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.chatPage.task.boardSettingsHeadline}</h2>
|
||||
@@ -15,7 +18,9 @@ export function BoardSettingsModal(boardViewModel: BoardViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">label</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.boardNameInputLabel}</span>
|
||||
<span>
|
||||
{translations.chatPage.task.boardNameInputLabel}
|
||||
</span>
|
||||
<input
|
||||
on:enter={boardViewModel.saveSettings}
|
||||
bind:value={boardViewModel.name}
|
||||
@@ -33,7 +38,7 @@ export function BoardSettingsModal(boardViewModel: BoardViewModel) {
|
||||
{DangerousActionButton(
|
||||
translations.chatPage.task.deleteBoardButton,
|
||||
"delete_forever",
|
||||
boardViewModel.deleteBoard
|
||||
boardViewModel.deleteBoard,
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -4,10 +4,13 @@ import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
import { translations } from "../translations";
|
||||
|
||||
export function ChatMessageInfoModal(
|
||||
chatMessageViewModel: ChatMessageViewModel
|
||||
chatMessageViewModel: ChatMessageViewModel,
|
||||
) {
|
||||
return (
|
||||
<div class="modal" toggle:open={chatMessageViewModel.isPresentingInfoModal}>
|
||||
<div
|
||||
class="modal"
|
||||
toggle:open={chatMessageViewModel.isPresentingInfoModal}
|
||||
>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.chatPage.message.messageInfoHeadline}</h2>
|
||||
@@ -16,34 +19,53 @@ export function ChatMessageInfoModal(
|
||||
<div class="tile">
|
||||
<span class="icon">account_circle</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.message.sentBy}</span>
|
||||
<b class="break-word">{chatMessageViewModel.sender}</b>
|
||||
<span>
|
||||
{translations.chatPage.message.sentBy}
|
||||
</span>
|
||||
<b class="break-word">
|
||||
{chatMessageViewModel.sender}
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tile">
|
||||
<span class="icon">schedule</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.message.timeSent}</span>
|
||||
<b class="break-word">{chatMessageViewModel.dateSent}</b>
|
||||
<span>
|
||||
{translations.chatPage.message.timeSent}
|
||||
</span>
|
||||
<b class="break-word">
|
||||
{chatMessageViewModel.dateSent}
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tile">
|
||||
<span class="icon">forum</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.message.channel}</span>
|
||||
<b class="break-word">{chatMessageViewModel.channel}</b>
|
||||
<span>
|
||||
{translations.chatPage.message.channel}
|
||||
</span>
|
||||
<b class="break-word">
|
||||
{chatMessageViewModel.channel}
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tile">
|
||||
<span class="icon">description</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.message.messageContent}</span>
|
||||
<span>
|
||||
{
|
||||
translations.chatPage.message
|
||||
.messageContent
|
||||
}
|
||||
</span>
|
||||
<b
|
||||
class="break-word"
|
||||
subscribe:innerText={chatMessageViewModel.body}
|
||||
subscribe:innerText={
|
||||
chatMessageViewModel.body
|
||||
}
|
||||
></b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { translations } from "../translations";
|
||||
|
||||
export function ConnectionModal(connectionViewModel: ConnectionViewModel) {
|
||||
const previousAddressConverter: React.StateItemConverter<string> = (
|
||||
address: string
|
||||
address: string,
|
||||
) => {
|
||||
function connnect() {
|
||||
connectionViewModel.connectToAddress(address);
|
||||
@@ -16,7 +16,7 @@ export function ConnectionModal(connectionViewModel: ConnectionViewModel) {
|
||||
[connectionViewModel.isConnected],
|
||||
() =>
|
||||
connectionViewModel.isConnected.value == true &&
|
||||
connectionViewModel.connectionModel.address == address
|
||||
connectionViewModel.connectionModel.address == address,
|
||||
);
|
||||
|
||||
return DeletableListItem(
|
||||
@@ -25,13 +25,15 @@ export function ConnectionModal(connectionViewModel: ConnectionViewModel) {
|
||||
class="primary"
|
||||
on:click={connnect}
|
||||
toggle:disabled={cannotConnect}
|
||||
aria-label={translations.connectionModal.connectButtonAudioLabel}
|
||||
aria-label={
|
||||
translations.connectionModal.connectButtonAudioLabel
|
||||
}
|
||||
>
|
||||
<span class="icon">link</span>
|
||||
</button>,
|
||||
() => {
|
||||
connectionViewModel.removePreviousAddress(address);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,7 +44,9 @@ export function ConnectionModal(connectionViewModel: ConnectionViewModel) {
|
||||
>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.connectionModal.connectionModalHeadline}</h2>
|
||||
<h2>
|
||||
{translations.connectionModal.connectionModalHeadline}
|
||||
</h2>
|
||||
|
||||
<div
|
||||
class="flex-column gap"
|
||||
|
||||
@@ -13,11 +13,14 @@ import { translations } from "../translations";
|
||||
|
||||
export function DataTransferModalWrapper(
|
||||
connectionViewModel: ConnectionViewModel,
|
||||
fileTransferViewModel: FileTransferViewModel
|
||||
fileTransferViewModel: FileTransferViewModel,
|
||||
) {
|
||||
return (
|
||||
<div>
|
||||
{DirectionSelectionModal(connectionViewModel, fileTransferViewModel)}
|
||||
{DirectionSelectionModal(
|
||||
connectionViewModel,
|
||||
fileTransferViewModel,
|
||||
)}
|
||||
{FileSelectionModal(fileTransferViewModel)}
|
||||
{TransferDataDisplayModal(fileTransferViewModel)}
|
||||
{TransferDisplayModal(fileTransferViewModel)}
|
||||
@@ -29,27 +32,32 @@ export function DataTransferModalWrapper(
|
||||
|
||||
function DirectionSelectionModal(
|
||||
connectionViewModel: ConnectionViewModel,
|
||||
fileTransferViewModel: FileTransferViewModel
|
||||
fileTransferViewModel: FileTransferViewModel,
|
||||
) {
|
||||
const isPresented = React.createProxyState(
|
||||
[fileTransferViewModel.presentedModal],
|
||||
() =>
|
||||
fileTransferViewModel.presentedModal.value ==
|
||||
FileTransferModal.DirectionSelection
|
||||
FileTransferModal.DirectionSelection,
|
||||
);
|
||||
|
||||
const isDisconnected = React.createProxyState(
|
||||
[connectionViewModel.isConnected],
|
||||
() => connectionViewModel.isConnected.value == false
|
||||
() => connectionViewModel.isConnected.value == false,
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.dataTransferModal.transferDataHeadline}</h2>
|
||||
<h2>
|
||||
{translations.dataTransferModal.transferDataHeadline}
|
||||
</h2>
|
||||
|
||||
<p class="error" toggle:hidden={connectionViewModel.isConnected}>
|
||||
<p
|
||||
class="error"
|
||||
toggle:hidden={connectionViewModel.isConnected}
|
||||
>
|
||||
{translations.dataTransferModal.notConnectedError}
|
||||
</p>
|
||||
|
||||
@@ -59,21 +67,35 @@ function DirectionSelectionModal(
|
||||
>
|
||||
<button
|
||||
class="tile"
|
||||
on:click={fileTransferViewModel.showFileSelectionModal}
|
||||
on:click={
|
||||
fileTransferViewModel.showFileSelectionModal
|
||||
}
|
||||
>
|
||||
<span class="icon">upload</span>
|
||||
<div>
|
||||
<b>{translations.dataTransferModal.fromThisDeviceButton}</b>
|
||||
<b>
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.fromThisDeviceButton
|
||||
}
|
||||
</b>
|
||||
</div>
|
||||
<span class="icon">arrow_forward</span>
|
||||
</button>
|
||||
<button
|
||||
class="tile"
|
||||
on:click={fileTransferViewModel.showTransferDataInputModal}
|
||||
on:click={
|
||||
fileTransferViewModel.showTransferDataInputModal
|
||||
}
|
||||
>
|
||||
<span class="icon">download</span>
|
||||
<div>
|
||||
<b>{translations.dataTransferModal.toThisDeviceButton}</b>
|
||||
<b>
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.toThisDeviceButton
|
||||
}
|
||||
</b>
|
||||
</div>
|
||||
<span class="icon">arrow_forward</span>
|
||||
</button>
|
||||
@@ -91,14 +113,14 @@ function DirectionSelectionModal(
|
||||
function FileSelectionModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
// option
|
||||
const OptionConverter: React.StateItemConverter<FileTransferOption> = (
|
||||
fileOption: FileTransferOption
|
||||
fileOption: FileTransferOption,
|
||||
) => {
|
||||
return OptionEntry(fileOption, fileTransferViewModel);
|
||||
};
|
||||
|
||||
function OptionEntry(
|
||||
fileOption: FileTransferOption,
|
||||
fileTransferViewModel: FileTransferViewModel
|
||||
fileTransferViewModel: FileTransferViewModel,
|
||||
) {
|
||||
const isSelected = new React.State(false);
|
||||
if (fileTransferViewModel.selectedPaths.value.has(fileOption.path)) {
|
||||
@@ -120,7 +142,9 @@ function FileSelectionModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
<div>
|
||||
<b class="ellipsis">{fileOption.label}</b>
|
||||
<span class="secondary ellipsis">
|
||||
{StorageModel.pathComponentsToString(...fileOption.path)}
|
||||
{StorageModel.pathComponentsToString(
|
||||
...fileOption.path,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
@@ -132,14 +156,16 @@ function FileSelectionModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
[fileTransferViewModel.presentedModal],
|
||||
() =>
|
||||
fileTransferViewModel.presentedModal.value ==
|
||||
FileTransferModal.FileSelection
|
||||
FileTransferModal.FileSelection,
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.dataTransferModal.transferDataHeadline}</h2>
|
||||
<h2>
|
||||
{translations.dataTransferModal.transferDataHeadline}
|
||||
</h2>
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.selectionDescription}
|
||||
</span>
|
||||
@@ -167,14 +193,18 @@ function FileSelectionModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
<div class="flex-row width-100">
|
||||
<button
|
||||
class="flex"
|
||||
on:click={fileTransferViewModel.showDirectionSelectionModal}
|
||||
on:click={
|
||||
fileTransferViewModel.showDirectionSelectionModal
|
||||
}
|
||||
>
|
||||
{translations.general.backButton}
|
||||
</button>
|
||||
<button
|
||||
class="primary flex"
|
||||
on:click={fileTransferViewModel.showTransferDataModal}
|
||||
toggle:disabled={fileTransferViewModel.hasNoPathsSelected}
|
||||
toggle:disabled={
|
||||
fileTransferViewModel.hasNoPathsSelected
|
||||
}
|
||||
>
|
||||
{translations.general.continueButton}
|
||||
<span class="icon">arrow_forward</span>
|
||||
@@ -186,20 +216,22 @@ function FileSelectionModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
}
|
||||
|
||||
function TransferDataDisplayModal(
|
||||
fileTransferViewModel: FileTransferViewModel
|
||||
fileTransferViewModel: FileTransferViewModel,
|
||||
) {
|
||||
const isPresented = React.createProxyState(
|
||||
[fileTransferViewModel.presentedModal],
|
||||
() =>
|
||||
fileTransferViewModel.presentedModal.value ==
|
||||
FileTransferModal.TransferDataDisplay
|
||||
FileTransferModal.TransferDataDisplay,
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.dataTransferModal.transferDataHeadline}</h2>
|
||||
<h2>
|
||||
{translations.dataTransferModal.transferDataHeadline}
|
||||
</h2>
|
||||
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.dataEntryDescription}
|
||||
@@ -212,10 +244,15 @@ function TransferDataDisplayModal(
|
||||
<span class="icon">forum</span>
|
||||
<div>
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.transferChannelHeadline}
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.transferChannelHeadline
|
||||
}
|
||||
</span>
|
||||
<b
|
||||
subscribe:innerText={fileTransferViewModel.transferChannel}
|
||||
subscribe:innerText={
|
||||
fileTransferViewModel.transferChannel
|
||||
}
|
||||
></b>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,9 +260,16 @@ function TransferDataDisplayModal(
|
||||
<span class="icon">key</span>
|
||||
<div>
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.transferKeyHeadline}
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.transferKeyHeadline
|
||||
}
|
||||
</span>
|
||||
<b subscribe:innerText={fileTransferViewModel.transferKey}></b>
|
||||
<b
|
||||
subscribe:innerText={
|
||||
fileTransferViewModel.transferKey
|
||||
}
|
||||
></b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,23 +299,29 @@ function TransferDisplayModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
[fileTransferViewModel.presentedModal],
|
||||
() =>
|
||||
fileTransferViewModel.presentedModal.value ==
|
||||
FileTransferModal.TransferDisplay
|
||||
FileTransferModal.TransferDisplay,
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.dataTransferModal.transferDataHeadline}</h2>
|
||||
<h2>
|
||||
{translations.dataTransferModal.transferDataHeadline}
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="secondary"
|
||||
subscribe:innerText={fileTransferViewModel.filesSentText}
|
||||
subscribe:innerText={
|
||||
fileTransferViewModel.filesSentText
|
||||
}
|
||||
></p>
|
||||
|
||||
<p
|
||||
class="secondary"
|
||||
toggle:hidden={fileTransferViewModel.didNotFinishSending}
|
||||
toggle:hidden={
|
||||
fileTransferViewModel.didNotFinishSending
|
||||
}
|
||||
>
|
||||
{translations.dataTransferModal.allFilesSent}
|
||||
</p>
|
||||
@@ -297,7 +347,9 @@ function TransferDisplayModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
<button
|
||||
class="flex"
|
||||
on:click={fileTransferViewModel.hideModal}
|
||||
toggle:disabled={fileTransferViewModel.didNotFinishSending}
|
||||
toggle:disabled={
|
||||
fileTransferViewModel.didNotFinishSending
|
||||
}
|
||||
>
|
||||
{translations.general.closeButton}
|
||||
<span class="icon">close</span>
|
||||
@@ -313,17 +365,22 @@ function TransferDataInputModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
[fileTransferViewModel.presentedModal],
|
||||
() =>
|
||||
fileTransferViewModel.presentedModal.value ==
|
||||
FileTransferModal.TransferDataInput
|
||||
FileTransferModal.TransferDataInput,
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.dataTransferModal.transferDataHeadline}</h2>
|
||||
<h2>
|
||||
{translations.dataTransferModal.transferDataHeadline}
|
||||
</h2>
|
||||
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.dataEntryInputDescription}
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.dataEntryInputDescription
|
||||
}
|
||||
</span>
|
||||
|
||||
<hr></hr>
|
||||
@@ -333,10 +390,15 @@ function TransferDataInputModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
<span class="icon">forum</span>
|
||||
<div>
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.transferChannelHeadline}
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.transferChannelHeadline
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
bind:value={fileTransferViewModel.receivingTransferChannel}
|
||||
bind:value={
|
||||
fileTransferViewModel.receivingTransferChannel
|
||||
}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -344,10 +406,15 @@ function TransferDataInputModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
<span class="icon">key</span>
|
||||
<div>
|
||||
<span class="secondary">
|
||||
{translations.dataTransferModal.transferKeyHeadline}
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.transferKeyHeadline
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
bind:value={fileTransferViewModel.receivingTransferKey}
|
||||
bind:value={
|
||||
fileTransferViewModel.receivingTransferKey
|
||||
}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -356,14 +423,18 @@ function TransferDataInputModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
<div class="flex-row width-100">
|
||||
<button
|
||||
class="flex"
|
||||
on:click={fileTransferViewModel.showDirectionSelectionModal}
|
||||
on:click={
|
||||
fileTransferViewModel.showDirectionSelectionModal
|
||||
}
|
||||
>
|
||||
{translations.general.backButton}
|
||||
</button>
|
||||
<button
|
||||
class="primary flex"
|
||||
on:click={fileTransferViewModel.prepareReceivingData}
|
||||
toggle:disabled={fileTransferViewModel.cannotPrepareToReceive}
|
||||
toggle:disabled={
|
||||
fileTransferViewModel.cannotPrepareToReceive
|
||||
}
|
||||
>
|
||||
{translations.general.continueButton}
|
||||
<span class="icon">arrow_forward</span>
|
||||
@@ -379,22 +450,29 @@ function DataReceptionModal(fileTransferViewModel: FileTransferViewModel) {
|
||||
[fileTransferViewModel.presentedModal],
|
||||
() =>
|
||||
fileTransferViewModel.presentedModal.value ==
|
||||
FileTransferModal.ReceptionDisplay
|
||||
FileTransferModal.ReceptionDisplay,
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translations.dataTransferModal.transferDataHeadline}</h2>
|
||||
<h2>
|
||||
{translations.dataTransferModal.transferDataHeadline}
|
||||
</h2>
|
||||
|
||||
<p class="secondary">
|
||||
{translations.dataTransferModal.readyToReceiveDescription}
|
||||
{
|
||||
translations.dataTransferModal
|
||||
.readyToReceiveDescription
|
||||
}
|
||||
</p>
|
||||
|
||||
<p
|
||||
class="secondary"
|
||||
subscribe:innerText={fileTransferViewModel.filesReceivedText}
|
||||
subscribe:innerText={
|
||||
fileTransferViewModel.filesReceivedText
|
||||
}
|
||||
></p>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
@@ -9,7 +9,7 @@ export function SearchModal<T>(
|
||||
searchViewModel: SearchViewModel<T>,
|
||||
headline: string,
|
||||
converter: React.StateItemConverter<T>,
|
||||
isOpen: React.State<boolean>
|
||||
isOpen: React.State<boolean>,
|
||||
) {
|
||||
function close() {
|
||||
isOpen.value = false;
|
||||
@@ -32,11 +32,16 @@ export function SearchModal<T>(
|
||||
<datalist
|
||||
hidden
|
||||
id={suggestionId}
|
||||
children:append={[searchViewModel.suggestions, StringToOption]}
|
||||
children:append={[
|
||||
searchViewModel.suggestions,
|
||||
StringToOption,
|
||||
]}
|
||||
></datalist>
|
||||
<button
|
||||
class="primary"
|
||||
aria-label={translations.general.searchButtonAudioLabel}
|
||||
aria-label={
|
||||
translations.general.searchButtonAudioLabel
|
||||
}
|
||||
on:click={searchViewModel.applySearch}
|
||||
toggle:disabled={searchViewModel.cannotApplySearch}
|
||||
>
|
||||
@@ -49,7 +54,10 @@ export function SearchModal<T>(
|
||||
<div
|
||||
class="grid gap"
|
||||
style="grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))"
|
||||
children:append={[searchViewModel.matchingObjects, converter]}
|
||||
children:append={[
|
||||
searchViewModel.matchingObjects,
|
||||
converter,
|
||||
]}
|
||||
></div>
|
||||
</main>
|
||||
<button on:click={close}>
|
||||
|
||||
@@ -18,7 +18,7 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
const statusSuggestionId = v4();
|
||||
|
||||
const BoardOptionConverter: React.StateItemConverter<Entry> = (
|
||||
entry: Entry
|
||||
entry: Entry,
|
||||
) => {
|
||||
const isSelected = entry[0] == taskViewModel.task.boardId;
|
||||
return Option(entry[1], entry[0], isSelected);
|
||||
@@ -36,7 +36,10 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<span>{translations.general.fileVersionLabel}</span>
|
||||
<select
|
||||
bind:value={taskViewModel.selectedVersionId}
|
||||
children:append={[taskViewModel.versionIds, VersionIdToOption]}
|
||||
children:append={[
|
||||
taskViewModel.versionIds,
|
||||
VersionIdToOption,
|
||||
]}
|
||||
></select>
|
||||
<span class="icon">arrow_drop_down</span>
|
||||
</div>
|
||||
@@ -47,7 +50,9 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">label</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskNameLabel}</span>
|
||||
<span>
|
||||
{translations.chatPage.task.taskNameLabel}
|
||||
</span>
|
||||
<input bind:value={taskViewModel.name}></input>
|
||||
</div>
|
||||
</label>
|
||||
@@ -55,11 +60,14 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">category</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskBoardLabel}</span>
|
||||
<span>
|
||||
{translations.chatPage.task.taskBoardLabel}
|
||||
</span>
|
||||
<select
|
||||
bind:value={taskViewModel.boardId}
|
||||
children:append={[
|
||||
taskViewModel.chatViewModel.taskBoardSuggestions,
|
||||
taskViewModel.chatViewModel
|
||||
.taskBoardSuggestions,
|
||||
BoardOptionConverter,
|
||||
]}
|
||||
></select>
|
||||
@@ -70,7 +78,12 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">description</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskDescriptionLabel}</span>
|
||||
<span>
|
||||
{
|
||||
translations.chatPage.task
|
||||
.taskDescriptionLabel
|
||||
}
|
||||
</span>
|
||||
<textarea
|
||||
rows="10"
|
||||
bind:value={taskViewModel.description}
|
||||
@@ -83,7 +96,9 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">category</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskCategoryLabel}</span>
|
||||
<span>
|
||||
{translations.chatPage.task.taskCategoryLabel}
|
||||
</span>
|
||||
<input
|
||||
bind:value={taskViewModel.category}
|
||||
list={categorySuggestionId}
|
||||
@@ -102,7 +117,9 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">clock_loader_40</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskStatusLabel}</span>
|
||||
<span>
|
||||
{translations.chatPage.task.taskStatusLabel}
|
||||
</span>
|
||||
<input
|
||||
bind:value={taskViewModel.status}
|
||||
list={statusSuggestionId}
|
||||
@@ -121,8 +138,13 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">priority_high</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskPriorityLabel}</span>
|
||||
<input type="number" bind:value={taskViewModel.priority}></input>
|
||||
<span>
|
||||
{translations.chatPage.task.taskPriorityLabel}
|
||||
</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={taskViewModel.priority}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -131,16 +153,26 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">calendar_month</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskDateLabel}</span>
|
||||
<input type="date" bind:value={taskViewModel.date}></input>
|
||||
<span>
|
||||
{translations.chatPage.task.taskDateLabel}
|
||||
</span>
|
||||
<input
|
||||
type="date"
|
||||
bind:value={taskViewModel.date}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="tile flex-no">
|
||||
<span class="icon">schedule</span>
|
||||
<div>
|
||||
<span>{translations.chatPage.task.taskTimeLabel}</span>
|
||||
<input type="time" bind:value={taskViewModel.time}></input>
|
||||
<span>
|
||||
{translations.chatPage.task.taskTimeLabel}
|
||||
</span>
|
||||
<input
|
||||
type="time"
|
||||
bind:value={taskViewModel.time}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -150,15 +182,21 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
||||
{DangerousActionButton(
|
||||
translations.chatPage.task.deleteTaskButton,
|
||||
"delete_forever",
|
||||
taskViewModel.deleteTask
|
||||
taskViewModel.deleteTask,
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
<div class="flex-row width-100">
|
||||
<button class="flex" on:click={taskViewModel.closeAndDiscard}>
|
||||
<button
|
||||
class="flex"
|
||||
on:click={taskViewModel.closeAndDiscard}
|
||||
>
|
||||
{translations.general.closeButton}
|
||||
</button>
|
||||
<button class="flex primary" on:click={taskViewModel.closeAndSave}>
|
||||
<button
|
||||
class="flex primary"
|
||||
on:click={taskViewModel.closeAndSave}
|
||||
>
|
||||
{translations.general.saveButton}
|
||||
<span class="icon">save</span>
|
||||
</button>
|
||||
|
||||
@@ -25,13 +25,13 @@ export function ChatPage(chatViewModel: ChatViewModel) {
|
||||
return TaskPage(chatViewModel.taskPageViewModel);
|
||||
}
|
||||
case ChatPageType.Calendar: {
|
||||
return CalendarPage(chatViewModel.calendarViewModel)
|
||||
return CalendarPage(chatViewModel.calendarViewModel);
|
||||
}
|
||||
default: {
|
||||
return MessagePage(chatViewModel.messagePageViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -55,25 +55,25 @@ export function ChatPage(chatViewModel: ChatViewModel) {
|
||||
translations.chatPage.pages.calendar,
|
||||
"calendar_month",
|
||||
ChatPageType.Calendar,
|
||||
chatViewModel
|
||||
chatViewModel,
|
||||
)}
|
||||
{ChatViewToggleButton(
|
||||
translations.chatPage.pages.tasks,
|
||||
"task_alt",
|
||||
ChatPageType.Tasks,
|
||||
chatViewModel
|
||||
chatViewModel,
|
||||
)}
|
||||
{ChatViewToggleButton(
|
||||
translations.chatPage.pages.messages,
|
||||
"forum",
|
||||
ChatPageType.Messages,
|
||||
chatViewModel
|
||||
chatViewModel,
|
||||
)}
|
||||
{ChatViewToggleButton(
|
||||
translations.chatPage.pages.settings,
|
||||
"settings",
|
||||
ChatPageType.Settings,
|
||||
chatViewModel
|
||||
chatViewModel,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -14,13 +14,8 @@ export function ChatPageWrapper(chatListViewModel: ChatListViewModel) {
|
||||
} else {
|
||||
return ChatPage(chatListViewModel.selectedChat.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
id="chat-page-wrapper"
|
||||
children:set={chatPageContent}
|
||||
></div>
|
||||
);
|
||||
return <div id="chat-page-wrapper" children:set={chatPageContent}></div>;
|
||||
}
|
||||
|
||||
+20
-8
@@ -17,7 +17,7 @@ export function HomePage(
|
||||
settingsViewModel: SettingsViewModel,
|
||||
connectionViewModel: ConnectionViewModel,
|
||||
fileTransferViewModel: FileTransferViewModel,
|
||||
chatListViewModel: ChatListViewModel
|
||||
chatListViewModel: ChatListViewModel,
|
||||
) {
|
||||
// sections
|
||||
const overviewSection = (
|
||||
@@ -30,7 +30,9 @@ export function HomePage(
|
||||
<span>{translations.homePage.serverAddress}</span>
|
||||
<input
|
||||
list="previous-connection-list"
|
||||
placeholder={translations.homePage.serverAddressPlaceholder}
|
||||
placeholder={
|
||||
translations.homePage.serverAddressPlaceholder
|
||||
}
|
||||
bind:value={connectionViewModel.serverAddressInput}
|
||||
on:enter={connectionViewModel.connect}
|
||||
></input>
|
||||
@@ -56,9 +58,13 @@ export function HomePage(
|
||||
</button>
|
||||
<button
|
||||
class="flex justify-center"
|
||||
aria-label={translations.homePage.manageConnectionsAudioLabel}
|
||||
aria-label={
|
||||
translations.homePage.manageConnectionsAudioLabel
|
||||
}
|
||||
on:click={connectionViewModel.showConnectionModal}
|
||||
toggle:disabled={connectionViewModel.hasNoPreviousConnections}
|
||||
toggle:disabled={
|
||||
connectionViewModel.hasNoPreviousConnections
|
||||
}
|
||||
>
|
||||
<span class="icon">history</span>
|
||||
</button>
|
||||
@@ -104,12 +110,15 @@ export function HomePage(
|
||||
<div>
|
||||
<span>{translations.homePage.firstDayOfWeekLabel}</span>
|
||||
<select bind:value={settingsViewModel.firstDayOfWeekInput}>
|
||||
{...translations.regional.weekdays.full.map((weekdayName, i) =>
|
||||
{...translations.regional.weekdays.full.map(
|
||||
(weekdayName, i) =>
|
||||
Option(
|
||||
weekdayName,
|
||||
i.toString(),
|
||||
i.toString() == settingsViewModel.firstDayOfWeekInput.value
|
||||
)
|
||||
i.toString() ==
|
||||
settingsViewModel.firstDayOfWeekInput
|
||||
.value,
|
||||
),
|
||||
)}
|
||||
</select>
|
||||
<span class="icon">arrow_drop_down</span>
|
||||
@@ -128,7 +137,10 @@ export function HomePage(
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button class="tile flex-no" on:click={storageViewModel.showStorageModal}>
|
||||
<button
|
||||
class="tile flex-no"
|
||||
on:click={storageViewModel.showStorageModal}
|
||||
>
|
||||
<span class="icon">hard_drive_2</span>
|
||||
<div>
|
||||
<span>{translations.homePage.manageStorageButton}</span>
|
||||
|
||||
+28
-14
@@ -85,7 +85,8 @@ const englishTranslations = {
|
||||
transferDataHeadline: "Data Transfer",
|
||||
selectionDescription: "Select the data that you want to transfer.",
|
||||
dataEntryDescription: "Enter this data on the other device.",
|
||||
dataEntryInputDescription: "Enter the data displayed on the other device.",
|
||||
dataEntryInputDescription:
|
||||
"Enter the data displayed on the other device.",
|
||||
readyToReceiveDescription: "Click 'send' on the other device.",
|
||||
|
||||
notConnectedError: "You are not connected to any server.",
|
||||
@@ -350,12 +351,14 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
|
||||
dataTransferModal: {
|
||||
transferDataHeadline: "Datenübertragung",
|
||||
selectionDescription: "Wähle die Daten aus, die du übertragen möchtest.",
|
||||
selectionDescription:
|
||||
"Wähle die Daten aus, die du übertragen möchtest.",
|
||||
dataEntryDescription:
|
||||
"Gib diese Informationen auf dem anderen Gerät ein.",
|
||||
dataEntryInputDescription:
|
||||
"Gib die auf dem anderen Gerät angezeigten Informationen ein.",
|
||||
readyToReceiveDescription: "Klicke auf dem anderen Gerät auf 'Senden'.",
|
||||
readyToReceiveDescription:
|
||||
"Klicke auf dem anderen Gerät auf 'Senden'.",
|
||||
|
||||
notConnectedError: "Du bist mit keinem Server verbunden.",
|
||||
|
||||
@@ -412,8 +415,10 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
setPrimaryChannelButtonAudioLabel: "Hauptkanal festlegen",
|
||||
|
||||
newSecondaryChannelPlaceholder: "Sekundären Kanal hinzufügen",
|
||||
newSecondaryChannelAudioLabel: "Name des neuen sekundären Kanals",
|
||||
addSecondaryChannelButtonAudioLabel: "Sekundären Kanal hinzufügen",
|
||||
newSecondaryChannelAudioLabel:
|
||||
"Name des neuen sekundären Kanals",
|
||||
addSecondaryChannelButtonAudioLabel:
|
||||
"Sekundären Kanal hinzufügen",
|
||||
|
||||
encryptionKeyLabel: "Schlüssel",
|
||||
setEncryptionKeyButtonAudioLabel: "Schlüssel festlegen",
|
||||
@@ -451,7 +456,8 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
|
||||
closeBoardButtonAudioLabel: "board schließen",
|
||||
toggleBoardButtonAudioLabel: "board-liste ein/ausblenden",
|
||||
showBoardSettingsButtonAudioLabel: "Board-Einstellungen anzeigen",
|
||||
showBoardSettingsButtonAudioLabel:
|
||||
"Board-Einstellungen anzeigen",
|
||||
|
||||
listViewButtonAudioLabel: "Listenansicht",
|
||||
kanbanViewButtonAudioLabel: "Kanban-Ansicht",
|
||||
@@ -587,11 +593,14 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
|
||||
dataTransferModal: {
|
||||
transferDataHeadline: "Transferencia de Datos",
|
||||
selectionDescription: "Selecciona los datos que quieres transferir.",
|
||||
dataEntryDescription: "Introduce estos datos en el otro dispositivo.",
|
||||
selectionDescription:
|
||||
"Selecciona los datos que quieres transferir.",
|
||||
dataEntryDescription:
|
||||
"Introduce estos datos en el otro dispositivo.",
|
||||
dataEntryInputDescription:
|
||||
"Introduce los datos mostrados en el otro dispositivo.",
|
||||
readyToReceiveDescription: "Haz clic en 'enviar' en el otro dispositivo.",
|
||||
readyToReceiveDescription:
|
||||
"Haz clic en 'enviar' en el otro dispositivo.",
|
||||
|
||||
notConnectedError: "No estás conectado a ningún servidor.",
|
||||
|
||||
@@ -648,11 +657,13 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
setPrimaryChannelButtonAudioLabel: "establecer canal principal",
|
||||
|
||||
newSecondaryChannelPlaceholder: "Añadir canal secundario",
|
||||
newSecondaryChannelAudioLabel: "nombre del nuevo canal secundario",
|
||||
newSecondaryChannelAudioLabel:
|
||||
"nombre del nuevo canal secundario",
|
||||
addSecondaryChannelButtonAudioLabel: "añadir canal secundario",
|
||||
|
||||
encryptionKeyLabel: "Clave de encriptación",
|
||||
setEncryptionKeyButtonAudioLabel: "establecer clave de encriptación",
|
||||
setEncryptionKeyButtonAudioLabel:
|
||||
"establecer clave de encriptación",
|
||||
showEncryptionKey: "Mostrar clave de encriptación",
|
||||
|
||||
deleteChatButton: "Eliminar todo el chat",
|
||||
@@ -664,7 +675,8 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
composerInputPlaceholder: "Escribe un mensaje...",
|
||||
sendMessageButtonAudioLabel: "enviar mensaje",
|
||||
|
||||
showMessageInfoButtonAudioLabel: "mostrar información del mensaje",
|
||||
showMessageInfoButtonAudioLabel:
|
||||
"mostrar información del mensaje",
|
||||
messageInfoHeadline: "Información del Mensaje",
|
||||
|
||||
sentBy: "Enviado por",
|
||||
@@ -686,8 +698,10 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
|
||||
boardNotFound: "Tablero no encontrado",
|
||||
|
||||
closeBoardButtonAudioLabel: "cerrar tablero",
|
||||
toggleBoardButtonAudioLabel: "mostrar o ocultar lista de tableros",
|
||||
showBoardSettingsButtonAudioLabel: "mostrar configuración del tablero",
|
||||
toggleBoardButtonAudioLabel:
|
||||
"mostrar o ocultar lista de tableros",
|
||||
showBoardSettingsButtonAudioLabel:
|
||||
"mostrar configuración del tablero",
|
||||
|
||||
listViewButtonAudioLabel: "vista de lista",
|
||||
kanbanViewButtonAudioLabel: "vista kanban",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ export function allowDrop(event: DragEvent) {
|
||||
}
|
||||
|
||||
export function allowDrag(event: DragEvent) {
|
||||
event.dataTransfer?.setData("text", "")
|
||||
event.dataTransfer?.setData("text", "");
|
||||
}
|
||||
|
||||
export function reload() {
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class ChatListViewModel {
|
||||
// data
|
||||
chatIndexManager: IndexManager<ChatViewModel> = new IndexManager(
|
||||
(chatViewModel: ChatViewModel) =>
|
||||
chatViewModel.settingsPageViewModel.primaryChannel.value
|
||||
chatViewModel.settingsPageViewModel.primaryChannel.value,
|
||||
);
|
||||
|
||||
// state
|
||||
@@ -24,23 +24,24 @@ export default class ChatListViewModel {
|
||||
chatViewModels: React.ListState<ChatViewModel> = new React.ListState();
|
||||
|
||||
selectedChat: React.State<ChatViewModel | undefined> = new React.State<any>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
|
||||
// guards
|
||||
cannotCreateChat = React.createProxyState(
|
||||
[this.newChatPrimaryChannel],
|
||||
() => this.newChatPrimaryChannel.value == ""
|
||||
() => this.newChatPrimaryChannel.value == "",
|
||||
);
|
||||
|
||||
// methods
|
||||
createChat = (): void => {
|
||||
const chatModel: ChatModel = this.chatListModel.createChat(
|
||||
this.newChatPrimaryChannel.value
|
||||
this.newChatPrimaryChannel.value,
|
||||
);
|
||||
this.newChatPrimaryChannel.value = "";
|
||||
|
||||
const chatViewModel: ChatViewModel = this.createChatViewModel(chatModel);
|
||||
const chatViewModel: ChatViewModel =
|
||||
this.createChatViewModel(chatModel);
|
||||
this.trackChat(chatViewModel);
|
||||
this.updateIndices();
|
||||
};
|
||||
@@ -60,7 +61,7 @@ export default class ChatListViewModel {
|
||||
this.storageModel,
|
||||
chatModel,
|
||||
this.settingsViewModel,
|
||||
this
|
||||
this,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -95,7 +96,7 @@ export default class ChatListViewModel {
|
||||
public coreViewModel: CoreViewModel,
|
||||
storageModel: StorageModel,
|
||||
chatListModel: ChatListModel,
|
||||
settingsViewModel: SettingsViewModel
|
||||
settingsViewModel: SettingsViewModel,
|
||||
) {
|
||||
this.storageModel = storageModel;
|
||||
this.chatListModel = chatListModel;
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class ChatMessageViewModel {
|
||||
dateSent: string;
|
||||
body: React.State<string> = new React.State("");
|
||||
status: React.State<ChatMessageStatus | any> = new React.State<any>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
sentByUser: boolean;
|
||||
|
||||
@@ -56,7 +56,7 @@ export default class ChatMessageViewModel {
|
||||
public coreViewModel: CoreViewModel,
|
||||
messagePageViewModel: MessagePageViewModel,
|
||||
chatMessage: ChatMessage,
|
||||
sentByUser: boolean
|
||||
sentByUser: boolean,
|
||||
) {
|
||||
this.messagePageViewModel = messagePageViewModel;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class ChatViewModel {
|
||||
// state
|
||||
displayedColor: React.State<Color> = new React.State<any>(Color.Standard);
|
||||
selectedPage: React.State<ChatPageType> = new React.State<any>(
|
||||
ChatPageType.Messages
|
||||
ChatPageType.Messages,
|
||||
);
|
||||
|
||||
index: React.State<number> = new React.State(0);
|
||||
@@ -81,7 +81,7 @@ export default class ChatViewModel {
|
||||
loadPageSelection = (): void => {
|
||||
const path: string[] = StorageModel.getPath(
|
||||
StorageModelSubPath.Chat,
|
||||
filePaths.chat.lastUsedPage(this.chatModel.id)
|
||||
filePaths.chat.lastUsedPage(this.chatModel.id),
|
||||
);
|
||||
const lastUsedPage: string | null = this.storageModel.read(path);
|
||||
if (lastUsedPage != null) {
|
||||
@@ -109,7 +109,7 @@ export default class ChatViewModel {
|
||||
storageModel: StorageModel,
|
||||
chatModel: ChatModel,
|
||||
settingsViewModel: SettingsViewModel,
|
||||
chatListViewModel: ChatListViewModel
|
||||
chatListViewModel: ChatListViewModel,
|
||||
) {
|
||||
// models
|
||||
this.storageModel = storageModel;
|
||||
@@ -123,21 +123,21 @@ export default class ChatViewModel {
|
||||
this,
|
||||
this.storageModel,
|
||||
this.chatModel.fileModel.boardsAndTasksModel.calendarModel,
|
||||
this.chatModel.fileModel.boardsAndTasksModel
|
||||
this.chatModel.fileModel.boardsAndTasksModel,
|
||||
);
|
||||
this.taskPageViewModel = new TaskPageViewModel(
|
||||
this.coreViewModel,
|
||||
this,
|
||||
this.storageModel,
|
||||
this.chatModel.fileModel.boardsAndTasksModel
|
||||
this.chatModel.fileModel.boardsAndTasksModel,
|
||||
);
|
||||
this.messagePageViewModel = new MessagePageViewModel(
|
||||
this.coreViewModel,
|
||||
this
|
||||
this,
|
||||
);
|
||||
this.settingsPageViewModel = new SettingsPageViewModel(
|
||||
this.coreViewModel,
|
||||
this
|
||||
this,
|
||||
);
|
||||
|
||||
// handlers
|
||||
@@ -145,7 +145,7 @@ export default class ChatViewModel {
|
||||
(chatMessage: ChatMessage) => {
|
||||
this.messagePageViewModel.showChatMessage(chatMessage);
|
||||
this.markRead();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// load
|
||||
|
||||
@@ -19,16 +19,17 @@ export default class ConnectionViewModel {
|
||||
[this.serverAddressInput, this.isConnected],
|
||||
() =>
|
||||
(this.isConnected.value == true &&
|
||||
this.serverAddressInput.value == this.connectionModel.address) ||
|
||||
this.serverAddressInput.value == ""
|
||||
this.serverAddressInput.value ==
|
||||
this.connectionModel.address) ||
|
||||
this.serverAddressInput.value == "",
|
||||
);
|
||||
cannotDisonnect: React.State<boolean> = React.createProxyState(
|
||||
[this.isConnected],
|
||||
() => this.isConnected.value == false
|
||||
() => this.isConnected.value == false,
|
||||
);
|
||||
hasNoPreviousConnections: React.State<boolean> = React.createProxyState(
|
||||
[this.previousAddresses],
|
||||
() => this.previousAddresses.value.size == 0
|
||||
() => this.previousAddresses.value.size == 0,
|
||||
);
|
||||
|
||||
// handlers
|
||||
@@ -80,13 +81,13 @@ export default class ConnectionViewModel {
|
||||
// init
|
||||
constructor(
|
||||
public coreViewModel: CoreViewModel,
|
||||
connectionModel: ConnectionModel
|
||||
connectionModel: ConnectionModel,
|
||||
) {
|
||||
this.connectionModel = connectionModel;
|
||||
this.updatePreviousAddresses();
|
||||
|
||||
connectionModel.connectionChangeHandlerManager.addHandler(
|
||||
this.connectionChangeHandler
|
||||
this.connectionChangeHandler,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ export default class FileTransferViewModel {
|
||||
|
||||
generalFileOptions: React.ListState<FileTransferOption> =
|
||||
new React.ListState();
|
||||
chatFileOptions: React.ListState<FileTransferOption> = new React.ListState();
|
||||
chatFileOptions: React.ListState<FileTransferOption> =
|
||||
new React.ListState();
|
||||
selectedPaths: React.ListState<string[]> = new React.ListState();
|
||||
|
||||
transferChannel: React.State<string> = new React.State("");
|
||||
@@ -34,38 +35,40 @@ export default class FileTransferViewModel {
|
||||
filePathsSent: React.ListState<string> = new React.ListState();
|
||||
filesSentCount: React.State<number> = React.createProxyState(
|
||||
[this.filePathsSent],
|
||||
() => this.filePathsSent.value.size
|
||||
() => this.filePathsSent.value.size,
|
||||
);
|
||||
filesSentText: React.State<string> = React.createProxyState(
|
||||
[this.filesSentCount],
|
||||
() =>
|
||||
translations.dataTransferModal.filesSentCount(this.filesSentCount.value)
|
||||
translations.dataTransferModal.filesSentCount(
|
||||
this.filesSentCount.value,
|
||||
),
|
||||
);
|
||||
|
||||
filePathsReceived: React.ListState<string> = new React.ListState();
|
||||
filesReceivedCount: React.State<number> = React.createProxyState(
|
||||
[this.filePathsReceived],
|
||||
() => this.filePathsReceived.value.size
|
||||
() => this.filePathsReceived.value.size,
|
||||
);
|
||||
filesReceivedText: React.State<string> = React.createProxyState(
|
||||
[this.filesReceivedCount],
|
||||
() =>
|
||||
translations.dataTransferModal.filesReceivedCount(
|
||||
this.filesReceivedCount.value
|
||||
)
|
||||
this.filesReceivedCount.value,
|
||||
),
|
||||
);
|
||||
|
||||
// guards
|
||||
hasNoPathsSelected: React.State<boolean> = React.createProxyState(
|
||||
[this.selectedPaths],
|
||||
() => this.selectedPaths.value.size == 0
|
||||
() => this.selectedPaths.value.size == 0,
|
||||
);
|
||||
didNotFinishSending: React.State<boolean> = new React.State(true);
|
||||
cannotPrepareToReceive: React.State<boolean> = React.createProxyState(
|
||||
[this.receivingTransferChannel, this.receivingTransferKey],
|
||||
() =>
|
||||
this.receivingTransferChannel.value == "" ||
|
||||
this.receivingTransferKey.value == ""
|
||||
this.receivingTransferKey.value == "",
|
||||
);
|
||||
|
||||
// handlers
|
||||
@@ -84,13 +87,16 @@ export default class FileTransferViewModel {
|
||||
label: translations.dataTransferModal.connectionData,
|
||||
path: StorageModel.getPath(
|
||||
StorageModelSubPath.ConnectionModel,
|
||||
filePaths.connectionModel.previousAddresses
|
||||
filePaths.connectionModel.previousAddresses,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: translations.dataTransferModal.settingsData,
|
||||
path: StorageModel.getPath(StorageModelSubPath.SettingsModel, []),
|
||||
}
|
||||
path: StorageModel.getPath(
|
||||
StorageModelSubPath.SettingsModel,
|
||||
[],
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const chatModels: Set<ChatModel> = this.chatListModel.chatModels;
|
||||
@@ -134,7 +140,7 @@ export default class FileTransferViewModel {
|
||||
(path: string) => {
|
||||
console.log(path);
|
||||
this.filePathsSent.add(path);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.didNotFinishSending.value = false;
|
||||
@@ -162,13 +168,13 @@ export default class FileTransferViewModel {
|
||||
// init
|
||||
constructor(
|
||||
fileTransferModel: FileTransferModel,
|
||||
chatListModel: ChatListModel
|
||||
chatListModel: ChatListModel,
|
||||
) {
|
||||
this.fileTransferModel = fileTransferModel;
|
||||
this.chatListModel = chatListModel;
|
||||
|
||||
this.fileTransferModel.fileHandlerManager.addHandler(
|
||||
this.handleReceivedFile
|
||||
this.handleReceivedFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class SettingsViewModel {
|
||||
[this.usernameInput],
|
||||
() =>
|
||||
this.usernameInput.value == "" ||
|
||||
this.usernameInput.value == this.settingsModel.username
|
||||
this.usernameInput.value == this.settingsModel.username,
|
||||
);
|
||||
|
||||
// methods
|
||||
@@ -33,7 +33,7 @@ export default class SettingsViewModel {
|
||||
// init
|
||||
constructor(
|
||||
public coreViewModel: CoreViewModel,
|
||||
settingsModel: SettingsModel
|
||||
settingsModel: SettingsModel,
|
||||
) {
|
||||
this.settingsModel = settingsModel;
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ export default class StorageViewModel {
|
||||
|
||||
// state
|
||||
isShowingStorageModal: React.State<boolean> = new React.State(false);
|
||||
selectedPath: React.State<string> = new React.State(PATH_COMPONENT_SEPARATOR);
|
||||
selectedPath: React.State<string> = new React.State(
|
||||
PATH_COMPONENT_SEPARATOR,
|
||||
);
|
||||
didMakeChanges: React.State<boolean> = new React.State(false);
|
||||
|
||||
selectedFileName: React.State<string>;
|
||||
@@ -22,7 +24,9 @@ export default class StorageViewModel {
|
||||
|
||||
// methods
|
||||
getSelectedItemContent = (): string => {
|
||||
const path = StorageModel.stringToPathComponents(this.selectedPath.value);
|
||||
const path = StorageModel.stringToPathComponents(
|
||||
this.selectedPath.value,
|
||||
);
|
||||
const content = this.storageModel.read(path);
|
||||
return (
|
||||
(content ?? translations.storage.notAFile) ||
|
||||
@@ -31,7 +35,9 @@ export default class StorageViewModel {
|
||||
};
|
||||
|
||||
deleteSelectedItem = (): void => {
|
||||
const path = StorageModel.stringToPathComponents(this.selectedPath.value);
|
||||
const path = StorageModel.stringToPathComponents(
|
||||
this.selectedPath.value,
|
||||
);
|
||||
this.lastDeletedItemPath.value = this.selectedPath.value;
|
||||
|
||||
this.storageModel.removeRecursively(path);
|
||||
@@ -57,14 +63,19 @@ export default class StorageViewModel {
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(public coreViewModel: CoreViewModel, storageModel: StorageModel) {
|
||||
constructor(
|
||||
public coreViewModel: CoreViewModel,
|
||||
storageModel: StorageModel,
|
||||
) {
|
||||
this.storageModel = storageModel;
|
||||
|
||||
this.selectedFileName = React.createProxyState([this.selectedPath], () =>
|
||||
StorageModel.getFileNameFromString(this.selectedPath.value)
|
||||
this.selectedFileName = React.createProxyState(
|
||||
[this.selectedPath],
|
||||
() => StorageModel.getFileNameFromString(this.selectedPath.value),
|
||||
);
|
||||
this.selectedFileContent = React.createProxyState([this.selectedPath], () =>
|
||||
this.getSelectedItemContent()
|
||||
this.selectedFileContent = React.createProxyState(
|
||||
[this.selectedPath],
|
||||
() => this.getSelectedItemContent(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
index: React.State<number> = new React.State(0);
|
||||
|
||||
selectedPage: React.State<BoardPageType> = new React.State<any>(
|
||||
BoardPageType.List
|
||||
BoardPageType.List,
|
||||
);
|
||||
|
||||
isSelected: React.State<boolean>;
|
||||
@@ -43,7 +43,9 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
|
||||
// paths
|
||||
getBasePath = (): string[] => {
|
||||
return [...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)];
|
||||
return [
|
||||
...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId),
|
||||
];
|
||||
};
|
||||
|
||||
getLastUsedBoardPath = (): string[] => {
|
||||
@@ -64,13 +66,15 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
BoardsAndTasksModel.createBoardInfoFileContent(
|
||||
this.boardInfo.fileId,
|
||||
this.name.value,
|
||||
this.color.value
|
||||
this.color.value,
|
||||
);
|
||||
this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
|
||||
};
|
||||
|
||||
applyColor = (): void => {
|
||||
this.taskPageViewModel.chatViewModel.setDisplayedColor(this.color.value);
|
||||
this.taskPageViewModel.chatViewModel.setDisplayedColor(
|
||||
this.color.value,
|
||||
);
|
||||
};
|
||||
|
||||
deleteBoard = (): void => {
|
||||
@@ -131,7 +135,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
// remove task that was moved to different board
|
||||
this.boardsAndTasksModel.deleteTaskReference(
|
||||
this.boardInfo.fileId,
|
||||
taskFileContent.fileId
|
||||
taskFileContent.fileId,
|
||||
);
|
||||
this.removeTaskFromView(taskFileContent);
|
||||
return;
|
||||
@@ -142,7 +146,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
this.chatViewModel,
|
||||
this.boardsAndTasksModel,
|
||||
this,
|
||||
taskFileContent
|
||||
taskFileContent,
|
||||
);
|
||||
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
||||
};
|
||||
@@ -192,7 +196,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
|
||||
loadTasks = (): void => {
|
||||
const taskIds: string[] = this.boardsAndTasksModel.listTaskIds(
|
||||
this.boardInfo.fileId
|
||||
this.boardInfo.fileId,
|
||||
);
|
||||
for (const taskId of taskIds) {
|
||||
if (this.taskViewModels.value.has(taskId)) return;
|
||||
@@ -206,7 +210,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
this.chatViewModel,
|
||||
this.boardsAndTasksModel,
|
||||
this,
|
||||
taskFileContent
|
||||
taskFileContent,
|
||||
);
|
||||
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
|
||||
}
|
||||
@@ -222,7 +226,8 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
|
||||
restoreSearch = (): void => {
|
||||
const lastSearchPath: string[] = this.getLastSearchPath();
|
||||
const lastSearch: string | null = this.storageModel.read(lastSearchPath);
|
||||
const lastSearch: string | null =
|
||||
this.storageModel.read(lastSearchPath);
|
||||
if (lastSearch != null) {
|
||||
this.searchViewModel.search(lastSearch);
|
||||
}
|
||||
@@ -241,7 +246,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
storageModel: StorageModel,
|
||||
boardsAndTasksModel: BoardsAndTasksModel,
|
||||
taskPageViewModel: TaskPageViewModel,
|
||||
boardInfo: BoardInfoFileContent
|
||||
boardInfo: BoardInfoFileContent,
|
||||
) {
|
||||
super(coreViewModel, chatViewModel, boardsAndTasksModel);
|
||||
|
||||
@@ -258,7 +263,8 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
this.isSelected = React.createProxyState(
|
||||
[this.taskPageViewModel.selectedBoardId],
|
||||
() =>
|
||||
this.taskPageViewModel.selectedBoardId.value == this.boardInfo.fileId
|
||||
this.taskPageViewModel.selectedBoardId.value ==
|
||||
this.boardInfo.fileId,
|
||||
);
|
||||
|
||||
this.color.subscribe(() => {
|
||||
@@ -276,7 +282,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
if (taskFileContent.boardId != this.boardInfo.fileId) return;
|
||||
this.showTask(taskFileContent);
|
||||
this.updateTaskIndices();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// search
|
||||
@@ -284,7 +290,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
this.taskViewModels,
|
||||
this.filteredTaskViewModels,
|
||||
TaskViewModel.getStringsForFilter,
|
||||
this.coreViewModel.boardSearchSuggestions
|
||||
this.coreViewModel.boardSearchSuggestions,
|
||||
);
|
||||
this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => {
|
||||
this.handleNewSearch(newQuery);
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
get monthString(): string {
|
||||
return CalendarModel.getMonthString(
|
||||
this.selectedYear.value.toString(),
|
||||
this.selectedMonth.value.toString()
|
||||
this.selectedMonth.value.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,8 +36,9 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
selectedMonth: React.State<number> = new React.State(0);
|
||||
selectedDate: React.State<number> = new React.State(0);
|
||||
|
||||
monthGrid: React.State<MonthGrid<React.MapState<TaskViewModel>> | undefined> =
|
||||
new React.State<any>(undefined);
|
||||
monthGrid: React.State<
|
||||
MonthGrid<React.MapState<TaskViewModel>> | undefined
|
||||
> = new React.State<any>(undefined);
|
||||
|
||||
// methods
|
||||
createEvent = (): void => {
|
||||
@@ -46,7 +47,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
taskFileContent.date = CalendarModel.getISODateString(
|
||||
this.selectedYear.value.toString(),
|
||||
this.selectedMonth.value.toString(),
|
||||
this.selectedDate.value.toString()
|
||||
this.selectedDate.value.toString(),
|
||||
);
|
||||
|
||||
const taskViewModel: TaskViewModel = new TaskViewModel(
|
||||
@@ -54,7 +55,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
this.chatViewModel,
|
||||
this.boardsAndTasksModel,
|
||||
this,
|
||||
taskFileContent
|
||||
taskFileContent,
|
||||
);
|
||||
|
||||
this.selectTask(taskViewModel);
|
||||
@@ -63,7 +64,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
|
||||
getEventsForDate = (): React.MapState<TaskViewModel> | undefined => {
|
||||
const paddedDate: string = CalendarModel.padDateOrMonth(
|
||||
this.selectedDate.toString()
|
||||
this.selectedDate.toString(),
|
||||
);
|
||||
|
||||
if (this.monthGrid.value == undefined) {
|
||||
@@ -75,25 +76,25 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
|
||||
// view
|
||||
getTaskMapState = (
|
||||
taskFileContent: TaskFileContent
|
||||
taskFileContent: TaskFileContent,
|
||||
): React.MapState<TaskViewModel> | null => {
|
||||
if (this.monthGrid.value == null) return null;
|
||||
|
||||
const date: string = CalendarModel.isoToDateString(
|
||||
taskFileContent.date ?? ""
|
||||
taskFileContent.date ?? "",
|
||||
);
|
||||
return this.monthGrid.value.days[date];
|
||||
};
|
||||
|
||||
showTask = (taskFileContent: TaskFileContent): void => {
|
||||
const monthString: string | undefined = CalendarModel.isoToMonthString(
|
||||
taskFileContent.date ?? ""
|
||||
taskFileContent.date ?? "",
|
||||
);
|
||||
if (monthString == undefined || monthString != this.monthString) {
|
||||
this.removeTaskFromView(taskFileContent);
|
||||
this.calendarModel.deleteTaskReference(
|
||||
this.monthString,
|
||||
taskFileContent.fileId
|
||||
taskFileContent.fileId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -103,7 +104,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
this.chatViewModel,
|
||||
this.boardsAndTasksModel,
|
||||
this,
|
||||
taskFileContent
|
||||
taskFileContent,
|
||||
);
|
||||
const mapState: React.MapState<TaskViewModel> | null =
|
||||
this.getTaskMapState(taskFileContent);
|
||||
@@ -148,7 +149,11 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
};
|
||||
|
||||
handleDrop = (year: string, month: string, date: string): void => {
|
||||
const ISOString: string = CalendarModel.getISODateString(year, month, date);
|
||||
const ISOString: string = CalendarModel.getISODateString(
|
||||
year,
|
||||
month,
|
||||
date,
|
||||
);
|
||||
|
||||
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
||||
if (draggedObject instanceof TaskViewModel == false) return;
|
||||
@@ -161,9 +166,11 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
this.monthGrid.value = this.calendarModel.generateMonthGrid(
|
||||
this.selectedYear.value,
|
||||
this.selectedMonth.value,
|
||||
() => new React.MapState()
|
||||
() => new React.MapState(),
|
||||
);
|
||||
const taskIds: string[] = this.calendarModel.listTaskIds(
|
||||
this.monthString,
|
||||
);
|
||||
const taskIds: string[] = this.calendarModel.listTaskIds(this.monthString);
|
||||
|
||||
for (const taskId of taskIds) {
|
||||
const taskFileContent: TaskFileContent | null =
|
||||
@@ -186,7 +193,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
public chatViewModel: ChatViewModel,
|
||||
storageModel: StorageModel,
|
||||
calendarModel: CalendarModel,
|
||||
boardsAndTasksModel: BoardsAndTasksModel
|
||||
boardsAndTasksModel: BoardsAndTasksModel,
|
||||
) {
|
||||
super(coreViewModel, chatViewModel, boardsAndTasksModel);
|
||||
|
||||
@@ -204,7 +211,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
boardsAndTasksModel.taskHandlerManager.addHandler(
|
||||
(taskFileContent: TaskFileContent) => {
|
||||
this.showTask(taskFileContent);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class MessagePageViewModel {
|
||||
};
|
||||
|
||||
decryptMessage = async (
|
||||
messageViewModel: ChatMessageViewModel
|
||||
messageViewModel: ChatMessageViewModel,
|
||||
): Promise<void> => {
|
||||
const chatMessage: ChatMessage = messageViewModel.chatMessage;
|
||||
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
|
||||
@@ -43,7 +43,8 @@ export default class MessagePageViewModel {
|
||||
this.coreViewModel,
|
||||
this,
|
||||
chatMessage,
|
||||
chatMessage.sender == this.chatViewModel.settingsViewModel.username.value
|
||||
chatMessage.sender ==
|
||||
this.chatViewModel.settingsViewModel.username.value,
|
||||
);
|
||||
|
||||
const existingChatMessageViewModel: ChatMessageViewModel | undefined =
|
||||
@@ -52,7 +53,10 @@ export default class MessagePageViewModel {
|
||||
existingChatMessageViewModel.body.value = chatMessage.body;
|
||||
existingChatMessageViewModel.status.value = chatMessage.status;
|
||||
} else {
|
||||
this.chatMessageViewModels.set(chatMessage.id, chatMessageViewModel);
|
||||
this.chatMessageViewModels.set(
|
||||
chatMessage.id,
|
||||
chatMessageViewModel,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,15 +71,18 @@ export default class MessagePageViewModel {
|
||||
// init
|
||||
constructor(
|
||||
public coreViewModel: CoreViewModel,
|
||||
chatViewModel: ChatViewModel
|
||||
chatViewModel: ChatViewModel,
|
||||
) {
|
||||
this.chatViewModel = chatViewModel;
|
||||
|
||||
this.cannotSendMessage = React.createProxyState(
|
||||
[this.chatViewModel.settingsViewModel.username, this.composingMessage],
|
||||
[
|
||||
this.chatViewModel.settingsViewModel.username,
|
||||
this.composingMessage,
|
||||
],
|
||||
() =>
|
||||
this.chatViewModel.settingsViewModel.username.value == "" ||
|
||||
this.composingMessage.value == ""
|
||||
this.composingMessage.value == "",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class SettingsPageViewModel {
|
||||
shouldShowEncryptionKey: React.State<boolean> = new React.State(false);
|
||||
encryptionKeyInputType: React.State<"text" | "password"> =
|
||||
React.createProxyState([this.shouldShowEncryptionKey], () =>
|
||||
this.shouldShowEncryptionKey.value == true ? "text" : "password"
|
||||
this.shouldShowEncryptionKey.value == true ? "text" : "password",
|
||||
);
|
||||
|
||||
color: React.State<Color> = new React.State<any>(Color.Standard);
|
||||
@@ -28,18 +28,18 @@ export default class SettingsPageViewModel {
|
||||
[this.primaryChannel, this.primaryChannelInput],
|
||||
() =>
|
||||
this.primaryChannelInput.value == "" ||
|
||||
this.primaryChannelInput.value == this.primaryChannel.value
|
||||
this.primaryChannelInput.value == this.primaryChannel.value,
|
||||
);
|
||||
cannotAddSecondaryChannel: React.State<boolean> = React.createProxyState(
|
||||
[this.newSecondaryChannelInput],
|
||||
() => this.newSecondaryChannelInput.value == ""
|
||||
() => this.newSecondaryChannelInput.value == "",
|
||||
);
|
||||
cannotSetEncryptionKey: React.State<boolean>;
|
||||
|
||||
// methods
|
||||
setPrimaryChannel = (): void => {
|
||||
this.chatViewModel.chatModel.setPrimaryChannel(
|
||||
this.primaryChannelInput.value
|
||||
this.primaryChannelInput.value,
|
||||
);
|
||||
this.primaryChannel.value =
|
||||
this.chatViewModel.chatModel.info.primaryChannel;
|
||||
@@ -67,7 +67,7 @@ export default class SettingsPageViewModel {
|
||||
|
||||
setEncryptionKey = (): void => {
|
||||
this.chatViewModel.chatModel.setEncryptionKey(
|
||||
this.encryptionKeyInput.value
|
||||
this.encryptionKeyInput.value,
|
||||
);
|
||||
|
||||
// disable button
|
||||
@@ -113,7 +113,7 @@ export default class SettingsPageViewModel {
|
||||
// init
|
||||
constructor(
|
||||
public coreViewModel: CoreViewModel,
|
||||
chatViewModel: ChatViewModel
|
||||
chatViewModel: ChatViewModel,
|
||||
) {
|
||||
this.chatViewModel = chatViewModel;
|
||||
this.loadListRelevantData();
|
||||
@@ -122,7 +122,7 @@ export default class SettingsPageViewModel {
|
||||
[this.encryptionKeyInput],
|
||||
() =>
|
||||
this.encryptionKeyInput.value ==
|
||||
this.chatViewModel.chatModel.info.encryptionKey
|
||||
this.chatViewModel.chatModel.info.encryptionKey,
|
||||
);
|
||||
|
||||
this.color.subscribe((newColor) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class TaskContainingPageViewModel {
|
||||
|
||||
// state
|
||||
taskIndexManager: IndexManager<TaskViewModel> = new IndexManager(
|
||||
(taskViewModel: TaskViewModel) => taskViewModel.sortingString
|
||||
(taskViewModel: TaskViewModel) => taskViewModel.sortingString,
|
||||
);
|
||||
|
||||
selectedTaskViewModel: React.State<TaskViewModel | undefined> =
|
||||
@@ -31,7 +31,7 @@ export default class TaskContainingPageViewModel {
|
||||
this.chatViewModel,
|
||||
this.boardsAndTasksModel,
|
||||
this,
|
||||
taskFileContent
|
||||
taskFileContent,
|
||||
);
|
||||
this.selectTask(taskViewModel);
|
||||
this.updateTaskIndices();
|
||||
@@ -60,7 +60,7 @@ export default class TaskContainingPageViewModel {
|
||||
constructor(
|
||||
public coreViewModel: CoreViewModel,
|
||||
public chatViewModel: ChatViewModel,
|
||||
boardsAndTasksModel: BoardsAndTasksModel
|
||||
boardsAndTasksModel: BoardsAndTasksModel,
|
||||
) {
|
||||
this.boardsAndTasksModel = boardsAndTasksModel;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class TaskPageViewModel {
|
||||
|
||||
// data
|
||||
boardIndexManager: IndexManager<BoardViewModel> = new IndexManager(
|
||||
(boardViewModel: BoardViewModel) => boardViewModel.name.value
|
||||
(boardViewModel: BoardViewModel) => boardViewModel.name.value,
|
||||
);
|
||||
|
||||
// paths
|
||||
@@ -40,13 +40,13 @@ export default class TaskPageViewModel {
|
||||
isShowingBoadList: React.State<boolean> = new React.State(true);
|
||||
|
||||
selectedBoardId: React.State<string | undefined> = new React.State<any>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
|
||||
// guards
|
||||
cannotCreateBoard: React.State<boolean> = React.createProxyState(
|
||||
[this.newBoardNameInput],
|
||||
() => this.newBoardNameInput.value == ""
|
||||
() => this.newBoardNameInput.value == "",
|
||||
);
|
||||
|
||||
// methods
|
||||
@@ -85,7 +85,7 @@ export default class TaskPageViewModel {
|
||||
this.storageModel,
|
||||
this.boardsAndTasksModel,
|
||||
this,
|
||||
boardInfo
|
||||
boardInfo,
|
||||
);
|
||||
this.boardViewModels.set(boardInfo.fileId, boardViewModel);
|
||||
|
||||
@@ -157,7 +157,7 @@ export default class TaskPageViewModel {
|
||||
public coreViewModel: CoreViewModel,
|
||||
public chatViewModel: ChatViewModel,
|
||||
storageModel: StorageModel,
|
||||
boardModel: BoardsAndTasksModel
|
||||
boardModel: BoardsAndTasksModel,
|
||||
) {
|
||||
this.storageModel = storageModel;
|
||||
this.boardsAndTasksModel = boardModel;
|
||||
@@ -169,7 +169,7 @@ export default class TaskPageViewModel {
|
||||
(boardInfoFileContent: BoardInfoFileContent) => {
|
||||
this.showBoardInList(boardInfoFileContent);
|
||||
this.updateBoardIndices();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,13 @@ export default class TaskViewModel {
|
||||
const invertedPriority = 100 - priorityNumber;
|
||||
|
||||
return (
|
||||
year + month + date + hour + minute + invertedPriority + this.name.value
|
||||
year +
|
||||
month +
|
||||
date +
|
||||
hour +
|
||||
minute +
|
||||
invertedPriority +
|
||||
this.name.value
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,7 +99,7 @@ export default class TaskViewModel {
|
||||
closeAndDiscard = (): void => {
|
||||
this.close();
|
||||
this.loadTaskData();
|
||||
}
|
||||
};
|
||||
|
||||
closeAndSave = (): void => {
|
||||
this.close();
|
||||
@@ -101,22 +107,24 @@ export default class TaskViewModel {
|
||||
};
|
||||
|
||||
updateIndex = (): void => {
|
||||
const index: number = this.containingModel.taskIndexManager.getIndex(this);
|
||||
const index: number =
|
||||
this.containingModel.taskIndexManager.getIndex(this);
|
||||
this.index.value = index;
|
||||
};
|
||||
|
||||
updateSuggestions = (): void => {
|
||||
if (
|
||||
this.coreViewModel.taskCategorySuggestions.value.has(
|
||||
this.category.value
|
||||
this.category.value,
|
||||
) == false
|
||||
) {
|
||||
this.coreViewModel.taskCategorySuggestions.add(this.category.value);
|
||||
}
|
||||
|
||||
if (
|
||||
this.coreViewModel.taskStatusSuggestions.value.has(this.status.value) ==
|
||||
false
|
||||
this.coreViewModel.taskStatusSuggestions.value.has(
|
||||
this.status.value,
|
||||
) == false
|
||||
) {
|
||||
this.coreViewModel.taskStatusSuggestions.add(this.status.value);
|
||||
}
|
||||
@@ -128,7 +136,7 @@ export default class TaskViewModel {
|
||||
BoardsAndTasksModel.createTaskFileContent(
|
||||
this.task.fileId,
|
||||
this.name.value,
|
||||
this.task.boardId
|
||||
this.task.boardId,
|
||||
);
|
||||
|
||||
newTaskFileContent.boardId = this.boardId.value;
|
||||
@@ -149,15 +157,17 @@ export default class TaskViewModel {
|
||||
|
||||
deleteTask = (): void => {
|
||||
this.close();
|
||||
this.boardsAndTasksModel.deleteTask(this.task.boardId, this.task.fileId);
|
||||
this.boardsAndTasksModel.deleteTask(
|
||||
this.task.boardId,
|
||||
this.task.fileId,
|
||||
);
|
||||
this.containingModel.removeTaskFromView(this.task);
|
||||
};
|
||||
|
||||
// load
|
||||
loadVersionIds = (): void => {
|
||||
const versionIds: string[] = this.boardsAndTasksModel.listTaskVersionIds(
|
||||
this.task.fileId
|
||||
);
|
||||
const versionIds: string[] =
|
||||
this.boardsAndTasksModel.listTaskVersionIds(this.task.fileId);
|
||||
const sortedVersionIds = versionIds.sort(localeCompare).reverse();
|
||||
this.versionIds.clear();
|
||||
this.versionIds.add(...sortedVersionIds);
|
||||
@@ -167,7 +177,7 @@ export default class TaskViewModel {
|
||||
const taskFileContent: TaskFileContent | null =
|
||||
this.boardsAndTasksModel.getSpecificTaskFileContent(
|
||||
this.task.fileId,
|
||||
versionId
|
||||
versionId,
|
||||
);
|
||||
if (taskFileContent == null) return;
|
||||
|
||||
@@ -204,7 +214,7 @@ export default class TaskViewModel {
|
||||
public chatViewModel: ChatViewModel,
|
||||
boardsAndTasksModel: BoardsAndTasksModel,
|
||||
containingModel: TaskContainingPageViewModel,
|
||||
taskFileContent: TaskFileContent
|
||||
taskFileContent: TaskFileContent,
|
||||
) {
|
||||
this.boardsAndTasksModel = boardsAndTasksModel;
|
||||
this.containingModel = containingModel;
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class SearchViewModel<T> {
|
||||
// guards
|
||||
cannotApplySearch: React.State<boolean> = React.createProxyState(
|
||||
[this.searchInput, this.appliedQuery],
|
||||
() => this.searchInput.value == this.appliedQuery.value
|
||||
() => this.searchInput.value == this.appliedQuery.value,
|
||||
);
|
||||
|
||||
// methods
|
||||
@@ -43,7 +43,7 @@ export default class SearchViewModel<T> {
|
||||
allObjects: React.ListState<T> | React.MapState<T>,
|
||||
matchingObjects: React.ListState<T>,
|
||||
getStringsOfObject: (object: T) => string[],
|
||||
suggestions: React.ListState<string>
|
||||
suggestions: React.ListState<string>,
|
||||
) {
|
||||
this.allObjects = allObjects;
|
||||
this.matchingObjects = matchingObjects;
|
||||
@@ -70,7 +70,7 @@ export default class SearchViewModel<T> {
|
||||
return checkDoesObjectMatchSearch(
|
||||
this.appliedQuery.value,
|
||||
this.getStringsOfObject,
|
||||
object
|
||||
object,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export default class TaskPropertyBulkChangeViewModel {
|
||||
constructor(
|
||||
taskViewModels: React.ListState<TaskViewModel>,
|
||||
valueSetter: (newValue: string, taskViewModel: TaskViewModel) => void,
|
||||
initialValue: string
|
||||
initialValue: string,
|
||||
) {
|
||||
this.taskViewModels = taskViewModels;
|
||||
this.inputValue.value = initialValue;
|
||||
@@ -34,7 +34,9 @@ export default class TaskPropertyBulkChangeViewModel {
|
||||
|
||||
this.cannotSet = React.createProxyState(
|
||||
[this.inputValue],
|
||||
() => this.inputValue.value == "" || this.inputValue.value == initialValue
|
||||
() =>
|
||||
this.inputValue.value == "" ||
|
||||
this.inputValue.value == initialValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +44,7 @@ export default class TaskPropertyBulkChangeViewModel {
|
||||
export class TaskCategoryBulkChangeViewModel extends TaskPropertyBulkChangeViewModel {
|
||||
constructor(
|
||||
taskViewModels: React.ListState<TaskViewModel>,
|
||||
initialValue: string
|
||||
initialValue: string,
|
||||
) {
|
||||
super(
|
||||
taskViewModels,
|
||||
@@ -50,7 +52,7 @@ export class TaskCategoryBulkChangeViewModel extends TaskPropertyBulkChangeViewM
|
||||
taskViewModel.category.value = newCategory;
|
||||
taskViewModel.save();
|
||||
},
|
||||
initialValue
|
||||
initialValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +60,7 @@ export class TaskCategoryBulkChangeViewModel extends TaskPropertyBulkChangeViewM
|
||||
export class TaskStatusBulkChangeViewModel extends TaskPropertyBulkChangeViewModel {
|
||||
constructor(
|
||||
taskViewModels: React.ListState<TaskViewModel>,
|
||||
initialValue: string
|
||||
initialValue: string,
|
||||
) {
|
||||
super(
|
||||
taskViewModels,
|
||||
@@ -66,7 +68,7 @@ export class TaskStatusBulkChangeViewModel extends TaskPropertyBulkChangeViewMod
|
||||
taskViewModel.status.value = newStatus;
|
||||
taskViewModel.save();
|
||||
},
|
||||
initialValue
|
||||
initialValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -29,7 +29,7 @@ const connectionModel = new ConnectionModel(storageModel);
|
||||
const chatListModel = new ChatListModel(
|
||||
storageModel,
|
||||
settingsModel,
|
||||
connectionModel
|
||||
connectionModel,
|
||||
);
|
||||
const fileTransferModel = new FileTransferModel(storageModel, connectionModel);
|
||||
|
||||
@@ -43,24 +43,24 @@ const storageViewModel = new StorageViewModel(coreVieWModel, storageModel);
|
||||
const settingsViewModel = new SettingsViewModel(coreVieWModel, settingsModel);
|
||||
const connectionViewModel = new ConnectionViewModel(
|
||||
coreVieWModel,
|
||||
connectionModel
|
||||
connectionModel,
|
||||
);
|
||||
const chatListViewModel = new ChatListViewModel(
|
||||
coreVieWModel,
|
||||
storageModel,
|
||||
chatListModel,
|
||||
settingsViewModel
|
||||
settingsViewModel,
|
||||
);
|
||||
const fileTransferViewModel = new FileTransferViewModel(
|
||||
fileTransferModel,
|
||||
chatListModel
|
||||
chatListModel,
|
||||
);
|
||||
|
||||
// view
|
||||
chatListViewModel.selectedChat.subscribe(() => {
|
||||
document.body.toggleAttribute(
|
||||
"showing-chat",
|
||||
chatListViewModel.selectedChat.value != undefined
|
||||
chatListViewModel.selectedChat.value != undefined,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ document.body.append(
|
||||
<div id="sky"></div>
|
||||
<div id="grass-1"></div>
|
||||
<div id="grass-2"></div>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
document
|
||||
.querySelector("main")!
|
||||
@@ -79,10 +79,10 @@ document
|
||||
settingsViewModel,
|
||||
connectionViewModel,
|
||||
fileTransferViewModel,
|
||||
chatListViewModel
|
||||
chatListViewModel,
|
||||
),
|
||||
ChatPageWrapper(chatListViewModel),
|
||||
ConnectionModal(connectionViewModel),
|
||||
DataTransferModalWrapper(connectionViewModel, fileTransferViewModel),
|
||||
StorageModal(storageViewModel)
|
||||
StorageModal(storageViewModel),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user