FIX: improves draft for channels (#21724)

This commit attempts to correctly change draft when the channel changes. It moves responsibility to the composer instead of the channel.

A new service `chatDraftsManager` is being introduced here to allow finer control and pave the way for future thread draft support.

These changes also now allow an editing message to be stored as a draft.
This commit is contained in:
Joffrey JAFFEUX
2023-05-24 15:36:46 +02:00
committed by GitHub
parent d4a5b79592
commit 4de1d3952b
17 changed files with 338 additions and 177 deletions
@@ -43,6 +43,7 @@ export default class ChatLivePane extends Component {
@service appEvents;
@service messageBus;
@service site;
@service chatDraftsManager;
@tracked loading = false;
@tracked loadingMorePast = false;
@@ -116,11 +117,6 @@ export default class ChatLivePane extends Component {
if (this._loadedChannelId !== this.args.channel?.id) {
this.unsubscribeToUpdates(this._loadedChannelId);
this.chatChannelPane.selectingMessages = false;
if (this.args.channel.draft) {
this.chatChannelComposer.message = this.args.channel.draft;
}
this._loadedChannelId = this.args.channel?.id;
}
@@ -642,6 +638,7 @@ export default class ChatLivePane extends Component {
.editMessage(this.args.channel.id, message.id, data)
.catch(popupAjaxError)
.finally(() => {
this.chatDraftsManager.remove({ channelId: this.args.channel.id });
this.chatChannelPane.sending = false;
});
}
@@ -704,7 +701,7 @@ export default class ChatLivePane extends Component {
return;
}
this.args.channel.draft = null;
this.chatDraftsManager.remove({ channelId: this.args.channel.id });
this.chatChannelPane.sending = false;
});
}
@@ -1,4 +1,8 @@
<div class="chat-composer-message-details" data-id={{@message.id}}>
<div
class="chat-composer-message-details"
data-id={{@message.id}}
data-action={{if @message.editing "edit" "reply"}}
>
<div class="chat-reply">
{{d-icon (if @message.editing "pencil-alt" "reply")}}
<ChatUserAvatar @user={{@message.user}} />
@@ -26,6 +26,8 @@
{{did-update this.didUpdateMessage this.currentMessage}}
{{did-update this.didUpdateInReplyTo this.currentMessage.inReplyTo}}
{{did-insert this.setup}}
{{did-insert this.didUpdateChannel}}
{{did-update this.didUpdateChannel @channel.id}}
{{will-destroy this.teardown}}
{{will-destroy this.cancelPersistDraft}}
>
@@ -17,8 +17,8 @@ import I18n from "I18n";
import { translations } from "pretty-text/emoji/data";
import { setupHashtagAutocomplete } from "discourse/lib/hashtag-autocomplete";
import { isEmpty, isPresent } from "@ember/utils";
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
import { Promise } from "rsvp";
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
import User from "discourse/models/user";
export default class ChatComposer extends Component {
@@ -33,6 +33,7 @@ export default class ChatComposer extends Component {
@service chatEmojiPickerManager;
@service currentUser;
@service chatApi;
@service chatDraftsManager;
@tracked isFocused = false;
@tracked inProgressUploadsCount = 0;
@@ -78,15 +79,8 @@ export default class ChatComposer extends Component {
}
@action
sendMessage(raw) {
const message = ChatMessage.createDraftMessage(this.args.channel, {
user: this.currentUser,
message: raw,
});
this.args.onSendMessage(message);
return Promise.resolve();
sendMessage() {
this.args.onSendMessage(this.currentMessage);
}
@action
@@ -107,13 +101,15 @@ export default class ChatComposer extends Component {
@action
didUpdateMessage() {
cancel(this._persistHandler);
this.cancelPersistDraft();
this.textareaInteractor.value = this.currentMessage.message || "";
this.textareaInteractor.focus({ refreshHeight: true });
this.persistDraft();
}
@action
didUpdateInReplyTo() {
this.cancelPersistDraft();
this.textareaInteractor.focus({ ensureAtEnd: true, refreshHeight: true });
this.persistDraft();
}
@@ -148,11 +144,21 @@ export default class ChatComposer extends Component {
);
}
@action
didUpdateChannel() {
if (!this.args.channel) {
this.composer.message = null;
}
this.composer.message =
this.chatDraftsManager.get({ channelId: this.args.channel.id }) ||
ChatMessage.createDraftMessage(this.args.channel, {
user: this.currentUser,
});
}
@action
setup() {
this.composer.message = ChatMessage.createDraftMessage(this.args.channel, {
user: this.currentUser,
});
this.appEvents.on("chat:modify-selection", this, "modifySelection");
this.appEvents.on(
"chat:open-insert-link-modal",
@@ -7,6 +7,7 @@ import { action } from "@ember/object";
export default class ChatComposerChannel extends ChatComposer {
@service("chat-channel-composer") composer;
@service("chat-channel-pane") pane;
@service chatDraftsManager;
context = "channel";
@@ -23,6 +24,8 @@ export default class ChatComposerChannel extends ChatComposer {
return;
}
this.chatDraftsManager.add(this.currentMessage);
this._persistHandler = discourseDebounce(
this,
this._debouncedPersistDraft,
@@ -81,7 +81,6 @@ export default class ChatChannel {
@tracked canFlag;
@tracked canModerate;
@tracked userSilenced;
@tracked draft = null;
@tracked meta;
@tracked chatableType;
@tracked chatableUrl;
@@ -138,8 +138,12 @@ export default class ChatMessage {
}
cook() {
const site = getOwner(this).lookup("service:site");
next(() => {
const site = getOwner(this).lookup("service:site");
if (this.isDestroyed || this.isDestroying) {
return;
}
const markdownOptions = {
featuresOverride:
@@ -248,6 +252,12 @@ export default class ChatMessage {
};
}
if (this.editing) {
data.editing = true;
data.id = this.id;
data.excerpt = this.excerpt;
}
return JSON.stringify(data);
}
@@ -1,11 +1,19 @@
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import ChatComposer from "./chat-composer";
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
export default class ChatChannelComposer extends ChatComposer {
@service chat;
@service router;
@action
reset(channel) {
this.message = ChatMessage.createDraftMessage(channel, {
user: this.currentUser,
});
}
@action
replyTo(message) {
this.chat.activeMessage = null;
@@ -7,7 +7,7 @@ export default class ChatChannelThreadComposer extends ChatComposer {
reset(channel, thread) {
this.message = ChatMessage.createDraftMessage(channel, {
user: this.currentUser,
thread,
});
this.message.thread = thread;
}
}
@@ -1,13 +1,12 @@
import { tracked } from "@glimmer/tracking";
import Service, { inject as service } from "@ember/service";
import { action } from "@ember/object";
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
export default class ChatComposer extends Service {
@service chat;
@service currentUser;
@tracked _message;
@tracked message;
@action
cancel() {
@@ -18,13 +17,6 @@ export default class ChatComposer extends Service {
}
}
@action
reset(channel) {
this.message = ChatMessage.createDraftMessage(channel, {
user: this.currentUser,
});
}
@action
clear() {
this.message.message = "";
@@ -41,12 +33,4 @@ export default class ChatComposer extends Service {
onCancelEditing() {
this.reset();
}
get message() {
return this._message;
}
set message(message) {
this._message = message;
}
}
@@ -0,0 +1,25 @@
import Service from "@ember/service";
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
export default class ChatDraftsManager extends Service {
drafts = {};
add(message) {
if (message instanceof ChatMessage) {
this.drafts[message.channel.id] = message;
} else {
throw new Error("message must be an instance of ChatMessage");
}
}
get({ channelId }) {
return this.drafts[channelId];
}
remove({ channelId }) {
delete this.drafts[channelId];
}
reset() {
this.drafts = {};
}
}
@@ -27,6 +27,7 @@ export default class Chat extends Service {
@service chatNotificationManager;
@service chatSubscriptionsManager;
@service chatStateManager;
@service chatDraftsManager;
@service presence;
@service router;
@service site;
@@ -167,19 +168,18 @@ export default class Chat extends Service {
[...channels.public_channels, ...channels.direct_message_channels].forEach(
(channelObject) => {
const channel = this.chatChannelsManager.store(channelObject);
const storedDraft = (this.currentUser?.chat_drafts || []).find(
(draft) => draft.channel_id === channel.id
);
if (this.currentUser.chat_drafts) {
const storedDraft = this.currentUser.chat_drafts.find(
(draft) => draft.channel_id === channel.id
);
channel.draft = ChatMessage.createDraftMessage(
channel,
Object.assign(
{
user: this.currentUser,
},
storedDraft ? JSON.parse(storedDraft.data) : {}
if (storedDraft) {
this.chatDraftsManager.add(
ChatMessage.createDraftMessage(
channel,
Object.assign(
{ user: this.currentUser },
JSON.parse(storedDraft.data)
)
)
);
}