improve keystrokes, cleanup

This commit is contained in:
marlon-erler
2026-04-15 12:47:10 +00:00
parent ed8f523a89
commit 091c666ccc
31 changed files with 324 additions and 245 deletions
+1 -1
View File
@@ -597,7 +597,7 @@ body[showing-chat] #chat-page-wrapper {
padding-bottom: calc(var(--message-container-padding) + var(--content-padding)) !important; padding-bottom: calc(var(--message-container-padding) + var(--content-padding)) !important;
} }
#chat-page #main #message-page .content:has(.reply-preview-wrapper .reply-preview) #message-container { #chat-page #main #message-page .content:has(.reply-preview-wrapper .reply-preview) #message-container {
--message-container-padding: calc(2 * var(--control-height) + var(--gap-between-tiles)); --message-container-padding: calc( 2 * var(--control-height) + var(--gap-between-tiles) );
} }
#message-page #message-container { #message-page #message-container {
overflow: auto; overflow: auto;
+157 -118
View File
@@ -2378,26 +2378,84 @@
} }
}; };
// src/View/utility.ts // src/View/viewController.ts
function allowDrop(event) { var Tracker = class {
event.preventDefault(); };
} var TrackerMap = class {
function allowDrag(event) { constructor() {
event.dataTransfer?.setData("text", ""); this.trackers = /* @__PURE__ */ new Map();
} this.register = (key) => {
function reload() { if (this.trackers.has(key)) {
window.location.reload(); return this.trackers.get(key);
} }
function setFocus() { const tracker = new Tracker();
const focusedInModal = document.querySelector( this.trackers.set(key, tracker);
".modal[open] #focused" return tracker;
); };
if (focusedInModal) return focusedInModal.focus(); this.setState = (key, stateBuilder) => {
document.getElementById("focused")?.focus(); const tracker = this.register(key);
} if (tracker.state != void 0) {
function setFocusWithDelay() { return tracker.state;
setTimeout(setFocus, 100); } else {
} tracker.state = stateBuilder();
return tracker.state;
}
};
}
};
var ViewController = class {
static {
this.chatPages = new Tracker();
}
static {
this.taskPages = new TrackerMap();
}
static {
this.boardPages = new TrackerMap();
}
static {
this.inlineReplies = new TrackerMap();
}
static {
this.scrollToView = (id) => {
const element = document.getElementById(id);
if (!element) return;
element.scrollIntoView();
element.setAttribute("scrolled", "");
setTimeout(() => element.removeAttribute("scrolled"), 1e3);
};
}
static {
this.allowDrop = (event) => {
event.preventDefault();
};
}
static {
this.allowDrag = (event) => {
event.dataTransfer?.setData("text", "");
};
}
static {
this.reload = () => {
window.location.reload();
};
}
static {
this.setFocus = () => {
const focusedInModal = document.querySelector(
".modal[open] #focused"
);
if (focusedInModal) return focusedInModal.focus();
document.getElementById("focused")?.focus();
};
}
static {
this.setFocusWithDelay = () => {
console.trace("focus");
setTimeout(this.setFocus, 100);
};
}
};
// src/ViewModel/Pages/taskViewModel.ts // src/ViewModel/Pages/taskViewModel.ts
var TaskViewModel = class extends Context { var TaskViewModel = class extends Context {
@@ -2427,7 +2485,7 @@
this.versionIds = new ListState(); this.versionIds = new ListState();
// methods // methods
this.dragStart = (event) => { this.dragStart = (event) => {
allowDrag(event); ViewController.allowDrag(event);
this.coreViewModel.draggedObject.value = this; this.coreViewModel.draggedObject.value = this;
}; };
this.setCategoryAndStatus = (category, status) => { this.setCategoryAndStatus = (category, status) => {
@@ -2842,12 +2900,12 @@
this.messagePageViewModel.decryptMessage(this); this.messagePageViewModel.decryptMessage(this);
}; };
this.reply = () => { this.reply = () => {
this.messagePageViewModel.replyingMessage.value = this; this.messagePageViewModel.setReply(this);
this.hideInfoModal(); this.hideInfoModal();
}; };
this.cancelReply = () => { this.cancelReply = () => {
if (this.messagePageViewModel.replyingMessage.value != this) return; if (this.messagePageViewModel.replyingMessage.value != this) return;
this.messagePageViewModel.replyingMessage.value = void 0; this.messagePageViewModel.setReply(void 0);
}; };
// view // view
this.showInfoModal = () => { this.showInfoModal = () => {
@@ -2882,7 +2940,9 @@
this.body.value = this.chatMessage.body; this.body.value = this.chatMessage.body;
this.status.value = this.chatMessage.status; this.status.value = this.chatMessage.status;
if (this.chatMessage.inlineReplyId) { if (this.chatMessage.inlineReplyId) {
this.inlineReply = this.messagePageViewModel.chatMessageViewModels.value.get(this.chatMessage.inlineReplyId); this.inlineReply = this.messagePageViewModel.chatMessageViewModels.value.get(
this.chatMessage.inlineReplyId
);
} }
}; };
this.chatMessage = chatMessage; this.chatMessage = chatMessage;
@@ -2994,6 +3054,7 @@
void 0 void 0
); );
this.composingMessage = new State(""); this.composingMessage = new State("");
this.focusSetter = new State(null);
// methods // methods
this.sendMessage = () => { this.sendMessage = () => {
if (this.cannotSendMessage.value == true) return; if (this.cannotSendMessage.value == true) return;
@@ -3005,10 +3066,7 @@
if (this.replyingMessage.value) { if (this.replyingMessage.value) {
replyId = this.replyingMessage.value.chatMessage.id; replyId = this.replyingMessage.value.chatMessage.id;
} }
this.chatViewModel.chatModel.sendMessage( this.chatViewModel.chatModel.sendMessage(body, replyId);
body,
replyId
);
this.replyingMessage.value = void 0; this.replyingMessage.value = void 0;
}; };
this.decryptMessage = async (messageViewModel) => { this.decryptMessage = async (messageViewModel) => {
@@ -3024,6 +3082,14 @@
isDeleting isDeleting
); );
}; };
this.setReply = (chatMessageViewModel) => {
this.replyingMessage.value = chatMessageViewModel;
this.setFocus();
};
this.resetReply = () => {
this.replyingMessage.value = void 0;
this.setFocus();
};
// view // view
this.showChatMessage = (chatMessage) => { this.showChatMessage = (chatMessage) => {
const chatMessageViewModel = new ChatMessageViewModel( const chatMessageViewModel = new ChatMessageViewModel(
@@ -3064,6 +3130,9 @@
this.revokeReactionFilter(); this.revokeReactionFilter();
this.searchViewModel.search(""); this.searchViewModel.search("");
}; };
this.setFocus = () => {
this.focusSetter.callSubscriptions();
};
// load // load
this.loadData = () => { this.loadData = () => {
this.chatMessageViewModels.clear(); this.chatMessageViewModels.clear();
@@ -3092,8 +3161,15 @@
() => this.searchViewModel.appliedQuery.value != "" || this.reactionFilter.value != void 0 () => this.searchViewModel.appliedQuery.value != "" || this.reactionFilter.value != void 0
); );
this.registerKeyStroke("=" /* Filter */, this.showFilterModal); this.registerKeyStroke("=" /* Filter */, this.showFilterModal);
this.registerKeyStroke("backspace" /* CloseOrCancel */, this.hideFilterModal); this.registerKeyStroke("backspace" /* CloseOrCancel */, () => {
if (this.isFilterModalOpen.value == true) {
this.hideFilterModal();
} else {
this.replyingMessage.value = void 0;
}
});
this.registerKeyStroke("-" /* Reset */, this.resetFilter); this.registerKeyStroke("-" /* Reset */, this.resetFilter);
this.registerKeyStroke(";" /* Create */, this.setFocus);
this.chatViewModel.registerContext("messages" /* Messages */, this); this.chatViewModel.registerContext("messages" /* Messages */, this);
} }
}; };
@@ -3526,7 +3602,8 @@
this.closeCurrentContext(); this.closeCurrentContext();
}; };
this.handleBoardClosed = (boardViewModel) => { this.handleBoardClosed = (boardViewModel) => {
if (boardViewModel.boardInfo.fileId != this.selectedBoardId.value) return; if (boardViewModel.boardInfo.fileId != this.selectedBoardId.value)
return;
this.selectedBoardId.value = void 0; this.selectedBoardId.value = void 0;
this.chatViewModel.resetColor(); this.chatViewModel.resetColor();
}; };
@@ -3945,7 +4022,7 @@
"on:click": select, "on:click": select,
"toggle:selected": isSelected, "toggle:selected": isSelected,
"toggle:today": isToday, "toggle:today": isToday,
"on:dragover": allowDrop, "on:dragover": ViewController.allowDrop,
"on:drop": drop "on:drop": drop
}, },
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, date), /* @__PURE__ */ createElement( /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, date), /* @__PURE__ */ createElement(
@@ -4255,7 +4332,7 @@
if (calendarPageViewModel.selectedTaskViewModel.value == void 0) { if (calendarPageViewModel.selectedTaskViewModel.value == void 0) {
return /* @__PURE__ */ createElement("div", null); return /* @__PURE__ */ createElement("div", null);
} else { } else {
setFocusWithDelay(); ViewController.setFocusWithDelay();
return TaskSettingsModal( return TaskSettingsModal(
coreViewModel2, coreViewModel2,
calendarPageViewModel.selectedTaskViewModel.value calendarPageViewModel.selectedTaskViewModel.value
@@ -4510,56 +4587,6 @@
); );
} }
// src/View/viewController.ts
var ItemTracker = class {
};
var ViewTrackerMap = class {
constructor() {
this.trackers = /* @__PURE__ */ new Map();
this.register = (key) => {
if (this.trackers.has(key)) {
return this.trackers.get(key);
}
const tracker = new ItemTracker();
this.trackers.set(key, tracker);
return tracker;
};
this.setItems = (key, views) => {
const tracker = this.register(key);
if (tracker.items != void 0) {
return tracker.items;
} else {
tracker.items = views();
return tracker.items;
}
};
}
};
var ViewController = class {
static {
this.chatPages = new ItemTracker();
}
static {
this.taskPages = new ViewTrackerMap();
}
static {
this.boardPages = new ViewTrackerMap();
}
static {
this.inlineReplies = new ViewTrackerMap();
}
static {
this.messageIcons = new ViewTrackerMap();
}
static scrollToView(id) {
const element = document.getElementById(id);
if (!element) return;
element.scrollIntoView();
element.setAttribute("scrolled", "");
setTimeout(() => element.removeAttribute("scrolled"), 1e3);
}
};
// src/View/Components/inlineReply.tsx // src/View/Components/inlineReply.tsx
function InlineReply(chatMessageViewModel) { function InlineReply(chatMessageViewModel) {
const reply = chatMessageViewModel.inlineReply; const reply = chatMessageViewModel.inlineReply;
@@ -4572,22 +4599,18 @@
// src/View/Components/chatMessage.tsx // src/View/Components/chatMessage.tsx
function ChatMessage4(coreViewModel2, chatMessageViewModel) { function ChatMessage4(coreViewModel2, chatMessageViewModel) {
const persistenceId = chatMessageViewModel.chatMessage.id; const statusIcon = createProxyState([chatMessageViewModel.status], () => {
const statusIcon = ViewController.messageIcons.setItems(persistenceId, () => createProxyState( switch (chatMessageViewModel.status.value) {
[chatMessageViewModel.status], case "outbox" /* Outbox */:
() => { return "hourglass_top";
switch (chatMessageViewModel.status.value) { case "sent" /* Sent */:
case "outbox" /* Outbox */: return "check";
return "hourglass_top"; case "received" /* Received */:
case "sent" /* Sent */: return "done_all";
return "check"; default:
case "received" /* Received */: return "warning";
return "done_all";
default:
return "warning";
}
} }
)); });
return /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement(
"div", "div",
{ {
@@ -4656,7 +4679,18 @@
[messagePageViewModel.reactionFilter], [messagePageViewModel.reactionFilter],
() => messagePageViewModel.reactionFilter.value == void 0 () => messagePageViewModel.reactionFilter.value == void 0
); );
messagePageViewModel.isFilterModalOpen.subscribe(setFocusWithDelay); messagePageViewModel.isFilterModalOpen.subscribe((isOpen) => {
if (isOpen == false) return;
ViewController.setFocusWithDelay();
});
createProxyState(
[messagePageViewModel.isFilterModalOpen],
() => {
if (messagePageViewModel.isFilterModalOpen.value == false)
return;
ViewController.setFocusWithDelay();
}
);
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": messagePageViewModel.isFilterModalOpen }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.chatPage.message.messageFilterHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": messagePageViewModel.isFilterModalOpen }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.chatPage.message.messageFilterHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input", "input",
{ {
@@ -4704,7 +4738,13 @@
// src/View/Components/replyPreview.tsx // src/View/Components/replyPreview.tsx
function ReplyPreview(coreViewModel2, chatMessageViewModel) { function ReplyPreview(coreViewModel2, chatMessageViewModel) {
return /* @__PURE__ */ createElement("div", { class: "reply-preview" }, /* @__PURE__ */ createElement("div", { class: "surface blur" }, /* @__PURE__ */ createElement("span", { class: "secondary" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement("b", { class: "ellipsis", "subscribe:innerText": chatMessageViewModel.body })), /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { class: "reply-preview" }, /* @__PURE__ */ createElement("div", { class: "surface blur" }, /* @__PURE__ */ createElement("span", { class: "secondary" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement(
"b",
{
class: "ellipsis",
"subscribe:innerText": chatMessageViewModel.body
}
)), /* @__PURE__ */ createElement(
"button", "button",
{ {
class: "standard square blur", class: "standard square blur",
@@ -4732,7 +4772,7 @@
} }
); );
const persistenceId = messagePageViewModel.chatViewModel.chatModel.id; const persistenceId = messagePageViewModel.chatViewModel.chatModel.id;
const replyPreview = ViewController.inlineReplies.setItems( const replyPreview = ViewController.inlineReplies.setState(
persistenceId, persistenceId,
() => createProxyState( () => createProxyState(
[messagePageViewModel.replyingMessage], [messagePageViewModel.replyingMessage],
@@ -4762,10 +4802,9 @@
scrollDownIfApplicable scrollDownIfApplicable
); );
setTimeout(() => scrollDown(true), 100); setTimeout(() => scrollDown(true), 100);
messagePageViewModel.replyingMessage.subscribeSilent(() => { messagePageViewModel.focusSetter.subscribeSilent(() => {
setFocusWithDelay(); ViewController.setFocusWithDelay();
}); });
setFocusWithDelay();
return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, coreViewModel2.translations.chatPage.message.messagesHeadline), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, coreViewModel2.translations.chatPage.message.messagesHeadline), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
"button", "button",
{ {
@@ -5081,7 +5120,7 @@
"div", "div",
{ {
class: "flex-column flex-no", class: "flex-column flex-no",
"on:dragover": allowDrop, "on:dragover": ViewController.allowDrop,
"on:drop": drop "on:drop": drop
}, },
/* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement( /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
@@ -5119,7 +5158,7 @@
// src/View/Modals/boardSettingsModal.tsx // src/View/Modals/boardSettingsModal.tsx
function BoardSettingsModal(coreViewModel2, boardViewModel) { function BoardSettingsModal(coreViewModel2, boardViewModel) {
boardViewModel.isPresentingSettingsModal.subscribe(setFocusWithDelay); boardViewModel.isPresentingSettingsModal.subscribe(ViewController.setFocusWithDelay);
return /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement(
"div", "div",
{ {
@@ -5302,7 +5341,7 @@
"div", "div",
{ {
class: "status-column gap", class: "status-column gap",
"on:dragover": allowDrop, "on:dragover": ViewController.allowDrop,
"on:drop": drop, "on:drop": drop,
"children:append": [taskViewModels, TaskViewModelToEntry] "children:append": [taskViewModels, TaskViewModelToEntry]
} }
@@ -5331,7 +5370,10 @@
isOpen.value = false; isOpen.value = false;
} }
const suggestionId = v4_default(); const suggestionId = v4_default();
isOpen.subscribe(setFocusWithDelay); isOpen.subscribe((isOpen2) => {
if (!isOpen2) return;
ViewController.setFocusWithDelay();
});
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen, extended: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen, extended: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input", "input",
{ {
@@ -5377,7 +5419,7 @@
function BoardPage(coreViewModel2, boardViewModel) { function BoardPage(coreViewModel2, boardViewModel) {
boardViewModel.loadData(); boardViewModel.loadData();
const persistenceId = boardViewModel.boardInfo.fileId; const persistenceId = boardViewModel.boardInfo.fileId;
const pages = ViewController.boardPages.setItems( const pages = ViewController.boardPages.setState(
persistenceId, persistenceId,
() => createProxyState([boardViewModel.selectedPage], () => { () => createProxyState([boardViewModel.selectedPage], () => {
switch (boardViewModel.selectedPage.value) { switch (boardViewModel.selectedPage.value) {
@@ -5408,7 +5450,7 @@
if (boardViewModel.selectedTaskViewModel.value == void 0) { if (boardViewModel.selectedTaskViewModel.value == void 0) {
return /* @__PURE__ */ createElement("div", null); return /* @__PURE__ */ createElement("div", null);
} else { } else {
setFocusWithDelay(); ViewController.setFocusWithDelay();
return TaskSettingsModal( return TaskSettingsModal(
coreViewModel2, coreViewModel2,
boardViewModel.selectedTaskViewModel.value boardViewModel.selectedTaskViewModel.value
@@ -5491,7 +5533,7 @@
class: "tile colored-tile", class: "tile colored-tile",
"toggle:selected": boardViewModel.isSelected, "toggle:selected": boardViewModel.isSelected,
"on:click": boardViewModel.select, "on:click": boardViewModel.select,
"on:dragover": allowDrop, "on:dragover": ViewController.allowDrop,
"on:drop": boardViewModel.handleDropBetweenBoards "on:drop": boardViewModel.handleDropBetweenBoards
}, },
/* @__PURE__ */ createElement( /* @__PURE__ */ createElement(
@@ -5520,7 +5562,7 @@
() => taskPageViewModel.selectedBoardId.value != void 0 () => taskPageViewModel.selectedBoardId.value != void 0
); );
const persistenceId = taskPageViewModel.chatViewModel.chatModel.id; const persistenceId = taskPageViewModel.chatViewModel.chatModel.id;
const pages = ViewController.taskPages.setItems( const pages = ViewController.taskPages.setState(
persistenceId, persistenceId,
() => createProxyState([taskPageViewModel.selectedBoardId], () => { () => createProxyState([taskPageViewModel.selectedBoardId], () => {
const selectedBoardId = taskPageViewModel.selectedBoardId.value; const selectedBoardId = taskPageViewModel.selectedBoardId.value;
@@ -5592,7 +5634,7 @@
// src/View/chatPage.tsx // src/View/chatPage.tsx
function ChatPage(coreViewModel2, chatViewModel) { function ChatPage(coreViewModel2, chatViewModel) {
ViewController.chatPages.items = createProxyState( ViewController.chatPages.state = createProxyState(
[chatViewModel.selectedPage], [chatViewModel.selectedPage],
() => { () => {
switch (chatViewModel.selectedPage.value) { switch (chatViewModel.selectedPage.value) {
@@ -5687,7 +5729,7 @@
"div", "div",
{ {
id: "main", id: "main",
"children:set": ViewController.chatPages.items "children:set": ViewController.chatPages.state
} }
)) ))
); );
@@ -6541,7 +6583,7 @@
StringToTextSpan StringToTextSpan
] ]
} }
)), /* @__PURE__ */ createElement("button", { "on:click": reload }, coreViewModel2.translations.general.reloadAppButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "refresh")))); )), /* @__PURE__ */ createElement("button", { "on:click": ViewController.reload }, coreViewModel2.translations.general.reloadAppButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "refresh"))));
} }
// src/Model/Global/fileTransferModel.ts // src/Model/Global/fileTransferModel.ts
@@ -6954,10 +6996,7 @@
"change", "change",
() => this.applyTheme() () => this.applyTheme()
); );
this.registerKeyStroke( this.registerKeyStroke("backspace" /* CloseOrCancel */, this.close);
"backspace" /* CloseOrCancel */,
this.close
);
} }
static generateThemeMedia() { static generateThemeMedia() {
return window.matchMedia("(prefers-color-scheme: dark)"); return window.matchMedia("(prefers-color-scheme: dark)");
+2 -2
View File
@@ -10,8 +10,8 @@ import { FilteredList } from "../Components/filteredList";
import { TaskCategoryBulkChangeViewModel } from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel"; import { TaskCategoryBulkChangeViewModel } from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel";
import TaskViewModel from "../../ViewModel/Pages/taskViewModel"; import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
import { TaskViewModelToEntry } from "../Components/taskEntry"; import { TaskViewModelToEntry } from "../Components/taskEntry";
import { allowDrop } from "../utility";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { ViewController } from "../viewController";
export function BoardKanbanPage( export function BoardKanbanPage(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -75,7 +75,7 @@ function Column(
const view = ( const view = (
<div <div
class="flex-column flex-no" class="flex-column flex-no"
on:dragover={allowDrop} on:dragover={ViewController.allowDrop}
on:drop={drop} on:drop={drop}
> >
<div class="flex-row width-input"> <div class="flex-row width-input">
+2 -3
View File
@@ -11,7 +11,6 @@ import { BoardViewToggleButton } from "../Components/boardViewToggleButton";
import { SearchModal } from "../Modals/searchModal"; import { SearchModal } from "../Modals/searchModal";
import { TaskSettingsModal } from "../Modals/taskSettingsModal"; import { TaskSettingsModal } from "../Modals/taskSettingsModal";
import { TaskViewModelToEntry } from "../Components/taskEntry"; import { TaskViewModelToEntry } from "../Components/taskEntry";
import { setFocusWithDelay } from "../utility";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { ViewController } from "../viewController"; import { ViewController } from "../viewController";
@@ -23,7 +22,7 @@ export function BoardPage(
// main content // main content
const persistenceId = boardViewModel.boardInfo.fileId; const persistenceId = boardViewModel.boardInfo.fileId;
const pages = ViewController.boardPages.setItems(persistenceId, () => const pages = ViewController.boardPages.setState(persistenceId, () =>
React.createProxyState([boardViewModel.selectedPage], () => { React.createProxyState([boardViewModel.selectedPage], () => {
switch (boardViewModel.selectedPage.value) { switch (boardViewModel.selectedPage.value) {
case BoardPageTypes.Kanban: { case BoardPageTypes.Kanban: {
@@ -54,7 +53,7 @@ export function BoardPage(
if (boardViewModel.selectedTaskViewModel.value == undefined) { if (boardViewModel.selectedTaskViewModel.value == undefined) {
return <div></div>; return <div></div>;
} else { } else {
setFocusWithDelay(); ViewController.setFocusWithDelay();
return TaskSettingsModal( return TaskSettingsModal(
coreViewModel, coreViewModel,
+2 -2
View File
@@ -15,8 +15,8 @@ import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
import { FilteredList } from "../Components/filteredList"; import { FilteredList } from "../Components/filteredList";
import TaskViewModel from "../../ViewModel/Pages/taskViewModel"; import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
import { TaskViewModelToEntry } from "../Components/taskEntry"; import { TaskViewModelToEntry } from "../Components/taskEntry";
import { allowDrop } from "../utility";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { ViewController } from "../viewController";
export function BoardStatusGridPage( export function BoardStatusGridPage(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -246,7 +246,7 @@ function StatusColumn(
const view = ( const view = (
<div <div
class="status-column gap" class="status-column gap"
on:dragover={allowDrop} on:dragover={ViewController.allowDrop}
on:drop={drop} on:drop={drop}
children:append={[taskViewModels, TaskViewModelToEntry]} children:append={[taskViewModels, TaskViewModelToEntry]}
></div> ></div>
+2 -3
View File
@@ -2,14 +2,13 @@ import "./calendarPage.css";
import * as React from "bloatless-react"; import * as React from "bloatless-react";
import CalendarModel from "../../Model/Files/calendarModel";
import CalendarPageViewModel from "../../ViewModel/Pages/calendarPageViewModel"; import CalendarPageViewModel from "../../ViewModel/Pages/calendarPageViewModel";
import { MonthGrid } from "../Components/monthGrid"; import { MonthGrid } from "../Components/monthGrid";
import { TaskSettingsModal } from "../Modals/taskSettingsModal"; import { TaskSettingsModal } from "../Modals/taskSettingsModal";
import { TaskViewModelToEntry } from "../Components/taskEntry"; import { TaskViewModelToEntry } from "../Components/taskEntry";
import { setFocusWithDelay } from "../utility";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { PlaceholderView } from "../Components/placeholderView"; import { PlaceholderView } from "../Components/placeholderView";
import { ViewController } from "../viewController";
export function CalendarPage( export function CalendarPage(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -95,7 +94,7 @@ export function CalendarPage(
) { ) {
return <div></div>; return <div></div>;
} else { } else {
setFocusWithDelay(); ViewController.setFocusWithDelay();
return TaskSettingsModal( return TaskSettingsModal(
coreViewModel, coreViewModel,
+17 -11
View File
@@ -9,11 +9,19 @@
#chat-page #main #message-page .content #message-container { #chat-page #main #message-page .content #message-container {
--message-container-padding: var(--control-height); --message-container-padding: var(--control-height);
padding-bottom: calc(var(--message-container-padding) + var(--content-padding)) !important; padding-bottom: calc(
var(--message-container-padding) + var(--content-padding)
) !important;
} }
#chat-page #main #message-page .content:has(.reply-preview-wrapper .reply-preview) #message-container { #chat-page
--message-container-padding: calc(2 * var(--control-height) + var(--gap-between-tiles)); #main
#message-page
.content:has(.reply-preview-wrapper .reply-preview)
#message-container {
--message-container-padding: calc(
2 * var(--control-height) + var(--gap-between-tiles)
);
} }
#message-page #message-container { #message-page #message-container {
@@ -62,7 +70,7 @@
gap: var(--gap-between-tiles); gap: var(--gap-between-tiles);
} }
#message-page #composer .reply-preview-wrapper .reply-preview>div { #message-page #composer .reply-preview-wrapper .reply-preview > div {
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--text-gap); gap: var(--text-gap);
@@ -81,7 +89,6 @@
} }
@media (min-width: 700px) { @media (min-width: 700px) {
#message-page #composer .content-width-constraint, #message-page #composer .content-width-constraint,
#message-page #composer .input-width-constraint { #message-page #composer .input-width-constraint {
display: flex; display: flex;
@@ -227,7 +234,7 @@
text-align: right; text-align: right;
} }
.message-bubble>.inline-reply { .message-bubble > .inline-reply {
cursor: pointer; cursor: pointer;
display: flex; display: flex;
@@ -246,7 +253,7 @@
border-left: var(--outline-width) solid var(--primary); border-left: var(--outline-width) solid var(--primary);
} }
.message-bubble>.message-reaction-row { .message-bubble > .message-reaction-row {
max-width: var(--max-input-width); max-width: var(--max-input-width);
width: 100%; width: 100%;
@@ -257,11 +264,11 @@
overflow: hidden; overflow: hidden;
} }
.message-bubble>.message-reaction-row:has(button:not([count="0"])) { .message-bubble > .message-reaction-row:has(button:not([count="0"])) {
display: grid !important; display: grid !important;
} }
.message-bubble>.message-reaction-row button { .message-bubble > .message-reaction-row button {
min-height: unset; min-height: unset;
height: 100%; height: 100%;
width: 100%; width: 100%;
@@ -271,7 +278,7 @@
animation: slide-up var(--animation-duration); animation: slide-up var(--animation-duration);
} }
.message-bubble>.message-reaction-row button[count="0"] { .message-bubble > .message-reaction-row button[count="0"] {
display: none; display: none;
} }
@@ -285,7 +292,6 @@
} }
} }
.grid.message-reaction-filter { .grid.message-reaction-filter {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
} }
+4 -6
View File
@@ -7,7 +7,6 @@ import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel"; import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
import { ChatMessage } from "../Components/chatMessage"; import { ChatMessage } from "../Components/chatMessage";
import { MessageFilterModal } from "../Modals/messageFilterModal"; import { MessageFilterModal } from "../Modals/messageFilterModal";
import { setFocusWithDelay } from "../utility";
import { ViewController } from "../viewController"; import { ViewController } from "../viewController";
import { ReplyPreview } from "../Components/replyPreview"; import { ReplyPreview } from "../Components/replyPreview";
@@ -34,7 +33,7 @@ export function MessagePage(
); );
const persistenceId = messagePageViewModel.chatViewModel.chatModel.id; const persistenceId = messagePageViewModel.chatViewModel.chatModel.id;
const replyPreview = ViewController.inlineReplies.setItems( const replyPreview = ViewController.inlineReplies.setState(
persistenceId, persistenceId,
() => () =>
React.createProxyState( React.createProxyState(
@@ -70,10 +69,9 @@ export function MessagePage(
); );
setTimeout(() => scrollDown(true), 100); setTimeout(() => scrollDown(true), 100);
messagePageViewModel.replyingMessage.subscribeSilent(() => { messagePageViewModel.focusSetter.subscribeSilent(() => {
setFocusWithDelay(); ViewController.setFocusWithDelay();
}) });
setFocusWithDelay();
return ( return (
<div id="message-page"> <div id="message-page">
+1 -1
View File
@@ -22,7 +22,7 @@ export function TaskPage(
); );
const persistenceId = taskPageViewModel.chatViewModel.chatModel.id; const persistenceId = taskPageViewModel.chatViewModel.chatModel.id;
const pages = ViewController.taskPages.setItems(persistenceId, () => const pages = ViewController.taskPages.setState(persistenceId, () =>
React.createProxyState([taskPageViewModel.selectedBoardId], () => { React.createProxyState([taskPageViewModel.selectedBoardId], () => {
const selectedBoardId = taskPageViewModel.selectedBoardId.value; const selectedBoardId = taskPageViewModel.selectedBoardId.value;
if (selectedBoardId == undefined) { if (selectedBoardId == undefined) {
+2 -2
View File
@@ -1,7 +1,7 @@
import * as React from "bloatless-react"; import * as React from "bloatless-react";
import BoardViewModel from "../../ViewModel/Pages/boardViewModel"; import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
import { allowDrop } from "../utility"; import { ViewController } from "../viewController";
export function BoardEntry(boardViewModel: BoardViewModel) { export function BoardEntry(boardViewModel: BoardViewModel) {
const view = ( const view = (
@@ -10,7 +10,7 @@ export function BoardEntry(boardViewModel: BoardViewModel) {
class="tile colored-tile" class="tile colored-tile"
toggle:selected={boardViewModel.isSelected} toggle:selected={boardViewModel.isSelected}
on:click={boardViewModel.select} on:click={boardViewModel.select}
on:dragover={allowDrop} on:dragover={ViewController.allowDrop}
on:drop={boardViewModel.handleDropBetweenBoards} on:drop={boardViewModel.handleDropBetweenBoards}
> >
<span <span
+3 -4
View File
@@ -12,8 +12,7 @@ export function ChatMessage(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
chatMessageViewModel: ChatMessageViewModel, chatMessageViewModel: ChatMessageViewModel,
) { ) {
const persistenceId = chatMessageViewModel.chatMessage.id; const statusIcon = React.createProxyState(
const statusIcon = ViewController.messageIcons.setItems(persistenceId, () => React.createProxyState(
[chatMessageViewModel.status], [chatMessageViewModel.status],
() => { () => {
switch (chatMessageViewModel.status.value) { switch (chatMessageViewModel.status.value) {
@@ -26,8 +25,8 @@ export function ChatMessage(
default: default:
return "warning"; return "warning";
} }
} },
)); );
return ( return (
<div <div
+3 -5
View File
@@ -4,15 +4,13 @@ import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { ViewController } from "../viewController"; import { ViewController } from "../viewController";
export function InlineReply ( export function InlineReply(chatMessageViewModel: ChatMessageViewModel) {
chatMessageViewModel: ChatMessageViewModel,
) {
const reply = chatMessageViewModel.inlineReply; const reply = chatMessageViewModel.inlineReply;
if (reply == undefined) return <div></div> if (reply == undefined) return <div></div>;
const scroll = () => { const scroll = () => {
ViewController.scrollToView(reply.chatMessage.id); ViewController.scrollToView(reply.chatMessage.id);
} };
return ( return (
<div class="inline-reply" on:click={scroll}> <div class="inline-reply" on:click={scroll}>
+2 -2
View File
@@ -3,9 +3,9 @@ import "./monthGrid.css";
import * as React from "bloatless-react"; import * as React from "bloatless-react";
import { MonthGrid } from "../../Model/Files/calendarModel"; import { MonthGrid } from "../../Model/Files/calendarModel";
import TaskViewModel from "../../ViewModel/Pages/taskViewModel"; import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
import { allowDrop } from "../utility";
import { localeCompare } from "../../Model/Utility/utility"; import { localeCompare } from "../../Model/Utility/utility";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { ViewController } from "../viewController";
export function MonthGrid<T>( export function MonthGrid<T>(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -93,7 +93,7 @@ export function MonthGrid<T>(
on:click={select} on:click={select}
toggle:selected={isSelected} toggle:selected={isSelected}
toggle:today={isToday} toggle:today={isToday}
on:dragover={allowDrop} on:dragover={ViewController.allowDrop}
on:drop={drop} on:drop={drop}
> >
<div> <div>
+8 -2
View File
@@ -11,12 +11,18 @@ export function ReplyPreview(
<div class="reply-preview"> <div class="reply-preview">
<div class="surface blur"> <div class="surface blur">
<span class="secondary">{chatMessageViewModel.sender}</span> <span class="secondary">{chatMessageViewModel.sender}</span>
<b class="ellipsis" subscribe:innerText={chatMessageViewModel.body}></b> <b
class="ellipsis"
subscribe:innerText={chatMessageViewModel.body}
></b>
</div> </div>
<button <button
class="standard square blur" class="standard square blur"
on:click={chatMessageViewModel.cancelReply} on:click={chatMessageViewModel.cancelReply}
aria-label={coreViewModel.translations.chatPage.message.cancelReplyAudioLabel} aria-label={
coreViewModel.translations.chatPage.message
.cancelReplyAudioLabel
}
> >
<span class="icon">close</span> <span class="icon">close</span>
</button> </button>
+4 -2
View File
@@ -4,13 +4,15 @@ import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
import { ColorPicker } from "../Components/colorPicker"; import { ColorPicker } from "../Components/colorPicker";
import { DangerousActionButton } from "../Components/dangerousActionButton"; import { DangerousActionButton } from "../Components/dangerousActionButton";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { setFocusWithDelay } from "../utility"; import { ViewController } from "../viewController";
export function BoardSettingsModal( export function BoardSettingsModal(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
boardViewModel: BoardViewModel, boardViewModel: BoardViewModel,
) { ) {
boardViewModel.isPresentingSettingsModal.subscribe(setFocusWithDelay); boardViewModel.isPresentingSettingsModal.subscribe(
ViewController.setFocusWithDelay,
);
return ( return (
<div <div
+2 -2
View File
@@ -8,8 +8,8 @@ import FileTransferViewModel, {
import ConnectionViewModel from "../../ViewModel/Global/connectionViewModel"; import ConnectionViewModel from "../../ViewModel/Global/connectionViewModel";
import StorageModel from "../../Model/Global/storageModel"; import StorageModel from "../../Model/Global/storageModel";
import { StringToTextSpan } from "../Components/textSpan"; import { StringToTextSpan } from "../Components/textSpan";
import { reload } from "../utility";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { ViewController } from "../viewController";
export function DataTransferModalWrapper( export function DataTransferModalWrapper(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -545,7 +545,7 @@ function DataReceptionModal(
]} ]}
></div> ></div>
</main> </main>
<button on:click={reload}> <button on:click={ViewController.reload}>
{coreViewModel.translations.general.reloadAppButton} {coreViewModel.translations.general.reloadAppButton}
<span class="icon">refresh</span> <span class="icon">refresh</span>
</button> </button>
+9 -2
View File
@@ -4,7 +4,7 @@ import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel"; import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton"; import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton";
import { ReactionSymbols } from "../../Model/Chat/chatModel"; import { ReactionSymbols } from "../../Model/Chat/chatModel";
import { setFocusWithDelay } from "../utility"; import { ViewController } from "../viewController";
export function MessageFilterModal<T>( export function MessageFilterModal<T>(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -16,7 +16,14 @@ export function MessageFilterModal<T>(
() => messagePageViewModel.reactionFilter.value == undefined, () => messagePageViewModel.reactionFilter.value == undefined,
); );
messagePageViewModel.isFilterModalOpen.subscribe(setFocusWithDelay); messagePageViewModel.isFilterModalOpen.subscribe((isOpen) => {
if (isOpen == false) return;
ViewController.setFocusWithDelay();
});
React.createProxyState([messagePageViewModel.isFilterModalOpen], () => {
if (messagePageViewModel.isFilterModalOpen.value == false) return;
ViewController.setFocusWithDelay();
});
return ( return (
<div class="modal" toggle:open={messagePageViewModel.isFilterModalOpen}> <div class="modal" toggle:open={messagePageViewModel.isFilterModalOpen}>
+5 -2
View File
@@ -4,7 +4,7 @@ import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
import { StringToOption } from "../Components/option"; import { StringToOption } from "../Components/option";
import { v4 } from "uuid"; import { v4 } from "uuid";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { setFocusWithDelay } from "../utility"; import { ViewController } from "../viewController";
export function SearchModal<T>( export function SearchModal<T>(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -19,7 +19,10 @@ export function SearchModal<T>(
const suggestionId = v4(); const suggestionId = v4();
isOpen.subscribe(setFocusWithDelay); isOpen.subscribe((isOpen) => {
if (!isOpen) return;
ViewController.setFocusWithDelay();
});
return ( return (
<div class="modal" toggle:open={isOpen} extended> <div class="modal" toggle:open={isOpen} extended>
+2 -2
View File
@@ -16,7 +16,7 @@ export function ChatPage(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
chatViewModel: ChatViewModel, chatViewModel: ChatViewModel,
) { ) {
ViewController.chatPages.items = React.createProxyState( ViewController.chatPages.state = React.createProxyState(
[chatViewModel.selectedPage], [chatViewModel.selectedPage],
() => { () => {
switch (chatViewModel.selectedPage.value) { switch (chatViewModel.selectedPage.value) {
@@ -132,7 +132,7 @@ export function ChatPage(
</div> </div>
<div <div
id="main" id="main"
children:set={ViewController.chatPages.items} children:set={ViewController.chatPages.state}
></div> ></div>
</div> </div>
</article> </article>
-23
View File
@@ -1,23 +0,0 @@
export function allowDrop(event: DragEvent) {
event.preventDefault();
}
export function allowDrag(event: DragEvent) {
event.dataTransfer?.setData("text", "");
}
export function reload() {
window.location.reload();
}
export function setFocus() {
const focusedInModal = document.querySelector(
".modal[open] #focused",
) as HTMLElement;
if (focusedInModal) return focusedInModal.focus();
document.getElementById("focused")?.focus();
}
export function setFocusWithDelay() {
setTimeout(setFocus, 100);
}
+46 -22
View File
@@ -1,48 +1,72 @@
import * as React from "bloatless-react"; import * as React from "bloatless-react";
class ItemTracker<T> { class Tracker<T> {
items: React.State<T | undefined> | undefined; state: React.State<T> | undefined;
} }
class ViewTrackerMap<T> { class TrackerMap<T> {
trackers = new Map<string, ItemTracker<T>>(); trackers = new Map<string, Tracker<T>>();
private register = (key: string): ItemTracker<T> => { private register = (key: string): Tracker<T> => {
if (this.trackers.has(key)) { if (this.trackers.has(key)) {
return this.trackers.get(key); return this.trackers.get(key);
} }
const tracker = new ItemTracker<T>(); const tracker = new Tracker<T>();
this.trackers.set(key, tracker); this.trackers.set(key, tracker);
return tracker; return tracker;
}; };
setItems = ( setState = (
key: string, key: string,
views: () => React.State<T>, stateBuilder: () => React.State<T>,
): React.State<T | undefined> | undefined => { ): React.State<T | undefined> | undefined => {
const tracker: ItemTracker<T> = this.register(key); const tracker: Tracker<T> = this.register(key);
if (tracker.items != undefined) { if (tracker.state != undefined) {
return tracker.items; return tracker.state;
} else { } else {
tracker.items = views(); tracker.state = stateBuilder();
return tracker.items; return tracker.state;
} }
}; };
} }
export class ViewController { export class ViewController {
static readonly chatPages = new ItemTracker<HTMLElement>(); static readonly chatPages = new Tracker<HTMLElement>();
static readonly taskPages = new ViewTrackerMap<HTMLElement>(); static readonly taskPages = new TrackerMap<HTMLElement>();
static readonly boardPages = new ViewTrackerMap<HTMLElement>(); static readonly boardPages = new TrackerMap<HTMLElement>();
static readonly inlineReplies = new ViewTrackerMap<HTMLElement>(); static readonly inlineReplies = new TrackerMap<HTMLElement>();
static readonly messageIcons = new ViewTrackerMap<string>();
static scrollToView(id: string) { static readonly scrollToView = (id: string) => {
const element = document.getElementById(id); const element = document.getElementById(id);
if (!element) return if (!element) return;
element.scrollIntoView(); element.scrollIntoView();
element.setAttribute("scrolled", ""); element.setAttribute("scrolled", "");
setTimeout(()=>element.removeAttribute("scrolled"), 1000); setTimeout(() => element.removeAttribute("scrolled"), 1000);
} };
static readonly allowDrop = (event: DragEvent) => {
event.preventDefault();
};
static readonly allowDrag = (event: DragEvent) => {
event.dataTransfer?.setData("text", "");
};
static readonly reload = () => {
window.location.reload();
};
static readonly setFocus = () => {
const focusedInModal = document.querySelector(
".modal[open] #focused",
) as HTMLElement;
if (focusedInModal) return focusedInModal.focus();
document.getElementById("focused")?.focus();
};
static readonly setFocusWithDelay = () => {
console.trace("focus");
setTimeout(this.setFocus, 100);
};
} }
+6 -3
View File
@@ -82,12 +82,12 @@ export default class ChatMessageViewModel {
}; };
reply = (): void => { reply = (): void => {
this.messagePageViewModel.replyingMessage.value = this; this.messagePageViewModel.setReply(this);
this.hideInfoModal(); this.hideInfoModal();
}; };
cancelReply = (): void => { cancelReply = (): void => {
if (this.messagePageViewModel.replyingMessage.value != this) return; if (this.messagePageViewModel.replyingMessage.value != this) return;
this.messagePageViewModel.replyingMessage.value = undefined; this.messagePageViewModel.setReply(undefined);
}; };
// view // view
@@ -131,7 +131,10 @@ export default class ChatMessageViewModel {
this.status.value = this.chatMessage.status; this.status.value = this.chatMessage.status;
if (this.chatMessage.inlineReplyId) { if (this.chatMessage.inlineReplyId) {
this.inlineReply = this.messagePageViewModel.chatMessageViewModels.value.get(this.chatMessage.inlineReplyId); this.inlineReply =
this.messagePageViewModel.chatMessageViewModels.value.get(
this.chatMessage.inlineReplyId,
);
} }
}; };
+1 -1
View File
@@ -142,7 +142,7 @@ export default class ChatViewModel extends ContextHost<ChatPageTypes> {
handleContextClose = (): void => { handleContextClose = (): void => {
this.chatListViewModel.closeChat(); this.chatListViewModel.closeChat();
} };
// init // init
constructor( constructor(
+6 -3
View File
@@ -27,7 +27,10 @@ export default class CoreViewModel {
history.pushState({ id: context.contextId }, ""); history.pushState({ id: context.contextId }, "");
} }
closeContext = (contextId: string, fromHistoryEvent: boolean = false): void => { closeContext = (
contextId: string,
fromHistoryEvent: boolean = false,
): void => {
if ( if (
!this.contexts !this.contexts
.map((context) => context.contextId) .map((context) => context.contextId)
@@ -81,7 +84,7 @@ export default class CoreViewModel {
window.onpopstate = () => { window.onpopstate = () => {
this.closeContext(this.context.contextId, true); this.closeContext(this.context.contextId, true);
} };
} }
// util // util
@@ -110,7 +113,7 @@ export class Context {
this.keystrokes.set(key, fn); this.keystrokes.set(key, fn);
}; };
constructor(public contextDebugDescription: string) { } constructor(public contextDebugDescription: string) {}
} }
export class ContextHost<T> extends Context { export class ContextHost<T> extends Context {
@@ -171,7 +171,7 @@ export default class FileTransferViewModel extends Context {
handleContextClose = (): void => { handleContextClose = (): void => {
this.presentedModal.value = undefined; this.presentedModal.value = undefined;
} };
// init // init
constructor(public readonly coreViewModel: CoreViewModel) { constructor(public readonly coreViewModel: CoreViewModel) {
+2 -5
View File
@@ -71,7 +71,7 @@ export default class SettingsViewModel extends Context {
if (this.requiresReload.value == true) { if (this.requiresReload.value == true) {
window.location.reload(); window.location.reload();
} }
} };
// init // init
constructor(public readonly coreViewModel: CoreViewModel) { constructor(public readonly coreViewModel: CoreViewModel) {
@@ -102,10 +102,7 @@ export default class SettingsViewModel extends Context {
); );
// keystrokes // keystrokes
this.registerKeyStroke( this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close);
CommonKeys.CloseOrCancel,
this.close,
);
} }
static generateThemeMedia(): MediaQueryList { static generateThemeMedia(): MediaQueryList {
+1 -1
View File
@@ -65,7 +65,7 @@ export default class StorageViewModel extends Context {
return; return;
} }
this.isShowingStorageModal.value = false; this.isShowingStorageModal.value = false;
} };
// init // init
constructor(public readonly coreViewModel: CoreViewModel) { constructor(public readonly coreViewModel: CoreViewModel) {
+1 -1
View File
@@ -252,7 +252,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
if (!fromHistoryEvent) return; if (!fromHistoryEvent) return;
this.taskPageViewModel.storeLastUsedBoard(); this.taskPageViewModel.storeLastUsedBoard();
} };
// init // init
constructor( constructor(
+25 -6
View File
@@ -27,6 +27,7 @@ export default class MessagePageViewModel extends Context {
undefined, undefined,
); );
composingMessage = new React.State<string>(""); composingMessage = new React.State<string>("");
focusSetter = new React.State(null);
// guards // guards
cannotSendMessage: React.State<boolean>; cannotSendMessage: React.State<boolean>;
@@ -40,14 +41,11 @@ export default class MessagePageViewModel extends Context {
}; };
sendMessageFromBody = (body: string): void => { sendMessageFromBody = (body: string): void => {
let replyId: string|undefined = undefined; let replyId: string | undefined = undefined;
if (this.replyingMessage.value) { if (this.replyingMessage.value) {
replyId = this.replyingMessage.value.chatMessage.id; replyId = this.replyingMessage.value.chatMessage.id;
} }
this.chatViewModel.chatModel.sendMessage( this.chatViewModel.chatModel.sendMessage(body, replyId);
body,
replyId,
);
this.replyingMessage.value = undefined; this.replyingMessage.value = undefined;
}; };
@@ -72,6 +70,16 @@ export default class MessagePageViewModel extends Context {
); );
}; };
setReply = (chatMessageViewModel: ChatMessageViewModel): void => {
this.replyingMessage.value = chatMessageViewModel;
this.setFocus();
};
resetReply = (): void => {
this.replyingMessage.value = undefined;
this.setFocus();
};
// view // view
showChatMessage = (chatMessage: ChatMessage): void => { showChatMessage = (chatMessage: ChatMessage): void => {
const chatMessageViewModel = new ChatMessageViewModel( const chatMessageViewModel = new ChatMessageViewModel(
@@ -123,6 +131,10 @@ export default class MessagePageViewModel extends Context {
this.searchViewModel.search(""); this.searchViewModel.search("");
}; };
setFocus = (): void => {
this.focusSetter.callSubscriptions();
};
// load // load
loadData = (): void => { loadData = (): void => {
this.chatMessageViewModels.clear(); this.chatMessageViewModels.clear();
@@ -168,8 +180,15 @@ export default class MessagePageViewModel extends Context {
// keystrokes // keystrokes
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal); this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
this.registerKeyStroke(CommonKeys.CloseOrCancel, this.hideFilterModal); this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
if (this.isFilterModalOpen.value == true) {
this.hideFilterModal();
} else {
this.replyingMessage.value = undefined;
}
});
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
this.chatViewModel.registerContext(ChatPageTypes.Messages, this); this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
} }
+3 -2
View File
@@ -114,10 +114,11 @@ export default class TaskPageViewModel extends ContextHost<string> {
closeBoard = (): void => { closeBoard = (): void => {
this.closeCurrentContext(); this.closeCurrentContext();
} };
handleBoardClosed = (boardViewModel: BoardViewModel): void => { handleBoardClosed = (boardViewModel: BoardViewModel): void => {
if (boardViewModel.boardInfo.fileId != this.selectedBoardId.value) return; if (boardViewModel.boardInfo.fileId != this.selectedBoardId.value)
return;
this.selectedBoardId.value = undefined; this.selectedBoardId.value = undefined;
this.chatViewModel.resetColor(); this.chatViewModel.resetColor();
+2 -3
View File
@@ -8,9 +8,8 @@ import { localeCompare, padZero } from "../../Model/Utility/utility";
import ChatViewModel from "../Chat/chatViewModel"; import ChatViewModel from "../Chat/chatViewModel";
import CoreViewModel, { Context } from "../Global/coreViewModel"; import CoreViewModel, { Context } from "../Global/coreViewModel";
import TaskContainingPageViewModel from "./taskContainingPageViewModel"; import TaskContainingPageViewModel from "./taskContainingPageViewModel";
import { allowDrag } from "../../View/utility";
import { v4 } from "uuid";
import { CommonKeys } from "../../View/keystrokes"; import { CommonKeys } from "../../View/keystrokes";
import { ViewController } from "../../View/viewController";
export default class TaskViewModel extends Context { export default class TaskViewModel extends Context {
// util // util
@@ -63,7 +62,7 @@ export default class TaskViewModel extends Context {
// methods // methods
dragStart = (event: DragEvent): void => { dragStart = (event: DragEvent): void => {
allowDrag(event); ViewController.allowDrag(event);
this.coreViewModel.draggedObject.value = this; this.coreViewModel.draggedObject.value = this;
}; };