add inline replies, ui improvements, rename ViewPersistence->ViewController, add scroll method and highlight
This commit is contained in:
@@ -180,7 +180,7 @@ export default class ChatModel {
|
||||
|
||||
sendMessage = async (
|
||||
body: string,
|
||||
replyId?: string,
|
||||
inlineReplyId?: string,
|
||||
fileContent?: FileContent<string>,
|
||||
): Promise<boolean> => {
|
||||
const nameAndChannel = this.getNameAndChannel();
|
||||
@@ -192,6 +192,7 @@ export default class ChatModel {
|
||||
senderName,
|
||||
this.info.encryptionKey,
|
||||
body,
|
||||
inlineReplyId,
|
||||
fileContent,
|
||||
);
|
||||
|
||||
@@ -373,6 +374,7 @@ export default class ChatModel {
|
||||
sender: string,
|
||||
encryptionKey: string,
|
||||
body: string,
|
||||
inlineReplyId?: string,
|
||||
fileContent?: FileContent<string>,
|
||||
): Promise<ChatMessage> => {
|
||||
const chatMessage: ChatMessage = {
|
||||
@@ -384,6 +386,7 @@ export default class ChatModel {
|
||||
sender,
|
||||
body,
|
||||
dateSent: createTimestamp(),
|
||||
inlineReplyId,
|
||||
|
||||
status: ChatMessageStatuses.Outbox,
|
||||
stringifiedFile: "",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { TaskSettingsModal } from "../Modals/taskSettingsModal";
|
||||
import { TaskViewModelToEntry } from "../Components/taskEntry";
|
||||
import { setFocusWithDelay } from "../utility";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import { ViewPersistence } from "../viewPersistence";
|
||||
import { ViewController } from "../viewController";
|
||||
|
||||
export function BoardPage(
|
||||
coreViewModel: CoreViewModel,
|
||||
@@ -23,7 +23,7 @@ export function BoardPage(
|
||||
|
||||
// main content
|
||||
const persistenceId = boardViewModel.boardInfo.fileId;
|
||||
const pages = ViewPersistence.boardPages.setPages(persistenceId, () =>
|
||||
const pages = ViewController.boardPages.setItems(persistenceId, () =>
|
||||
React.createProxyState([boardViewModel.selectedPage], () => {
|
||||
switch (boardViewModel.selectedPage.value) {
|
||||
case BoardPageTypes.Kanban: {
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
/* layout */
|
||||
#chat-page #main #message-page .content {
|
||||
padding: 0px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
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 {
|
||||
@@ -20,21 +30,51 @@
|
||||
|
||||
#message-page #composer input {
|
||||
margin: 0px;
|
||||
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
|
||||
flex: 1;
|
||||
|
||||
border-top: 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) {
|
||||
#message-page #composer div {
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +84,6 @@
|
||||
#message-page #composer .input-width-constraint {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
width: 100%;
|
||||
max-width: var(--max-content-width);
|
||||
}
|
||||
@@ -178,12 +217,31 @@
|
||||
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 {
|
||||
max-width: var(--max-input-width);
|
||||
width: 100%;
|
||||
|
||||
padding: var(--gap-between-tiles);
|
||||
background: var(--surface);
|
||||
background: var(--surface-alt);
|
||||
|
||||
display: none !important;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -8,6 +8,8 @@ import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
import { ChatMessage } from "../Components/chatMessage";
|
||||
import { MessageFilterModal } from "../Modals/messageFilterModal";
|
||||
import { setFocusWithDelay } from "../utility";
|
||||
import { ViewController } from "../viewController";
|
||||
import { ReplyPreview } from "../Components/replyPreview";
|
||||
|
||||
export function MessagePage(
|
||||
coreViewModel: CoreViewModel,
|
||||
@@ -31,6 +33,23 @@ export function MessagePage(
|
||||
></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) {
|
||||
if (hard) {
|
||||
messageContainer.setAttribute("scroll-hard", "");
|
||||
@@ -51,6 +70,9 @@ export function MessagePage(
|
||||
);
|
||||
setTimeout(() => scrollDown(true), 100);
|
||||
|
||||
messagePageViewModel.replyingMessage.subscribeSilent(() => {
|
||||
setFocusWithDelay();
|
||||
})
|
||||
setFocusWithDelay();
|
||||
|
||||
return (
|
||||
@@ -84,6 +106,10 @@ export function MessagePage(
|
||||
{messageContainer}
|
||||
<div id="composer">
|
||||
<div class="content-width-constraint">
|
||||
<div
|
||||
class="reply-preview-wrapper"
|
||||
children:set={replyPreview}
|
||||
></div>
|
||||
<div class="input-width-constraint">
|
||||
<input
|
||||
id="focused"
|
||||
|
||||
@@ -8,7 +8,7 @@ import TaskPageViewModel from "../../ViewModel/Pages/taskPageViewModel";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import { PlaceholderView } from "../Components/placeholderView";
|
||||
import { v4 } from "uuid";
|
||||
import { ViewPersistence } from "../viewPersistence";
|
||||
import { ViewController } from "../viewController";
|
||||
|
||||
export function TaskPage(
|
||||
coreViewModel: CoreViewModel,
|
||||
@@ -22,7 +22,7 @@ export function TaskPage(
|
||||
);
|
||||
|
||||
const persistenceId = taskPageViewModel.chatViewModel.chatModel.id;
|
||||
const pages = ViewPersistence.taskPages.setPages(persistenceId, () =>
|
||||
const pages = ViewController.taskPages.setItems(persistenceId, () =>
|
||||
React.createProxyState([taskPageViewModel.selectedBoardId], () => {
|
||||
const selectedBoardId = taskPageViewModel.selectedBoardId.value;
|
||||
if (selectedBoardId == undefined) {
|
||||
|
||||
@@ -5,12 +5,15 @@ import { ChatMessageStatuses } from "../../Model/Chat/chatModel";
|
||||
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
|
||||
import { MessageReactionButtonRow } from "./messageReactionButtonRow";
|
||||
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
|
||||
import { InlineReply } from "./inlineReply";
|
||||
import { ViewController } from "../viewController";
|
||||
|
||||
export function ChatMessage(
|
||||
coreViewModel: CoreViewModel,
|
||||
chatMessageViewModel: ChatMessageViewModel,
|
||||
) {
|
||||
const statusIcon = React.createProxyState(
|
||||
const persistenceId = chatMessageViewModel.chatMessage.id;
|
||||
const statusIcon = ViewController.messageIcons.setItems(persistenceId,()=> React.createProxyState(
|
||||
[chatMessageViewModel.status],
|
||||
() => {
|
||||
switch (chatMessageViewModel.status.value) {
|
||||
@@ -23,15 +26,17 @@ export function ChatMessage(
|
||||
default:
|
||||
return "warning";
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
));
|
||||
|
||||
return (
|
||||
<div
|
||||
class="message-bubble"
|
||||
id={chatMessageViewModel.chatMessage.id}
|
||||
toggle:sentbyuser={chatMessageViewModel.sentByUser}
|
||||
toggle:hidden={chatMessageViewModel.isHidden}
|
||||
>
|
||||
{InlineReply(chatMessageViewModel)}
|
||||
<div class="main tile">
|
||||
<div class="text-container">
|
||||
<span class="sender-name ellipsis">
|
||||
@@ -60,6 +65,15 @@ export function ChatMessage(
|
||||
>
|
||||
<span class="icon">info</span>
|
||||
</button>
|
||||
<button
|
||||
on:click={chatMessageViewModel.reply}
|
||||
aria-label={
|
||||
coreViewModel.translations.chatPage.message
|
||||
.replyToMessageButton
|
||||
}
|
||||
>
|
||||
<span class="icon">reply</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{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>
|
||||
</button>
|
||||
<button on:click={chatMessageViewModel.reply}>
|
||||
{
|
||||
coreViewModel.translations.chatPage.message
|
||||
.replyToMessageButton
|
||||
}
|
||||
<span class="icon">reply</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
@@ -10,13 +10,13 @@ import { MessagePage } from "./ChatPages/messagePage";
|
||||
import { SettingsPage } from "./ChatPages/settingsPage";
|
||||
import { TaskPage } from "./ChatPages/taskPage";
|
||||
import CoreViewModel from "../ViewModel/Global/coreViewModel";
|
||||
import { ViewPersistence } from "./viewPersistence";
|
||||
import { ViewController } from "./viewController";
|
||||
|
||||
export function ChatPage(
|
||||
coreViewModel: CoreViewModel,
|
||||
chatViewModel: ChatViewModel,
|
||||
) {
|
||||
ViewPersistence.chatPages.pages = React.createProxyState(
|
||||
ViewController.chatPages.items = React.createProxyState(
|
||||
[chatViewModel.selectedPage],
|
||||
() => {
|
||||
switch (chatViewModel.selectedPage.value) {
|
||||
@@ -132,7 +132,7 @@ export function ChatPage(
|
||||
</div>
|
||||
<div
|
||||
id="main"
|
||||
children:set={ViewPersistence.chatPages.pages}
|
||||
children:set={ViewController.chatPages.items}
|
||||
></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -199,6 +199,7 @@ const englishTranslations = {
|
||||
|
||||
showMessageInfoButtonAudioLabel: "show message info",
|
||||
messageInfoHeadline: "Message Info",
|
||||
cancelReplyAudioLabel: "cancel reply",
|
||||
|
||||
sentBy: "Sent by",
|
||||
timeSent: "Time sent",
|
||||
@@ -208,6 +209,7 @@ const englishTranslations = {
|
||||
copyMessageButton: "Copy message",
|
||||
resendMessageButton: "Resend message",
|
||||
decryptMessageButton: "Decrypt message",
|
||||
replyToMessageButton: "Reply to message",
|
||||
deleteMessageButton: "Delete message",
|
||||
|
||||
//
|
||||
@@ -496,6 +498,7 @@ export const allTranslations: {
|
||||
|
||||
showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
|
||||
messageInfoHeadline: "Nachrichteninfo",
|
||||
cancelReplyAudioLabel: "antwort abbrechen",
|
||||
|
||||
sentBy: "Gesendet von",
|
||||
timeSent: "Sendezeit",
|
||||
@@ -505,6 +508,7 @@ export const allTranslations: {
|
||||
copyMessageButton: "Nachricht kopieren",
|
||||
resendMessageButton: "Nachricht erneut senden",
|
||||
decryptMessageButton: "Nachricht entschlüsseln",
|
||||
replyToMessageButton: "Auf Nachricht antworten",
|
||||
deleteMessageButton: "Nachricht löschen",
|
||||
|
||||
//
|
||||
@@ -772,6 +776,7 @@ export const allTranslations: {
|
||||
showMessageInfoButtonAudioLabel:
|
||||
"mostrar información del mensaje",
|
||||
messageInfoHeadline: "Información del Mensaje",
|
||||
cancelReplyAudioLabel: "abortar la respuesta",
|
||||
|
||||
sentBy: "Enviado por",
|
||||
timeSent: "Hora de envío",
|
||||
@@ -781,6 +786,7 @@ export const allTranslations: {
|
||||
copyMessageButton: "Copiar mensaje",
|
||||
resendMessageButton: "Reenviar mensaje",
|
||||
decryptMessageButton: "Desencriptar mensaje",
|
||||
replyToMessageButton: "Responder al 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;
|
||||
dateSent: string;
|
||||
body: React.State<string> = new React.State("");
|
||||
inlineReply: ChatMessageViewModel | undefined = undefined;
|
||||
status: React.State<ChatMessageStatuses | any> = new React.State<any>(
|
||||
undefined,
|
||||
);
|
||||
@@ -80,6 +81,15 @@ export default class ChatMessageViewModel {
|
||||
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
|
||||
showInfoModal = (): void => {
|
||||
this.isPresentingInfoModal.value = true;
|
||||
@@ -119,6 +129,10 @@ export default class ChatMessageViewModel {
|
||||
this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
|
||||
this.body.value = this.chatMessage.body;
|
||||
this.status.value = this.chatMessage.status;
|
||||
|
||||
if (this.chatMessage.inlineReplyId) {
|
||||
this.inlineReply = this.messagePageViewModel.chatMessageViewModels.value.get(this.chatMessage.inlineReplyId);
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
|
||||
@@ -318,9 +318,18 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
||||
this.registerKeyStroke(CommonKeys.Apply, this.hideSettings);
|
||||
this.registerKeyStroke(CommonKeys.Create, this.createTask);
|
||||
|
||||
this.registerKeyStroke("j", ()=>this.selectedPage.value = BoardPageTypes.List);
|
||||
this.registerKeyStroke("k", ()=>this.selectedPage.value = BoardPageTypes.Kanban);
|
||||
this.registerKeyStroke("l", ()=>this.selectedPage.value = BoardPageTypes.StatusGrid);
|
||||
this.registerKeyStroke(
|
||||
"j",
|
||||
() => (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);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ export default class MessagePageViewModel extends Context {
|
||||
reactionFilter = new React.State<ReactionSymbols | undefined>(undefined);
|
||||
isFilterActive: React.State<boolean>;
|
||||
|
||||
replyingMessageId = new React.State<string|undefined>(undefined)
|
||||
replyingMessage = new React.State<ChatMessageViewModel | undefined>(
|
||||
undefined,
|
||||
);
|
||||
composingMessage = new React.State<string>("");
|
||||
|
||||
// guards
|
||||
@@ -38,8 +40,15 @@ export default class MessagePageViewModel extends Context {
|
||||
};
|
||||
|
||||
sendMessageFromBody = (body: string): void => {
|
||||
this.chatViewModel.chatModel.sendMessage(body, this.replyingMessageId.value);
|
||||
this.replyingMessageId.value = undefined;
|
||||
let replyId: string|undefined = undefined;
|
||||
if (this.replyingMessage.value) {
|
||||
replyId = this.replyingMessage.value.chatMessage.id;
|
||||
}
|
||||
this.chatViewModel.chatModel.sendMessage(
|
||||
body,
|
||||
replyId,
|
||||
);
|
||||
this.replyingMessage.value = undefined;
|
||||
};
|
||||
|
||||
decryptMessage = async (
|
||||
|
||||
Reference in New Issue
Block a user