add inline replies, ui improvements, rename ViewPersistence->ViewController, add scroll method and highlight
This commit is contained in:
Vendored
+45
-2
@@ -590,7 +590,11 @@ body[showing-chat] #chat-page-wrapper {
|
|||||||
padding: 0px;
|
padding: 0px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr var(--control-height) var(--safe-area-bottom);
|
--composer-height: var(--control-height);
|
||||||
|
grid-template-rows: 1fr calc(var(--composer-height) + var(--gap-between-tiles)) var(--safe-area-bottom);
|
||||||
|
}
|
||||||
|
#chat-page #main #message-page .content:has(.reply-preview-wrapper .reply-preview) {
|
||||||
|
--composer-height: calc( 2 * var(--control-height) + var(--gap-between-tiles) );
|
||||||
}
|
}
|
||||||
#message-page #message-container {
|
#message-page #message-container {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -607,6 +611,32 @@ body[showing-chat] #chat-page-wrapper {
|
|||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
border-left: 1px solid var(--border);
|
border-left: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
#message-page #composer .content-width-constraint {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--gap-between-tiles);
|
||||||
|
}
|
||||||
|
#message-page #composer .reply-preview-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#message-page #composer .reply-preview-wrapper .reply-preview {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--max-input-width);
|
||||||
|
display: flex;
|
||||||
|
gap: var(--gap-between-tiles);
|
||||||
|
}
|
||||||
|
#message-page #composer .reply-preview-wrapper .reply-preview > div {
|
||||||
|
background: var(--surface);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--text-gap);
|
||||||
|
padding: 0px var(--content-padding);
|
||||||
|
width: calc(100% - var(--control-height) - var(--gap-between-tiles));
|
||||||
|
}
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
#message-page #composer div {
|
#message-page #composer div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -720,11 +750,24 @@ body[showing-chat] #chat-page-wrapper {
|
|||||||
.message-bubble[sentbyuser] .tile.main span {
|
.message-bubble[sentbyuser] .tile.main span {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.message-bubble > .inline-reply {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--surface-alt);
|
||||||
|
height: var(--control-height);
|
||||||
|
max-width: var(--max-input-width);
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0px var(--control-padding);
|
||||||
|
gap: var(--text-gap);
|
||||||
|
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%;
|
||||||
padding: var(--gap-between-tiles);
|
padding: var(--gap-between-tiles);
|
||||||
background: var(--surface);
|
background: var(--surface-alt);
|
||||||
display: none !important;
|
display: none !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+164
-53
@@ -1312,7 +1312,7 @@
|
|||||||
const combinedChannel = allChannels.join("/");
|
const combinedChannel = allChannels.join("/");
|
||||||
return [senderName, combinedChannel];
|
return [senderName, combinedChannel];
|
||||||
};
|
};
|
||||||
this.sendMessage = async (body, replyId, fileContent) => {
|
this.sendMessage = async (body, inlineReplyId, fileContent) => {
|
||||||
const nameAndChannel = this.getNameAndChannel();
|
const nameAndChannel = this.getNameAndChannel();
|
||||||
if (nameAndChannel == false) return;
|
if (nameAndChannel == false) return;
|
||||||
const [senderName, combinedChannel] = nameAndChannel;
|
const [senderName, combinedChannel] = nameAndChannel;
|
||||||
@@ -1321,6 +1321,7 @@
|
|||||||
senderName,
|
senderName,
|
||||||
this.info.encryptionKey,
|
this.info.encryptionKey,
|
||||||
body,
|
body,
|
||||||
|
inlineReplyId,
|
||||||
fileContent
|
fileContent
|
||||||
);
|
);
|
||||||
this.addMessage(chatMessage);
|
this.addMessage(chatMessage);
|
||||||
@@ -1462,7 +1463,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
static {
|
static {
|
||||||
this.createChatMessage = async (channel, sender, encryptionKey, body, fileContent) => {
|
this.createChatMessage = async (channel, sender, encryptionKey, body, inlineReplyId, fileContent) => {
|
||||||
const chatMessage = {
|
const chatMessage = {
|
||||||
dataVersion: DATA_VERSION,
|
dataVersion: DATA_VERSION,
|
||||||
id: v4_default(),
|
id: v4_default(),
|
||||||
@@ -1470,6 +1471,7 @@
|
|||||||
sender,
|
sender,
|
||||||
body,
|
body,
|
||||||
dateSent: createTimestamp(),
|
dateSent: createTimestamp(),
|
||||||
|
inlineReplyId,
|
||||||
status: "outbox" /* Outbox */,
|
status: "outbox" /* Outbox */,
|
||||||
stringifiedFile: ""
|
stringifiedFile: ""
|
||||||
};
|
};
|
||||||
@@ -1773,6 +1775,7 @@
|
|||||||
///
|
///
|
||||||
showMessageInfoButtonAudioLabel: "show message info",
|
showMessageInfoButtonAudioLabel: "show message info",
|
||||||
messageInfoHeadline: "Message Info",
|
messageInfoHeadline: "Message Info",
|
||||||
|
cancelReplyAudioLabel: "cancel reply",
|
||||||
sentBy: "Sent by",
|
sentBy: "Sent by",
|
||||||
timeSent: "Time sent",
|
timeSent: "Time sent",
|
||||||
channel: "Channel",
|
channel: "Channel",
|
||||||
@@ -1780,6 +1783,7 @@
|
|||||||
copyMessageButton: "Copy message",
|
copyMessageButton: "Copy message",
|
||||||
resendMessageButton: "Resend message",
|
resendMessageButton: "Resend message",
|
||||||
decryptMessageButton: "Decrypt message",
|
decryptMessageButton: "Decrypt message",
|
||||||
|
replyToMessageButton: "Reply to message",
|
||||||
deleteMessageButton: "Delete message",
|
deleteMessageButton: "Delete message",
|
||||||
//
|
//
|
||||||
thumbsUpReaction: "Reaction: thumbs up",
|
thumbsUpReaction: "Reaction: thumbs up",
|
||||||
@@ -1981,6 +1985,7 @@
|
|||||||
filterMessagesButtonAudioLabel: "nachrichten filtern",
|
filterMessagesButtonAudioLabel: "nachrichten filtern",
|
||||||
showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
|
showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
|
||||||
messageInfoHeadline: "Nachrichteninfo",
|
messageInfoHeadline: "Nachrichteninfo",
|
||||||
|
cancelReplyAudioLabel: "antwort abbrechen",
|
||||||
sentBy: "Gesendet von",
|
sentBy: "Gesendet von",
|
||||||
timeSent: "Sendezeit",
|
timeSent: "Sendezeit",
|
||||||
channel: "Kanal",
|
channel: "Kanal",
|
||||||
@@ -1988,6 +1993,7 @@
|
|||||||
copyMessageButton: "Nachricht kopieren",
|
copyMessageButton: "Nachricht kopieren",
|
||||||
resendMessageButton: "Nachricht erneut senden",
|
resendMessageButton: "Nachricht erneut senden",
|
||||||
decryptMessageButton: "Nachricht entschl\xFCsseln",
|
decryptMessageButton: "Nachricht entschl\xFCsseln",
|
||||||
|
replyToMessageButton: "Auf Nachricht antworten",
|
||||||
deleteMessageButton: "Nachricht l\xF6schen",
|
deleteMessageButton: "Nachricht l\xF6schen",
|
||||||
//
|
//
|
||||||
thumbsUpReaction: "Reaktion: Daumen hoch",
|
thumbsUpReaction: "Reaktion: Daumen hoch",
|
||||||
@@ -2179,6 +2185,7 @@
|
|||||||
filterMessagesButtonAudioLabel: "filtrar mensajes",
|
filterMessagesButtonAudioLabel: "filtrar mensajes",
|
||||||
showMessageInfoButtonAudioLabel: "mostrar informaci\xF3n del mensaje",
|
showMessageInfoButtonAudioLabel: "mostrar informaci\xF3n del mensaje",
|
||||||
messageInfoHeadline: "Informaci\xF3n del Mensaje",
|
messageInfoHeadline: "Informaci\xF3n del Mensaje",
|
||||||
|
cancelReplyAudioLabel: "abortar la respuesta",
|
||||||
sentBy: "Enviado por",
|
sentBy: "Enviado por",
|
||||||
timeSent: "Hora de env\xEDo",
|
timeSent: "Hora de env\xEDo",
|
||||||
channel: "Canal",
|
channel: "Canal",
|
||||||
@@ -2186,6 +2193,7 @@
|
|||||||
copyMessageButton: "Copiar mensaje",
|
copyMessageButton: "Copiar mensaje",
|
||||||
resendMessageButton: "Reenviar mensaje",
|
resendMessageButton: "Reenviar mensaje",
|
||||||
decryptMessageButton: "Desencriptar mensaje",
|
decryptMessageButton: "Desencriptar mensaje",
|
||||||
|
replyToMessageButton: "Responder al mensaje",
|
||||||
deleteMessageButton: "Eliminar mensaje",
|
deleteMessageButton: "Eliminar mensaje",
|
||||||
//
|
//
|
||||||
thumbsUpReaction: "Reacci\xF3n: pulgar hacia arriba",
|
thumbsUpReaction: "Reacci\xF3n: pulgar hacia arriba",
|
||||||
@@ -2768,6 +2776,7 @@
|
|||||||
this.coreViewModel = coreViewModel2;
|
this.coreViewModel = coreViewModel2;
|
||||||
this.messagePageViewModel = messagePageViewModel;
|
this.messagePageViewModel = messagePageViewModel;
|
||||||
this.body = new State("");
|
this.body = new State("");
|
||||||
|
this.inlineReply = void 0;
|
||||||
this.status = new State(
|
this.status = new State(
|
||||||
void 0
|
void 0
|
||||||
);
|
);
|
||||||
@@ -2820,6 +2829,14 @@
|
|||||||
this.decryptMessage = () => {
|
this.decryptMessage = () => {
|
||||||
this.messagePageViewModel.decryptMessage(this);
|
this.messagePageViewModel.decryptMessage(this);
|
||||||
};
|
};
|
||||||
|
this.reply = () => {
|
||||||
|
this.messagePageViewModel.replyingMessage.value = this;
|
||||||
|
this.hideInfoModal();
|
||||||
|
};
|
||||||
|
this.cancelReply = () => {
|
||||||
|
if (this.messagePageViewModel.replyingMessage.value != this) return;
|
||||||
|
this.messagePageViewModel.replyingMessage.value = void 0;
|
||||||
|
};
|
||||||
// view
|
// view
|
||||||
this.showInfoModal = () => {
|
this.showInfoModal = () => {
|
||||||
this.isPresentingInfoModal.value = true;
|
this.isPresentingInfoModal.value = true;
|
||||||
@@ -2852,6 +2869,9 @@
|
|||||||
this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
|
this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
|
||||||
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) {
|
||||||
|
this.inlineReply = this.messagePageViewModel.chatMessageViewModels.value.get(this.chatMessage.inlineReplyId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.chatMessage = chatMessage;
|
this.chatMessage = chatMessage;
|
||||||
this.sentByUser = sentByUser;
|
this.sentByUser = sentByUser;
|
||||||
@@ -2958,7 +2978,9 @@
|
|||||||
this.filteredMessageViewModels = new ListState();
|
this.filteredMessageViewModels = new ListState();
|
||||||
this.isFilterModalOpen = new State(false);
|
this.isFilterModalOpen = new State(false);
|
||||||
this.reactionFilter = new State(void 0);
|
this.reactionFilter = new State(void 0);
|
||||||
this.replyingMessageId = new State(void 0);
|
this.replyingMessage = new State(
|
||||||
|
void 0
|
||||||
|
);
|
||||||
this.composingMessage = new State("");
|
this.composingMessage = new State("");
|
||||||
// methods
|
// methods
|
||||||
this.sendMessage = () => {
|
this.sendMessage = () => {
|
||||||
@@ -2967,8 +2989,15 @@
|
|||||||
this.composingMessage.value = "";
|
this.composingMessage.value = "";
|
||||||
};
|
};
|
||||||
this.sendMessageFromBody = (body) => {
|
this.sendMessageFromBody = (body) => {
|
||||||
this.chatViewModel.chatModel.sendMessage(body, this.replyingMessageId.value);
|
let replyId = void 0;
|
||||||
this.replyingMessageId.value = void 0;
|
if (this.replyingMessage.value) {
|
||||||
|
replyId = this.replyingMessage.value.chatMessage.id;
|
||||||
|
}
|
||||||
|
this.chatViewModel.chatModel.sendMessage(
|
||||||
|
body,
|
||||||
|
replyId
|
||||||
|
);
|
||||||
|
this.replyingMessage.value = void 0;
|
||||||
};
|
};
|
||||||
this.decryptMessage = async (messageViewModel) => {
|
this.decryptMessage = async (messageViewModel) => {
|
||||||
const chatMessage = messageViewModel.chatMessage;
|
const chatMessage = messageViewModel.chatMessage;
|
||||||
@@ -3383,9 +3412,18 @@
|
|||||||
this.registerKeyStroke("," /* Settings */, this.showSettings);
|
this.registerKeyStroke("," /* Settings */, this.showSettings);
|
||||||
this.registerKeyStroke("enter" /* Apply */, this.hideSettings);
|
this.registerKeyStroke("enter" /* Apply */, this.hideSettings);
|
||||||
this.registerKeyStroke(";" /* Create */, this.createTask);
|
this.registerKeyStroke(";" /* Create */, this.createTask);
|
||||||
this.registerKeyStroke("j", () => this.selectedPage.value = "list" /* List */);
|
this.registerKeyStroke(
|
||||||
this.registerKeyStroke("k", () => this.selectedPage.value = "kanban" /* Kanban */);
|
"j",
|
||||||
this.registerKeyStroke("l", () => this.selectedPage.value = "status-grid" /* StatusGrid */);
|
() => this.selectedPage.value = "list" /* List */
|
||||||
|
);
|
||||||
|
this.registerKeyStroke(
|
||||||
|
"k",
|
||||||
|
() => this.selectedPage.value = "kanban" /* Kanban */
|
||||||
|
);
|
||||||
|
this.registerKeyStroke(
|
||||||
|
"l",
|
||||||
|
() => this.selectedPage.value = "status-grid" /* StatusGrid */
|
||||||
|
);
|
||||||
this.taskPageViewModel.registerContext(this.boardInfo.fileId, this);
|
this.taskPageViewModel.registerContext(this.boardInfo.fileId, this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -4435,7 +4473,7 @@
|
|||||||
"description",
|
"description",
|
||||||
coreViewModel2.translations.chatPage.message.messageContent,
|
coreViewModel2.translations.chatPage.message.messageContent,
|
||||||
chatMessageViewModel.body
|
chatMessageViewModel.body
|
||||||
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.copyMessage }, coreViewModel2.translations.chatPage.message.copyMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "content_copy")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.resendMessage }, coreViewModel2.translations.chatPage.message.resendMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "redo")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.decryptMessage }, coreViewModel2.translations.chatPage.message.decryptMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, MessageReactionButtonRow(
|
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.copyMessage }, coreViewModel2.translations.chatPage.message.copyMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "content_copy")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.resendMessage }, coreViewModel2.translations.chatPage.message.resendMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "redo")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.decryptMessage }, coreViewModel2.translations.chatPage.message.decryptMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "key")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.reply }, coreViewModel2.translations.chatPage.message.replyToMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "reply"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, MessageReactionButtonRow(
|
||||||
coreViewModel2,
|
coreViewModel2,
|
||||||
chatMessageViewModel
|
chatMessageViewModel
|
||||||
), /* @__PURE__ */ createElement(
|
), /* @__PURE__ */ createElement(
|
||||||
@@ -4451,9 +4489,70 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
function InlineReply(chatMessageViewModel) {
|
||||||
|
const reply = chatMessageViewModel.inlineReply;
|
||||||
|
if (reply == void 0) return /* @__PURE__ */ createElement("div", null);
|
||||||
|
const scroll = () => {
|
||||||
|
ViewController.scrollToView(reply.chatMessage.id);
|
||||||
|
};
|
||||||
|
return /* @__PURE__ */ createElement("div", { class: "inline-reply", "on:click": scroll }, /* @__PURE__ */ createElement("span", null, reply.sender), /* @__PURE__ */ createElement("b", { class: "ellipsis", "subscribe:innerText": reply.body }));
|
||||||
|
}
|
||||||
|
|
||||||
// src/View/Components/chatMessage.tsx
|
// src/View/Components/chatMessage.tsx
|
||||||
function ChatMessage4(coreViewModel2, chatMessageViewModel) {
|
function ChatMessage4(coreViewModel2, chatMessageViewModel) {
|
||||||
const statusIcon = createProxyState(
|
const persistenceId = chatMessageViewModel.chatMessage.id;
|
||||||
|
const statusIcon = ViewController.messageIcons.setItems(persistenceId, () => createProxyState(
|
||||||
[chatMessageViewModel.status],
|
[chatMessageViewModel.status],
|
||||||
() => {
|
() => {
|
||||||
switch (chatMessageViewModel.status.value) {
|
switch (chatMessageViewModel.status.value) {
|
||||||
@@ -4467,14 +4566,16 @@
|
|||||||
return "warning";
|
return "warning";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
));
|
||||||
return /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
class: "message-bubble",
|
class: "message-bubble",
|
||||||
|
id: chatMessageViewModel.chatMessage.id,
|
||||||
"toggle:sentbyuser": chatMessageViewModel.sentByUser,
|
"toggle:sentbyuser": chatMessageViewModel.sentByUser,
|
||||||
"toggle:hidden": chatMessageViewModel.isHidden
|
"toggle:hidden": chatMessageViewModel.isHidden
|
||||||
},
|
},
|
||||||
|
InlineReply(chatMessageViewModel),
|
||||||
/* @__PURE__ */ createElement("div", { class: "main tile" }, /* @__PURE__ */ createElement("div", { class: "text-container" }, /* @__PURE__ */ createElement("span", { class: "sender-name ellipsis" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement(
|
/* @__PURE__ */ createElement("div", { class: "main tile" }, /* @__PURE__ */ createElement("div", { class: "text-container" }, /* @__PURE__ */ createElement("span", { class: "sender-name ellipsis" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement(
|
||||||
"span",
|
"span",
|
||||||
{
|
{
|
||||||
@@ -4494,6 +4595,13 @@
|
|||||||
"aria-label": coreViewModel2.translations.chatPage.message.showMessageInfoButtonAudioLabel
|
"aria-label": coreViewModel2.translations.chatPage.message.showMessageInfoButtonAudioLabel
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "info")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "info")
|
||||||
|
), /* @__PURE__ */ createElement(
|
||||||
|
"button",
|
||||||
|
{
|
||||||
|
"on:click": chatMessageViewModel.reply,
|
||||||
|
"aria-label": coreViewModel2.translations.chatPage.message.replyToMessageButton
|
||||||
|
},
|
||||||
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "reply")
|
||||||
))),
|
))),
|
||||||
MessageReactionButtonRow(coreViewModel2, chatMessageViewModel),
|
MessageReactionButtonRow(coreViewModel2, chatMessageViewModel),
|
||||||
ChatMessageInfoModal(coreViewModel2, chatMessageViewModel)
|
ChatMessageInfoModal(coreViewModel2, chatMessageViewModel)
|
||||||
@@ -4572,6 +4680,22 @@
|
|||||||
)))), /* @__PURE__ */ createElement("button", { "on:click": messagePageViewModel.hideFilterModal }, coreViewModel2.translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
)))), /* @__PURE__ */ createElement("button", { "on:click": messagePageViewModel.hideFilterModal }, coreViewModel2.translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/View/Components/replyPreview.tsx
|
||||||
|
function ReplyPreview(coreViewModel2, chatMessageViewModel) {
|
||||||
|
const scroll = () => {
|
||||||
|
chatMessageViewModel.scrollIntoView();
|
||||||
|
};
|
||||||
|
return /* @__PURE__ */ createElement("div", { class: "reply-preview slide-up", "on:click": scroll }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement("b", { class: "ellipsis", "subscribe:innerText": chatMessageViewModel.body })), /* @__PURE__ */ createElement(
|
||||||
|
"button",
|
||||||
|
{
|
||||||
|
class: "standard square",
|
||||||
|
"on:click": chatMessageViewModel.cancelReply,
|
||||||
|
"aria-label": coreViewModel2.translations.chatPage.message.cancelReplyAudioLabel
|
||||||
|
},
|
||||||
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "close")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// src/View/ChatPages/messagePage.tsx
|
// src/View/ChatPages/messagePage.tsx
|
||||||
function MessagePage(coreViewModel2, messagePageViewModel) {
|
function MessagePage(coreViewModel2, messagePageViewModel) {
|
||||||
messagePageViewModel.loadData();
|
messagePageViewModel.loadData();
|
||||||
@@ -4588,6 +4712,21 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const persistenceId = messagePageViewModel.chatViewModel.chatModel.id;
|
||||||
|
const replyPreview = ViewController.inlineReplies.setItems(
|
||||||
|
persistenceId,
|
||||||
|
() => createProxyState(
|
||||||
|
[messagePageViewModel.replyingMessage],
|
||||||
|
() => {
|
||||||
|
if (messagePageViewModel.replyingMessage.value == void 0)
|
||||||
|
return /* @__PURE__ */ createElement("div", null);
|
||||||
|
return ReplyPreview(
|
||||||
|
coreViewModel2,
|
||||||
|
messagePageViewModel.replyingMessage.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
function scrollDown(hard = false) {
|
function scrollDown(hard = false) {
|
||||||
if (hard) {
|
if (hard) {
|
||||||
messageContainer.setAttribute("scroll-hard", "");
|
messageContainer.setAttribute("scroll-hard", "");
|
||||||
@@ -4604,6 +4743,9 @@
|
|||||||
scrollDownIfApplicable
|
scrollDownIfApplicable
|
||||||
);
|
);
|
||||||
setTimeout(() => scrollDown(true), 100);
|
setTimeout(() => scrollDown(true), 100);
|
||||||
|
messagePageViewModel.replyingMessage.subscribeSilent(() => {
|
||||||
|
setFocusWithDelay();
|
||||||
|
});
|
||||||
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",
|
||||||
@@ -4614,7 +4756,13 @@
|
|||||||
"toggle:selected": messagePageViewModel.isFilterActive
|
"toggle:selected": messagePageViewModel.isFilterActive
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "filter_alt")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "filter_alt")
|
||||||
))), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
|
))), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "reply-preview-wrapper",
|
||||||
|
"children:set": replyPreview
|
||||||
|
}
|
||||||
|
), /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
id: "focused",
|
id: "focused",
|
||||||
@@ -5205,48 +5353,11 @@
|
|||||||
)), /* @__PURE__ */ createElement("button", { "on:click": close }, coreViewModel2.translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
)), /* @__PURE__ */ createElement("button", { "on:click": close }, coreViewModel2.translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/View/viewPersistence.ts
|
|
||||||
var PageTracker = class {
|
|
||||||
};
|
|
||||||
var PageTrackerMap = class {
|
|
||||||
constructor() {
|
|
||||||
this.trackers = /* @__PURE__ */ new Map();
|
|
||||||
this.register = (key) => {
|
|
||||||
if (this.trackers.has(key)) {
|
|
||||||
return this.trackers.get(key);
|
|
||||||
}
|
|
||||||
const tracker = new PageTracker();
|
|
||||||
this.trackers.set(key, tracker);
|
|
||||||
return tracker;
|
|
||||||
};
|
|
||||||
this.setPages = (key, pages) => {
|
|
||||||
const tracker = this.register(key);
|
|
||||||
if (tracker.pages != void 0) {
|
|
||||||
return tracker.pages;
|
|
||||||
} else {
|
|
||||||
tracker.pages = pages();
|
|
||||||
return tracker.pages;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var ViewPersistence = class {
|
|
||||||
static {
|
|
||||||
this.chatPages = new PageTracker();
|
|
||||||
}
|
|
||||||
static {
|
|
||||||
this.taskPages = new PageTrackerMap();
|
|
||||||
}
|
|
||||||
static {
|
|
||||||
this.boardPages = new PageTrackerMap();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// src/View/ChatPages/boardPage.tsx
|
// src/View/ChatPages/boardPage.tsx
|
||||||
function BoardPage(coreViewModel2, boardViewModel) {
|
function BoardPage(coreViewModel2, boardViewModel) {
|
||||||
boardViewModel.loadData();
|
boardViewModel.loadData();
|
||||||
const persistenceId = boardViewModel.boardInfo.fileId;
|
const persistenceId = boardViewModel.boardInfo.fileId;
|
||||||
const pages = ViewPersistence.boardPages.setPages(
|
const pages = ViewController.boardPages.setItems(
|
||||||
persistenceId,
|
persistenceId,
|
||||||
() => createProxyState([boardViewModel.selectedPage], () => {
|
() => createProxyState([boardViewModel.selectedPage], () => {
|
||||||
switch (boardViewModel.selectedPage.value) {
|
switch (boardViewModel.selectedPage.value) {
|
||||||
@@ -5389,7 +5500,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 = ViewPersistence.taskPages.setPages(
|
const pages = ViewController.taskPages.setItems(
|
||||||
persistenceId,
|
persistenceId,
|
||||||
() => createProxyState([taskPageViewModel.selectedBoardId], () => {
|
() => createProxyState([taskPageViewModel.selectedBoardId], () => {
|
||||||
const selectedBoardId = taskPageViewModel.selectedBoardId.value;
|
const selectedBoardId = taskPageViewModel.selectedBoardId.value;
|
||||||
@@ -5461,7 +5572,7 @@
|
|||||||
|
|
||||||
// src/View/chatPage.tsx
|
// src/View/chatPage.tsx
|
||||||
function ChatPage(coreViewModel2, chatViewModel) {
|
function ChatPage(coreViewModel2, chatViewModel) {
|
||||||
ViewPersistence.chatPages.pages = createProxyState(
|
ViewController.chatPages.items = createProxyState(
|
||||||
[chatViewModel.selectedPage],
|
[chatViewModel.selectedPage],
|
||||||
() => {
|
() => {
|
||||||
switch (chatViewModel.selectedPage.value) {
|
switch (chatViewModel.selectedPage.value) {
|
||||||
@@ -5556,7 +5667,7 @@
|
|||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
id: "main",
|
id: "main",
|
||||||
"children:set": ViewPersistence.chatPages.pages
|
"children:set": ViewController.chatPages.items
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|||||||
Vendored
+28
-3
@@ -49,6 +49,7 @@ body {
|
|||||||
--surface: hsla(0, 0%, 0%, 5%);
|
--surface: hsla(0, 0%, 0%, 5%);
|
||||||
--surface-hover: hsla(0, 0%, 5%, 10%);
|
--surface-hover: hsla(0, 0%, 5%, 10%);
|
||||||
--surface-active: hsla(0, 0%, 5%, 15%);
|
--surface-active: hsla(0, 0%, 5%, 15%);
|
||||||
|
--surface-alt: hsla(0, 0%, 0%, 2%);
|
||||||
|
|
||||||
--border: hsla(0, 0%, 0%, 10%);
|
--border: hsla(0, 0%, 0%, 10%);
|
||||||
--surface-border: #8d8d8d;
|
--surface-border: #8d8d8d;
|
||||||
@@ -86,9 +87,10 @@ body[theme="dark"] {
|
|||||||
|
|
||||||
--layer: hsla(0, 0%, 15%, 60%);
|
--layer: hsla(0, 0%, 15%, 60%);
|
||||||
|
|
||||||
--surface: hsla(0, 0%, 100%, 4%);
|
--surface: hsla(0, 0%, 100%, 5%);
|
||||||
--surface-hover: hsla(0, 0%, 100%, 10%);
|
--surface-hover: hsla(0, 0%, 100%, 10%);
|
||||||
--surface-active: hsla(0, 0%, 100%, 15%);
|
--surface-active: hsla(0, 0%, 100%, 15%);
|
||||||
|
--surface-alt: hsla(0, 0%, 100%, 2%);
|
||||||
|
|
||||||
--border: hsla(0, 0%, 100%, 10%);
|
--border: hsla(0, 0%, 100%, 10%);
|
||||||
--surface-border: #6f6f6f;
|
--surface-border: #6f6f6f;
|
||||||
@@ -166,8 +168,11 @@ select:not(.tile *),
|
|||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (hover: hover) {
|
.surface-alt {
|
||||||
|
background: var(--surface-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
button:hover,
|
button:hover,
|
||||||
select:hover,
|
select:hover,
|
||||||
button.tile.ghost:hover {
|
button.tile.ghost:hover {
|
||||||
@@ -259,8 +264,9 @@ SIZES
|
|||||||
body {
|
body {
|
||||||
/* gap */
|
/* gap */
|
||||||
--gap-between-tiles: 4px;
|
--gap-between-tiles: 4px;
|
||||||
|
--gap-in-controls: 7px;
|
||||||
--gap-in-tab-links: 1rem;
|
--gap-in-tab-links: 1rem;
|
||||||
--gap-in-controls: 1.2rem;
|
--text-gap: 1.2rem;
|
||||||
--large-gap: 2rem;
|
--large-gap: 2rem;
|
||||||
|
|
||||||
/* outline */
|
/* outline */
|
||||||
@@ -822,6 +828,12 @@ select+.icon {
|
|||||||
height: var(--control-height);
|
height: var(--control-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.square {
|
||||||
|
width: var(--control-height);
|
||||||
|
padding: 0px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
height: var(--header-height);
|
height: var(--header-height);
|
||||||
}
|
}
|
||||||
@@ -1227,6 +1239,19 @@ STATE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[scrolled] {
|
||||||
|
animation: 1s ease scroll-highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scroll-highlight {
|
||||||
|
0%, 40%, 50%, 90% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
10%, 30%, 60%, 80% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ICONS
|
ICONS
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export default class ChatModel {
|
|||||||
|
|
||||||
sendMessage = async (
|
sendMessage = async (
|
||||||
body: string,
|
body: string,
|
||||||
replyId?: string,
|
inlineReplyId?: string,
|
||||||
fileContent?: FileContent<string>,
|
fileContent?: FileContent<string>,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
const nameAndChannel = this.getNameAndChannel();
|
const nameAndChannel = this.getNameAndChannel();
|
||||||
@@ -192,6 +192,7 @@ export default class ChatModel {
|
|||||||
senderName,
|
senderName,
|
||||||
this.info.encryptionKey,
|
this.info.encryptionKey,
|
||||||
body,
|
body,
|
||||||
|
inlineReplyId,
|
||||||
fileContent,
|
fileContent,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -373,6 +374,7 @@ export default class ChatModel {
|
|||||||
sender: string,
|
sender: string,
|
||||||
encryptionKey: string,
|
encryptionKey: string,
|
||||||
body: string,
|
body: string,
|
||||||
|
inlineReplyId?: string,
|
||||||
fileContent?: FileContent<string>,
|
fileContent?: FileContent<string>,
|
||||||
): Promise<ChatMessage> => {
|
): Promise<ChatMessage> => {
|
||||||
const chatMessage: ChatMessage = {
|
const chatMessage: ChatMessage = {
|
||||||
@@ -384,6 +386,7 @@ export default class ChatModel {
|
|||||||
sender,
|
sender,
|
||||||
body,
|
body,
|
||||||
dateSent: createTimestamp(),
|
dateSent: createTimestamp(),
|
||||||
|
inlineReplyId,
|
||||||
|
|
||||||
status: ChatMessageStatuses.Outbox,
|
status: ChatMessageStatuses.Outbox,
|
||||||
stringifiedFile: "",
|
stringifiedFile: "",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { TaskSettingsModal } from "../Modals/taskSettingsModal";
|
|||||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||||
import { setFocusWithDelay } from "../utility";
|
import { setFocusWithDelay } from "../utility";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import { ViewPersistence } from "../viewPersistence";
|
import { ViewController } from "../viewController";
|
||||||
|
|
||||||
export function BoardPage(
|
export function BoardPage(
|
||||||
coreViewModel: CoreViewModel,
|
coreViewModel: CoreViewModel,
|
||||||
@@ -23,7 +23,7 @@ export function BoardPage(
|
|||||||
|
|
||||||
// main content
|
// main content
|
||||||
const persistenceId = boardViewModel.boardInfo.fileId;
|
const persistenceId = boardViewModel.boardInfo.fileId;
|
||||||
const pages = ViewPersistence.boardPages.setPages(persistenceId, () =>
|
const pages = ViewController.boardPages.setItems(persistenceId, () =>
|
||||||
React.createProxyState([boardViewModel.selectedPage], () => {
|
React.createProxyState([boardViewModel.selectedPage], () => {
|
||||||
switch (boardViewModel.selectedPage.value) {
|
switch (boardViewModel.selectedPage.value) {
|
||||||
case BoardPageTypes.Kanban: {
|
case BoardPageTypes.Kanban: {
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
/* layout */
|
/* layout */
|
||||||
#chat-page #main #message-page .content {
|
#chat-page #main #message-page .content {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr var(--control-height) var(--safe-area-bottom);
|
--composer-height: var(--control-height);
|
||||||
|
grid-template-rows:
|
||||||
|
1fr calc(var(--composer-height) + var(--gap-between-tiles))
|
||||||
|
var(--safe-area-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-page
|
||||||
|
#main
|
||||||
|
#message-page
|
||||||
|
.content:has(.reply-preview-wrapper .reply-preview) {
|
||||||
|
--composer-height: calc(
|
||||||
|
2 * var(--control-height) + var(--gap-between-tiles)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#message-page #message-container {
|
#message-page #message-container {
|
||||||
@@ -20,21 +30,51 @@
|
|||||||
|
|
||||||
#message-page #composer input {
|
#message-page #composer input {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: unset;
|
max-width: unset;
|
||||||
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
border-left: 1px solid var(--border);
|
border-left: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#message-page #composer .content-width-constraint {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--gap-between-tiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-page #composer .reply-preview-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-page #composer .reply-preview-wrapper .reply-preview {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--max-input-width);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: var(--gap-between-tiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-page #composer .reply-preview-wrapper .reply-preview > div {
|
||||||
|
background: var(--surface);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--text-gap);
|
||||||
|
|
||||||
|
padding: 0px var(--content-padding);
|
||||||
|
|
||||||
|
width: calc(100% - var(--control-height) - var(--gap-between-tiles));
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
#message-page #composer div {
|
#message-page #composer div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: unset;
|
max-width: unset;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +84,6 @@
|
|||||||
#message-page #composer .input-width-constraint {
|
#message-page #composer .input-width-constraint {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: var(--max-content-width);
|
max-width: var(--max-content-width);
|
||||||
}
|
}
|
||||||
@@ -178,12 +217,31 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-bubble > .inline-reply {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background: var(--surface-alt);
|
||||||
|
height: var(--control-height);
|
||||||
|
|
||||||
|
max-width: var(--max-input-width);
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
padding: 0px var(--control-padding);
|
||||||
|
gap: var(--text-gap);
|
||||||
|
|
||||||
|
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%;
|
||||||
|
|
||||||
padding: var(--gap-between-tiles);
|
padding: var(--gap-between-tiles);
|
||||||
background: var(--surface);
|
background: var(--surface-alt);
|
||||||
|
|
||||||
display: none !important;
|
display: none !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ 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 { setFocusWithDelay } from "../utility";
|
||||||
|
import { ViewController } from "../viewController";
|
||||||
|
import { ReplyPreview } from "../Components/replyPreview";
|
||||||
|
|
||||||
export function MessagePage(
|
export function MessagePage(
|
||||||
coreViewModel: CoreViewModel,
|
coreViewModel: CoreViewModel,
|
||||||
@@ -31,6 +33,23 @@ export function MessagePage(
|
|||||||
></div>
|
></div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const persistenceId = messagePageViewModel.chatViewModel.chatModel.id;
|
||||||
|
const replyPreview = ViewController.inlineReplies.setItems(
|
||||||
|
persistenceId,
|
||||||
|
() =>
|
||||||
|
React.createProxyState(
|
||||||
|
[messagePageViewModel.replyingMessage],
|
||||||
|
() => {
|
||||||
|
if (messagePageViewModel.replyingMessage.value == undefined)
|
||||||
|
return <div></div>;
|
||||||
|
return ReplyPreview(
|
||||||
|
coreViewModel,
|
||||||
|
messagePageViewModel.replyingMessage.value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
function scrollDown(hard: boolean = false) {
|
function scrollDown(hard: boolean = false) {
|
||||||
if (hard) {
|
if (hard) {
|
||||||
messageContainer.setAttribute("scroll-hard", "");
|
messageContainer.setAttribute("scroll-hard", "");
|
||||||
@@ -51,6 +70,9 @@ export function MessagePage(
|
|||||||
);
|
);
|
||||||
setTimeout(() => scrollDown(true), 100);
|
setTimeout(() => scrollDown(true), 100);
|
||||||
|
|
||||||
|
messagePageViewModel.replyingMessage.subscribeSilent(() => {
|
||||||
|
setFocusWithDelay();
|
||||||
|
})
|
||||||
setFocusWithDelay();
|
setFocusWithDelay();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -84,6 +106,10 @@ export function MessagePage(
|
|||||||
{messageContainer}
|
{messageContainer}
|
||||||
<div id="composer">
|
<div id="composer">
|
||||||
<div class="content-width-constraint">
|
<div class="content-width-constraint">
|
||||||
|
<div
|
||||||
|
class="reply-preview-wrapper"
|
||||||
|
children:set={replyPreview}
|
||||||
|
></div>
|
||||||
<div class="input-width-constraint">
|
<div class="input-width-constraint">
|
||||||
<input
|
<input
|
||||||
id="focused"
|
id="focused"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
|||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
import { PlaceholderView } from "../Components/placeholderView";
|
import { PlaceholderView } from "../Components/placeholderView";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { ViewPersistence } from "../viewPersistence";
|
import { ViewController } from "../viewController";
|
||||||
|
|
||||||
export function TaskPage(
|
export function TaskPage(
|
||||||
coreViewModel: CoreViewModel,
|
coreViewModel: CoreViewModel,
|
||||||
@@ -22,7 +22,7 @@ export function TaskPage(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const persistenceId = taskPageViewModel.chatViewModel.chatModel.id;
|
const persistenceId = taskPageViewModel.chatViewModel.chatModel.id;
|
||||||
const pages = ViewPersistence.taskPages.setPages(persistenceId, () =>
|
const pages = ViewController.taskPages.setItems(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) {
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import { ChatMessageStatuses } from "../../Model/Chat/chatModel";
|
|||||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
import { MessageReactionButtonRow } from "./messageReactionButtonRow";
|
import { MessageReactionButtonRow } from "./messageReactionButtonRow";
|
||||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
import { InlineReply } from "./inlineReply";
|
||||||
|
import { ViewController } from "../viewController";
|
||||||
|
|
||||||
export function ChatMessage(
|
export function ChatMessage(
|
||||||
coreViewModel: CoreViewModel,
|
coreViewModel: CoreViewModel,
|
||||||
chatMessageViewModel: ChatMessageViewModel,
|
chatMessageViewModel: ChatMessageViewModel,
|
||||||
) {
|
) {
|
||||||
const statusIcon = React.createProxyState(
|
const persistenceId = chatMessageViewModel.chatMessage.id;
|
||||||
|
const statusIcon = ViewController.messageIcons.setItems(persistenceId,()=> React.createProxyState(
|
||||||
[chatMessageViewModel.status],
|
[chatMessageViewModel.status],
|
||||||
() => {
|
() => {
|
||||||
switch (chatMessageViewModel.status.value) {
|
switch (chatMessageViewModel.status.value) {
|
||||||
@@ -23,15 +26,17 @@ export function ChatMessage(
|
|||||||
default:
|
default:
|
||||||
return "warning";
|
return "warning";
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="message-bubble"
|
class="message-bubble"
|
||||||
|
id={chatMessageViewModel.chatMessage.id}
|
||||||
toggle:sentbyuser={chatMessageViewModel.sentByUser}
|
toggle:sentbyuser={chatMessageViewModel.sentByUser}
|
||||||
toggle:hidden={chatMessageViewModel.isHidden}
|
toggle:hidden={chatMessageViewModel.isHidden}
|
||||||
>
|
>
|
||||||
|
{InlineReply(chatMessageViewModel)}
|
||||||
<div class="main tile">
|
<div class="main tile">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<span class="sender-name ellipsis">
|
<span class="sender-name ellipsis">
|
||||||
@@ -60,6 +65,15 @@ export function ChatMessage(
|
|||||||
>
|
>
|
||||||
<span class="icon">info</span>
|
<span class="icon">info</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={chatMessageViewModel.reply}
|
||||||
|
aria-label={
|
||||||
|
coreViewModel.translations.chatPage.message
|
||||||
|
.replyToMessageButton
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span class="icon">reply</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{MessageReactionButtonRow(coreViewModel, chatMessageViewModel)}
|
{MessageReactionButtonRow(coreViewModel, chatMessageViewModel)}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
import { ViewController } from "../viewController";
|
||||||
|
|
||||||
|
export function InlineReply (
|
||||||
|
chatMessageViewModel: ChatMessageViewModel,
|
||||||
|
) {
|
||||||
|
const reply = chatMessageViewModel.inlineReply;
|
||||||
|
if (reply == undefined) return <div></div>
|
||||||
|
|
||||||
|
const scroll = () => {
|
||||||
|
ViewController.scrollToView(reply.chatMessage.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="inline-reply" on:click={scroll}>
|
||||||
|
<span>{reply.sender}</span>
|
||||||
|
<b class="ellipsis" subscribe:innerText={reply.body}></b>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
|
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||||
|
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||||
|
|
||||||
|
export function ReplyPreview(
|
||||||
|
coreViewModel: CoreViewModel,
|
||||||
|
chatMessageViewModel: ChatMessageViewModel,
|
||||||
|
) {
|
||||||
|
const scroll = () => {
|
||||||
|
chatMessageViewModel.scrollIntoView();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div class="reply-preview slide-up" on:click={scroll}>
|
||||||
|
<div>
|
||||||
|
<span class="secondary">{chatMessageViewModel.sender}</span>
|
||||||
|
<b class="ellipsis" subscribe:innerText={chatMessageViewModel.body}></b>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="standard square"
|
||||||
|
on:click={chatMessageViewModel.cancelReply}
|
||||||
|
aria-label={coreViewModel.translations.chatPage.message.cancelReplyAudioLabel}
|
||||||
|
>
|
||||||
|
<span class="icon">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -73,6 +73,13 @@ export function ChatMessageInfoModal(
|
|||||||
}
|
}
|
||||||
<span class="icon">key</span>
|
<span class="icon">key</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button on:click={chatMessageViewModel.reply}>
|
||||||
|
{
|
||||||
|
coreViewModel.translations.chatPage.message
|
||||||
|
.replyToMessageButton
|
||||||
|
}
|
||||||
|
<span class="icon">reply</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import { MessagePage } from "./ChatPages/messagePage";
|
|||||||
import { SettingsPage } from "./ChatPages/settingsPage";
|
import { SettingsPage } from "./ChatPages/settingsPage";
|
||||||
import { TaskPage } from "./ChatPages/taskPage";
|
import { TaskPage } from "./ChatPages/taskPage";
|
||||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||||
import { ViewPersistence } from "./viewPersistence";
|
import { ViewController } from "./viewController";
|
||||||
|
|
||||||
export function ChatPage(
|
export function ChatPage(
|
||||||
coreViewModel: CoreViewModel,
|
coreViewModel: CoreViewModel,
|
||||||
chatViewModel: ChatViewModel,
|
chatViewModel: ChatViewModel,
|
||||||
) {
|
) {
|
||||||
ViewPersistence.chatPages.pages = React.createProxyState(
|
ViewController.chatPages.items = 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={ViewPersistence.chatPages.pages}
|
children:set={ViewController.chatPages.items}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const englishTranslations = {
|
|||||||
|
|
||||||
showMessageInfoButtonAudioLabel: "show message info",
|
showMessageInfoButtonAudioLabel: "show message info",
|
||||||
messageInfoHeadline: "Message Info",
|
messageInfoHeadline: "Message Info",
|
||||||
|
cancelReplyAudioLabel: "cancel reply",
|
||||||
|
|
||||||
sentBy: "Sent by",
|
sentBy: "Sent by",
|
||||||
timeSent: "Time sent",
|
timeSent: "Time sent",
|
||||||
@@ -208,6 +209,7 @@ const englishTranslations = {
|
|||||||
copyMessageButton: "Copy message",
|
copyMessageButton: "Copy message",
|
||||||
resendMessageButton: "Resend message",
|
resendMessageButton: "Resend message",
|
||||||
decryptMessageButton: "Decrypt message",
|
decryptMessageButton: "Decrypt message",
|
||||||
|
replyToMessageButton: "Reply to message",
|
||||||
deleteMessageButton: "Delete message",
|
deleteMessageButton: "Delete message",
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -496,6 +498,7 @@ export const allTranslations: {
|
|||||||
|
|
||||||
showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
|
showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
|
||||||
messageInfoHeadline: "Nachrichteninfo",
|
messageInfoHeadline: "Nachrichteninfo",
|
||||||
|
cancelReplyAudioLabel: "antwort abbrechen",
|
||||||
|
|
||||||
sentBy: "Gesendet von",
|
sentBy: "Gesendet von",
|
||||||
timeSent: "Sendezeit",
|
timeSent: "Sendezeit",
|
||||||
@@ -505,6 +508,7 @@ export const allTranslations: {
|
|||||||
copyMessageButton: "Nachricht kopieren",
|
copyMessageButton: "Nachricht kopieren",
|
||||||
resendMessageButton: "Nachricht erneut senden",
|
resendMessageButton: "Nachricht erneut senden",
|
||||||
decryptMessageButton: "Nachricht entschlüsseln",
|
decryptMessageButton: "Nachricht entschlüsseln",
|
||||||
|
replyToMessageButton: "Auf Nachricht antworten",
|
||||||
deleteMessageButton: "Nachricht löschen",
|
deleteMessageButton: "Nachricht löschen",
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -772,6 +776,7 @@ export const allTranslations: {
|
|||||||
showMessageInfoButtonAudioLabel:
|
showMessageInfoButtonAudioLabel:
|
||||||
"mostrar información del mensaje",
|
"mostrar información del mensaje",
|
||||||
messageInfoHeadline: "Información del Mensaje",
|
messageInfoHeadline: "Información del Mensaje",
|
||||||
|
cancelReplyAudioLabel: "abortar la respuesta",
|
||||||
|
|
||||||
sentBy: "Enviado por",
|
sentBy: "Enviado por",
|
||||||
timeSent: "Hora de envío",
|
timeSent: "Hora de envío",
|
||||||
@@ -781,6 +786,7 @@ export const allTranslations: {
|
|||||||
copyMessageButton: "Copiar mensaje",
|
copyMessageButton: "Copiar mensaje",
|
||||||
resendMessageButton: "Reenviar mensaje",
|
resendMessageButton: "Reenviar mensaje",
|
||||||
decryptMessageButton: "Desencriptar mensaje",
|
decryptMessageButton: "Desencriptar mensaje",
|
||||||
|
replyToMessageButton: "Responder al mensaje",
|
||||||
deleteMessageButton: "Eliminar mensaje",
|
deleteMessageButton: "Eliminar mensaje",
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
|
class ItemTracker<T> {
|
||||||
|
items: React.State<T | undefined> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewTrackerMap<T> {
|
||||||
|
trackers = new Map<string, ItemTracker<T>>();
|
||||||
|
|
||||||
|
private register = (key: string): ItemTracker<T> => {
|
||||||
|
if (this.trackers.has(key)) {
|
||||||
|
return this.trackers.get(key);
|
||||||
|
}
|
||||||
|
const tracker = new ItemTracker<T>();
|
||||||
|
this.trackers.set(key, tracker);
|
||||||
|
return tracker;
|
||||||
|
};
|
||||||
|
|
||||||
|
setItems = (
|
||||||
|
key: string,
|
||||||
|
views: () => React.State<T>,
|
||||||
|
): React.State<T | undefined> | undefined => {
|
||||||
|
const tracker: ItemTracker<T> = this.register(key);
|
||||||
|
|
||||||
|
if (tracker.items != undefined) {
|
||||||
|
return tracker.items;
|
||||||
|
} else {
|
||||||
|
tracker.items = views();
|
||||||
|
return tracker.items;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewController {
|
||||||
|
static readonly chatPages = new ItemTracker<HTMLElement>();
|
||||||
|
static readonly taskPages = new ViewTrackerMap<HTMLElement>();
|
||||||
|
static readonly boardPages = new ViewTrackerMap<HTMLElement>();
|
||||||
|
static readonly inlineReplies = new ViewTrackerMap<HTMLElement>();
|
||||||
|
static readonly messageIcons = new ViewTrackerMap<string>();
|
||||||
|
|
||||||
|
static scrollToView(id: string) {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
if (!element) return
|
||||||
|
element.scrollIntoView();
|
||||||
|
element.setAttribute("scrolled", "");
|
||||||
|
setTimeout(()=>element.removeAttribute("scrolled"), 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import * as React from "bloatless-react";
|
|
||||||
|
|
||||||
class PageTracker {
|
|
||||||
pages: React.State<HTMLElement | undefined> | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PageTrackerMap {
|
|
||||||
trackers = new Map<string, PageTracker>();
|
|
||||||
|
|
||||||
private register = (key: string): PageTracker => {
|
|
||||||
if (this.trackers.has(key)) {
|
|
||||||
return this.trackers.get(key);
|
|
||||||
}
|
|
||||||
const tracker = new PageTracker();
|
|
||||||
this.trackers.set(key, tracker);
|
|
||||||
return tracker;
|
|
||||||
};
|
|
||||||
|
|
||||||
setPages = (
|
|
||||||
key: string,
|
|
||||||
pages: () => React.State<HTMLElement>,
|
|
||||||
): React.State<HTMLElement | undefined> | undefined => {
|
|
||||||
const tracker: PageTracker = this.register(key);
|
|
||||||
|
|
||||||
if (tracker.pages != undefined) {
|
|
||||||
return tracker.pages;
|
|
||||||
} else {
|
|
||||||
tracker.pages = pages();
|
|
||||||
return tracker.pages;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ViewPersistence {
|
|
||||||
static readonly chatPages = new PageTracker();
|
|
||||||
static readonly taskPages = new PageTrackerMap();
|
|
||||||
static readonly boardPages = new PageTrackerMap();
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@ export default class ChatMessageViewModel {
|
|||||||
sender: string;
|
sender: string;
|
||||||
dateSent: string;
|
dateSent: string;
|
||||||
body: React.State<string> = new React.State("");
|
body: React.State<string> = new React.State("");
|
||||||
|
inlineReply: ChatMessageViewModel | undefined = undefined;
|
||||||
status: React.State<ChatMessageStatuses | any> = new React.State<any>(
|
status: React.State<ChatMessageStatuses | any> = new React.State<any>(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
@@ -80,6 +81,15 @@ export default class ChatMessageViewModel {
|
|||||||
this.messagePageViewModel.decryptMessage(this);
|
this.messagePageViewModel.decryptMessage(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reply = (): void => {
|
||||||
|
this.messagePageViewModel.replyingMessage.value = this;
|
||||||
|
this.hideInfoModal();
|
||||||
|
};
|
||||||
|
cancelReply = (): void => {
|
||||||
|
if (this.messagePageViewModel.replyingMessage.value != this) return;
|
||||||
|
this.messagePageViewModel.replyingMessage.value = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
// view
|
// view
|
||||||
showInfoModal = (): void => {
|
showInfoModal = (): void => {
|
||||||
this.isPresentingInfoModal.value = true;
|
this.isPresentingInfoModal.value = true;
|
||||||
@@ -119,6 +129,10 @@ export default class ChatMessageViewModel {
|
|||||||
this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
|
this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
|
||||||
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) {
|
||||||
|
this.inlineReply = this.messagePageViewModel.chatMessageViewModels.value.get(this.chatMessage.inlineReplyId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
|
|||||||
@@ -318,9 +318,18 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
this.registerKeyStroke(CommonKeys.Apply, this.hideSettings);
|
this.registerKeyStroke(CommonKeys.Apply, this.hideSettings);
|
||||||
this.registerKeyStroke(CommonKeys.Create, this.createTask);
|
this.registerKeyStroke(CommonKeys.Create, this.createTask);
|
||||||
|
|
||||||
this.registerKeyStroke("j", ()=>this.selectedPage.value = BoardPageTypes.List);
|
this.registerKeyStroke(
|
||||||
this.registerKeyStroke("k", ()=>this.selectedPage.value = BoardPageTypes.Kanban);
|
"j",
|
||||||
this.registerKeyStroke("l", ()=>this.selectedPage.value = BoardPageTypes.StatusGrid);
|
() => (this.selectedPage.value = BoardPageTypes.List),
|
||||||
|
);
|
||||||
|
this.registerKeyStroke(
|
||||||
|
"k",
|
||||||
|
() => (this.selectedPage.value = BoardPageTypes.Kanban),
|
||||||
|
);
|
||||||
|
this.registerKeyStroke(
|
||||||
|
"l",
|
||||||
|
() => (this.selectedPage.value = BoardPageTypes.StatusGrid),
|
||||||
|
);
|
||||||
|
|
||||||
this.taskPageViewModel.registerContext(this.boardInfo.fileId, this);
|
this.taskPageViewModel.registerContext(this.boardInfo.fileId, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export default class MessagePageViewModel extends Context {
|
|||||||
reactionFilter = new React.State<ReactionSymbols | undefined>(undefined);
|
reactionFilter = new React.State<ReactionSymbols | undefined>(undefined);
|
||||||
isFilterActive: React.State<boolean>;
|
isFilterActive: React.State<boolean>;
|
||||||
|
|
||||||
replyingMessageId = new React.State<string|undefined>(undefined)
|
replyingMessage = new React.State<ChatMessageViewModel | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
composingMessage = new React.State<string>("");
|
composingMessage = new React.State<string>("");
|
||||||
|
|
||||||
// guards
|
// guards
|
||||||
@@ -38,8 +40,15 @@ export default class MessagePageViewModel extends Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
sendMessageFromBody = (body: string): void => {
|
sendMessageFromBody = (body: string): void => {
|
||||||
this.chatViewModel.chatModel.sendMessage(body, this.replyingMessageId.value);
|
let replyId: string|undefined = undefined;
|
||||||
this.replyingMessageId.value = undefined;
|
if (this.replyingMessage.value) {
|
||||||
|
replyId = this.replyingMessage.value.chatMessage.id;
|
||||||
|
}
|
||||||
|
this.chatViewModel.chatModel.sendMessage(
|
||||||
|
body,
|
||||||
|
replyId,
|
||||||
|
);
|
||||||
|
this.replyingMessage.value = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
decryptMessage = async (
|
decryptMessage = async (
|
||||||
|
|||||||
Reference in New Issue
Block a user