Skip to content

Commit

Permalink
Store draft messages by MessageContext (#5042)
Browse files Browse the repository at this point in the history
  • Loading branch information
hpeebles authored Dec 19, 2023
1 parent 8306314 commit 2914f73
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 166 deletions.
14 changes: 7 additions & 7 deletions frontend/app/src/components/home/CurrentChat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
$: currentChatPinnedMessages = client.currentChatPinnedMessages;
$: currentChatAttachment = client.currentChatAttachment;
$: currentChatEditingEvent = client.currentChatEditingEvent;
$: currentChatDraftMessage = client.currentChatDraftMessage;
$: draftMessagesStore = client.draftMessagesStore;
$: lastCryptoSent = client.lastCryptoSent;
$: messagesRead = client.messagesRead;
$: directlyBlockedUsers = client.blockedUsers;
Expand Down Expand Up @@ -151,7 +151,7 @@
}
function fileSelected(ev: CustomEvent<AttachmentContent>) {
currentChatDraftMessage.setAttachment(chat.id, ev.detail);
draftMessagesStore.setAttachment({ chatId: chat.id }, ev.detail);
}
function attachGif(ev: CustomEvent<string>) {
Expand All @@ -170,7 +170,7 @@
function replyTo(ev: CustomEvent<EnhancedReplyContext>) {
showSearchHeader = false;
currentChatDraftMessage.setReplyingTo(chat.id, ev.detail);
draftMessagesStore.setReplyingTo({ chatId: chat.id }, ev.detail);
}
function searchChat(ev: CustomEvent<string>) {
Expand Down Expand Up @@ -226,7 +226,7 @@
}
function setTextContent(ev: CustomEvent<string | undefined>): void {
currentChatDraftMessage.setTextContent(chat.id, ev.detail);
draftMessagesStore.setTextContent({ chatId: chat.id }, ev.detail);
}
function isBlocked(chatSummary: ChatSummary, blockedUsers: Set<string>): boolean {
Expand Down Expand Up @@ -337,9 +337,9 @@
{blocked}
on:joinGroup
on:upgrade
on:cancelReply={() => currentChatDraftMessage.setReplyingTo(chat.id, undefined)}
on:clearAttachment={() => currentChatDraftMessage.setAttachment(chat.id, undefined)}
on:cancelEditEvent={() => currentChatDraftMessage.clear(chat.id)}
on:cancelReply={() => draftMessagesStore.setReplyingTo({ chatId: chat.id }, undefined)}
on:clearAttachment={() => draftMessagesStore.setAttachment({ chatId: chat.id }, undefined)}
on:cancelEditEvent={() => draftMessagesStore.delete({ chatId: chat.id })}
on:setTextContent={setTextContent}
on:startTyping={() => client.startTyping(chat, $user.userId)}
on:stopTyping={() => client.stopTyping(chat, $user.userId)}
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/src/components/home/CurrentChatMessages.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
$: unconfirmed = client.unconfirmed;
$: failedMessagesStore = client.failedMessagesStore;
$: userGroupKeys = client.userGroupKeys;
$: currentChatDraftMessage = client.currentChatDraftMessage;
$: draftMessagesStore = client.draftMessagesStore;
$: focusMessageIndex = client.focusMessageIndex;
$: chatStateStore = client.chatStateStore;
$: chatListScope = client.chatListScope;
Expand Down Expand Up @@ -90,7 +90,7 @@
}
function onEditEvent(ev: CustomEvent<EventWrapper<Message>>) {
currentChatDraftMessage.setEditing(chat.id, ev.detail);
draftMessagesStore.setEditing({ chatId: chat.id }, ev.detail);
}
function eventKey(e: EventWrapper<ChatEventType>): string {
Expand Down
8 changes: 4 additions & 4 deletions frontend/app/src/components/home/Home.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
$: selectedChatStore = client.selectedChatStore;
$: selectedChatId = client.selectedChatId;
$: chatsInitialised = client.chatsInitialised;
$: currentChatDraftMessage = client.currentChatDraftMessage;
$: draftMessagesStore = client.draftMessagesStore;
$: chatStateStore = client.chatStateStore;
$: confirmMessage = getConfirmMessage(confirmActionEvent);
$: chatListScope = client.chatListScope;
Expand Down Expand Up @@ -681,8 +681,8 @@
});
const chatId = chat?.id ?? { kind: "direct_chat", userId: ev.detail.sender.userId };
currentChatDraftMessage.setTextContent(chatId, "");
currentChatDraftMessage.setReplyingTo(chatId, ev.detail);
draftMessagesStore.setTextContent({ chatId }, "");
draftMessagesStore.setReplyingTo({ chatId }, ev.detail);
if (chat) {
page(routeForChatIdentifier($chatListScope.kind, chatId));
} else {
Expand Down Expand Up @@ -872,7 +872,7 @@
text += shareUrl;
}
currentChatDraftMessage.setTextContent(chatId, text);
draftMessagesStore.setTextContent({ chatId }, text);
}
function groupCreated(
Expand Down
21 changes: 10 additions & 11 deletions frontend/app/src/components/home/thread/Thread.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
$: user = client.user;
$: focusMessageIndex = client.focusThreadMessageIndex;
$: lastCryptoSent = client.lastCryptoSent;
$: draftThreadMessages = client.draftThreadMessages;
$: draftMessagesStore = client.draftMessagesStore;
$: unconfirmed = client.unconfirmed;
$: messagesRead = client.messagesRead;
$: currentChatBlockedUsers = client.currentChatBlockedUsers;
Expand All @@ -59,8 +59,8 @@
$: messageContext = { chatId: chat.id, threadRootMessageIndex };
$: threadRootMessage = rootEvent.event;
$: blocked = chat.kind === "direct_chat" && $currentChatBlockedUsers.has(chat.them.userId);
$: draftMessage = readable(draftThreadMessages.get(threadRootMessageIndex), (set) =>
draftThreadMessages.subscribe((d) => set(d[threadRootMessageIndex] ?? {})),
$: draftMessage = readable(draftMessagesStore.get(messageContext), (set) =>
draftMessagesStore.subscribe((d) => set(d.get(messageContext) ?? {}))
);
$: textContent = derived(draftMessage, (d) => d.textContent);
$: replyingTo = derived(draftMessage, (d) => d.replyingTo);
Expand Down Expand Up @@ -112,11 +112,10 @@
} else {
sendMessageWithAttachment(text, $attachment, mentioned);
}
draftThreadMessages.delete(threadRootMessageIndex);
}
function editEvent(ev: EventWrapper<Message>): void {
draftThreadMessages.setEditing(threadRootMessageIndex, ev);
draftMessagesStore.setEditing(messageContext, ev);
}
function sendMessageWithAttachment(
Expand All @@ -128,19 +127,19 @@
}
function cancelReply() {
draftThreadMessages.setReplyingTo(threadRootMessageIndex, undefined);
draftMessagesStore.setReplyingTo(messageContext, undefined);
}
function clearAttachment() {
draftThreadMessages.setAttachment(threadRootMessageIndex, undefined);
draftMessagesStore.setAttachment(messageContext, undefined);
}
function cancelEditEvent() {
draftThreadMessages.delete(threadRootMessageIndex);
draftMessagesStore.delete(messageContext);
}
function setTextContent(ev: CustomEvent<string | undefined>) {
draftThreadMessages.setTextContent(threadRootMessageIndex, ev.detail);
draftMessagesStore.setTextContent(messageContext, ev.detail);
}
function onStartTyping() {
Expand All @@ -152,7 +151,7 @@
}
function fileSelected(ev: CustomEvent<AttachmentContent>) {
draftThreadMessages.setAttachment(threadRootMessageIndex, ev.detail);
draftMessagesStore.setAttachment(messageContext, ev.detail);
}
function tokenTransfer(ev: CustomEvent<{ ledger: string; amount: bigint } | undefined>) {
Expand Down Expand Up @@ -186,7 +185,7 @@
}
function replyTo(ev: CustomEvent<EnhancedReplyContext>) {
draftThreadMessages.setReplyingTo(threadRootMessageIndex, ev.detail);
draftMessagesStore.setReplyingTo(messageContext, ev.detail);
}
function defaultCryptoTransferReceiver(): string | undefined {
Expand Down
10 changes: 3 additions & 7 deletions frontend/openchat-client/src/liveState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import {
selectedMessageContext,
allChats,
currentChatMembers,
currentChatDraftMessage,
currentChatRules,
} from "./stores/chat";
import { remainingStorage } from "./stores/storage";
Expand All @@ -62,9 +61,8 @@ import {
currentCommunityRules,
} from "./stores/community";
import { type GlobalState, chatListScopeStore, globalStateStore } from "./stores/global";
import type { DraftMessage, DraftMessagesByThread } from "./stores/draftMessageFactory";
import { draftThreadMessages } from "./stores/draftThreadMessages";
import { offlineStore } from "./stores/network";
import { type DraftMessages, draftMessagesStore } from "./stores/draftMessages";

/**
* Any stores that we reference inside the OpenChat client can be added here so that we always have the up to date current value
Expand Down Expand Up @@ -110,8 +108,7 @@ export class LiveState {
allChats!: ChatMap<ChatSummary>;
selectedCommunity!: CommunitySummary | undefined;
currentCommunityMembers!: Map<string, Member>;
currentChatDraftMessage!: DraftMessage | undefined;
draftThreadMessages!: DraftMessagesByThread;
draftMessages!: DraftMessages;
currentCommunityRules!: VersionedRules | undefined;
user!: CreatedUser;
anonUser!: boolean;
Expand Down Expand Up @@ -168,8 +165,7 @@ export class LiveState {
allChats.subscribe((data) => (this.allChats = data));
selectedCommunity.subscribe((data) => (this.selectedCommunity = data));
currentCommunityMembers.subscribe((data) => (this.currentCommunityMembers = data));
currentChatDraftMessage.subscribe((data) => (this.currentChatDraftMessage = data));
draftThreadMessages.subscribe((data) => (this.draftThreadMessages = data));
draftMessagesStore.subscribe((data) => (this.draftMessages = data));
currentCommunityRules.subscribe((data) => (this.currentCommunityRules = data));
}
}
27 changes: 6 additions & 21 deletions frontend/openchat-client/src/openchat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ import {
lastCryptoSent,
nervousSystemLookup,
} from "./stores/crypto";
import { draftThreadMessages } from "./stores/draftThreadMessages";
import {
disableAllProposalFilters,
enableAllProposalFilters,
Expand Down Expand Up @@ -438,10 +437,10 @@ import { localCommunitySummaryUpdates } from "./stores/localCommunitySummaryUpda
import { hasFlag, moderationFlags } from "./stores/flagStore";
import { hasOwnerRights } from "./utils/permissions";
import { isDisplayNameValid, isUsernameValid } from "./utils/validation";
import type { DraftMessage } from "./stores/draftMessageFactory";
import { verifyCredential } from "./utils/credentials";
import { offlineStore } from "./stores/network";
import { messageFiltersStore, type MessageFilter } from "./stores/messageFilters";
import { draftMessagesStore } from "./stores/draftMessages";

const UPGRADE_POLL_INTERVAL = 1000;
const MARK_ONLINE_INTERVAL = 61 * 1000;
Expand Down Expand Up @@ -3247,13 +3246,6 @@ export class OpenChat extends OpenChatAgentWorker {
return this._liveState.threadEvents;
}

private draftMessageForMessageContext({
threadRootMessageIndex,
}: MessageContext): DraftMessage | undefined {
if (threadRootMessageIndex === undefined) return this._liveState.currentChatDraftMessage;
return this._liveState.draftThreadMessages[threadRootMessageIndex];
}

eventExpiry(chat: ChatSummary, timestamp: number): number | undefined {
if (chat.kind === "group_chat" || chat.kind === "channel") {
if (chat.eventsTTL !== undefined) {
Expand All @@ -3277,7 +3269,7 @@ export class OpenChat extends OpenChatAgentWorker {
return;
}

const draftMessage = this.draftMessageForMessageContext(messageContext);
const draftMessage = this._liveState.draftMessages.get(messageContext);
const currentEvents = this.eventsForMessageContext(messageContext);
const [nextEventIndex, nextMessageIndex] =
threadRootMessageIndex !== undefined
Expand Down Expand Up @@ -3445,9 +3437,7 @@ export class OpenChat extends OpenChatAgentWorker {
messagesRead.markReadUpTo(context, messageEvent.event.messageIndex - 1);
}

if (threadRootMessageIndex === undefined) {
currentChatDraftMessage.clear(chat.id);
}
draftMessagesStore.delete(context);

this.sendMessageWebRtc(chat, messageEvent, threadRootMessageIndex).then(() => {
this.dispatchEvent(new SentMessage(context, messageEvent));
Expand Down Expand Up @@ -3507,25 +3497,20 @@ export class OpenChat extends OpenChatAgentWorker {
return Promise.resolve(false);
}

const { chatId, threadRootMessageIndex } = messageContext;

if (textContent || attachment) {
const msg = {
...editingEvent.event,
edited: true,
content: this.getMessageContent(textContent ?? undefined, attachment),
};
localMessageUpdates.markContentEdited(msg.messageId, msg.content);

if (threadRootMessageIndex === undefined) {
currentChatDraftMessage.clear(chatId);
}
draftMessagesStore.delete(messageContext);

return this.sendRequest({
kind: "editMessage",
chatId: chat.id,
msg,
threadRootMessageIndex,
threadRootMessageIndex: messageContext.threadRootMessageIndex,
})
.then((resp) => {
if (resp !== "success") {
Expand Down Expand Up @@ -5969,7 +5954,7 @@ export class OpenChat extends OpenChatAgentWorker {
nervousSystemLookup = nervousSystemLookup;
exchangeRatesLookupStore = exchangeRatesLookupStore;
lastCryptoSent = lastCryptoSent;
draftThreadMessages = draftThreadMessages;
draftMessagesStore = draftMessagesStore;
translationStore = translationStore;
eventsStore = eventsStore;
selectedChatStore = selectedChatStore;
Expand Down
47 changes: 8 additions & 39 deletions frontend/openchat-client/src/stores/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ import type {
ChatSpecificState,
ChatSummary,
DirectChatSummary,
EnhancedReplyContext,
EventWrapper,
Message,
ThreadSyncDetails,
ChatIdentifier,
DirectChatIdentifier,
MultiUserChat,
ChatListScope,
AttachmentContent,
ExpiredEventsRange,
MessageContext,
} from "openchat-shared";
Expand All @@ -23,7 +20,6 @@ import {
ChatMap,
nullMembership,
chatIdentifiersEqual,
isAttachmentContent,
messageContextsEqual,
} from "openchat-shared";
import { unconfirmed } from "./unconfirmed";
Expand All @@ -37,13 +33,12 @@ import {
mergeChatMetrics,
mergeLocalSummaryUpdates,
} from "../utils/chat";
import { currentUser, currentUserIdStore, suspendedUsers, userStore } from "./user";
import { currentUser, currentUserIdStore, suspendedUsers } from "./user";
import DRange from "drange";
import { snsFunctions } from "./snsFunctions";
import { filteredProposalsStore, resetFilteredProposalsStore } from "./filteredProposals";
import { createChatSpecificObjectStore } from "./dataByChatFactory";
import { localMessageUpdates } from "./localMessageUpdates";
import type { DraftMessage } from "./draftMessageFactory";
import { localChatSummaryUpdates } from "./localChatSummaryUpdates";
import { setsAreEqual } from "../utils/set";
import { failedMessagesStore } from "./failedMessages";
Expand All @@ -62,6 +57,7 @@ import { safeWritable } from "./safeWritable";
import { communityPreviewsStore, currentCommunityBlockedUsers } from "./community";
import { translationStore } from "./translation";
import { messageFiltersStore } from "./messageFilters";
import { draftMessagesStore } from "./draftMessages";

let currentScope: ChatListScope = { kind: "direct_chat" };
chatListScopeStore.subscribe((s) => (currentScope = s));
Expand Down Expand Up @@ -733,40 +729,13 @@ export function clearServerEvents(id: ChatIdentifier): void {
chatStateStore.setProp(id, "expiredEventRanges", new DRange());
}

/**
* You might think that this belongs in the chatStateStore, but this needs to persist across chat selection boundary
* so it has a different scope.
*/
const draftMessages = createChatSpecificObjectStore<DraftMessage>(selectedChatId, () => ({}));

export const currentChatDraftMessage = {
...draftMessages,
setTextContent: (id: ChatIdentifier, textContent: string | undefined): void =>
draftMessages.setProp(id, "textContent", textContent),
setAttachment: (id: ChatIdentifier, attachment: AttachmentContent | undefined): void =>
draftMessages.setProp(id, "attachment", attachment),
setReplyingTo: (id: ChatIdentifier, replyingTo: EnhancedReplyContext | undefined): void =>
draftMessages.setProp(id, "replyingTo", replyingTo),
setEditing: (id: ChatIdentifier, editingEvent: EventWrapper<Message>): void => {
const users = get(userStore);
const updated = {
editingEvent,
attachment: isAttachmentContent(editingEvent.event.content)
? editingEvent.event.content
: undefined,
replyingTo:
editingEvent.event.repliesTo &&
editingEvent.event.repliesTo.kind === "rehydrated_reply_context"
? {
...editingEvent.event.repliesTo,
content: editingEvent.event.content,
sender: users[editingEvent.event.sender],
}
: undefined,
};
draftMessages.update(id, (d) => ({ ...d, ...updated }));
export const currentChatDraftMessage = derived(
[draftMessagesStore, selectedChatId],
([draftMessages, chatId]) => {
return chatId !== undefined ? draftMessages.get({ chatId }) ?? {} : {};
},
};
);

export const currentChatTextContent = createDerivedPropStore(
currentChatDraftMessage,
"textContent",
Expand Down
Loading

0 comments on commit 2914f73

Please sign in to comment.