diff --git a/app/assets/javascripts/discourse/app/components/composer-messages.js b/app/assets/javascripts/discourse/app/components/composer-messages.js
index f400e139c5e..e4936b6410e 100644
--- a/app/assets/javascripts/discourse/app/components/composer-messages.js
+++ b/app/assets/javascripts/discourse/app/components/composer-messages.js
@@ -1,110 +1,76 @@
import Component from "@ember/component";
+import { classNameBindings } from "@ember-decorators/component";
import EmberObject, { action } from "@ember/object";
import I18n from "I18n";
import LinkLookup from "discourse/lib/link-lookup";
import { not } from "@ember/object/computed";
-import { scheduleOnce } from "@ember/runloop";
import showModal from "discourse/lib/show-modal";
import { ajax } from "discourse/lib/ajax";
let _messagesCache = {};
let _recipient_names = [];
-export default Component.extend({
- classNameBindings: [":composer-popup-container", "hidden"],
- checkedMessages: false,
- messages: null,
- messagesByTemplate: null,
- queuedForTyping: null,
- _lastSimilaritySearch: null,
- _similarTopicsMessage: null,
- _yourselfConfirm: null,
- similarTopics: null,
- usersNotSeen: null,
+@classNameBindings(":composer-popup-container", "hidden")
+export default class ComposerMessages extends Component {
+ checkedMessages = false;
+ messages = null;
+ messagesByTemplate = null;
+ queuedForTyping = null;
+ similarTopics = null;
+ usersNotSeen = null;
- hidden: not("composer.viewOpenOrFullscreen"),
+ @not("composer.viewOpenOrFullscreen") hidden;
+
+ _lastSimilaritySearch = null;
+ _similarTopicsMessage = null;
didInsertElement() {
- this._super(...arguments);
+ super.didInsertElement(...arguments);
+
this.appEvents.on("composer:typed-reply", this, this._typedReply);
this.appEvents.on("composer:opened", this, this._findMessages);
this.appEvents.on("composer:find-similar", this, this._findSimilar);
this.appEvents.on("composer-messages:close", this, this._closeTop);
this.appEvents.on("composer-messages:create", this, this._create);
- scheduleOnce("afterRender", this, this.reset);
- },
+ this.reset();
+ }
willDestroyElement() {
+ super.willDestroyElement(...arguments);
+
this.appEvents.off("composer:typed-reply", this, this._typedReply);
this.appEvents.off("composer:opened", this, this._findMessages);
this.appEvents.off("composer:find-similar", this, this._findSimilar);
this.appEvents.off("composer-messages:close", this, this._closeTop);
this.appEvents.off("composer-messages:create", this, this._create);
- },
+ }
_closeTop() {
- const messages = this.messages;
- messages.popObject();
- this.set("messageCount", messages.get("length"));
- },
+ if (this.isDestroying || this.isDestroyed) {
+ return;
+ }
+
+ this.messages.popObject();
+ this.set("messageCount", this.messages.length);
+ }
_removeMessage(message) {
- const messages = this.messages;
- messages.removeObject(message);
- this.set("messageCount", messages.get("length"));
- },
+ this.messages.removeObject(message);
+ this.set("messageCount", this.messages.length);
+ }
- @action
- closeMessage(message, event) {
- event?.preventDefault();
- this._removeMessage(message);
- },
+ _create(info) {
+ if (this.isDestroying || this.isDestroyed) {
+ return;
+ }
- actions: {
- hideMessage(message) {
- this._removeMessage(message);
- // kind of hacky but the visibility depends on this
- this.messagesByTemplate[message.get("templateName")] = undefined;
- },
-
- popup(message) {
- const messagesByTemplate = this.messagesByTemplate;
- const templateName = message.get("templateName");
-
- if (!messagesByTemplate[templateName]) {
- const messages = this.messages;
- messages.pushObject(message);
- this.set("messageCount", messages.get("length"));
- messagesByTemplate[templateName] = message;
- }
- },
-
- shareModal() {
- const { topic } = this.composer;
- const controller = showModal("share-topic", { model: topic.category });
- controller.setProperties({
- allowInvites:
- topic.details.can_invite_to &&
- !topic.archived &&
- !topic.closed &&
- !topic.deleted,
- topic,
- });
- },
-
- switchPM(message) {
- this.composer.set("action", "privateMessage");
- this.composer.set("targetRecipients", message.reply_username);
- this._removeMessage(message);
- },
- },
+ this.reset();
+ this.popup(EmberObject.create(info));
+ }
// Resets all active messages.
// For example if composing a new post.
reset() {
- if (this.isDestroying || this.isDestroyed) {
- return;
- }
this.setProperties({
messages: [],
messagesByTemplate: {},
@@ -112,19 +78,41 @@ export default Component.extend({
checkedMessages: false,
similarTopics: [],
});
- },
+ }
// Called after the user has typed a reply.
// Some messages only get shown after being typed.
- _typedReply() {
+ async _typedReply() {
if (this.isDestroying || this.isDestroyed) {
return;
}
- const composer = this.composer;
- if (composer.get("privateMessage")) {
- const recipients = composer.targetRecipientsArray;
- const recipient_names = recipients
+ for (const msg of this.queuedForTyping) {
+ if (this.composer.whisper && msg.hide_if_whisper) {
+ return;
+ }
+
+ this.popup(msg);
+ }
+
+ if (this.composer.privateMessage) {
+ if (
+ this.composer.targetRecipientsArray.length > 0 &&
+ this.composer.targetRecipientsArray.every(
+ (r) => r.name === this.currentUser.username
+ )
+ ) {
+ const message = this.composer.store.createRecord("composer-message", {
+ id: "yourself_confirm",
+ templateName: "education",
+ title: I18n.t("composer.yourself_confirm.title"),
+ body: I18n.t("composer.yourself_confirm.body"),
+ });
+
+ this.popup(message);
+ }
+
+ const recipient_names = this.composer.targetRecipientsArray
.filter((r) => r.type === "user")
.map(({ name }) => name);
@@ -135,83 +123,69 @@ export default Component.extend({
) {
_recipient_names = recipient_names;
- ajax(`/composer_messages/user_not_seen_in_a_while`, {
- type: "GET",
- data: {
- usernames: recipient_names,
- },
- }).then((response) => {
- if (
- response.user_count > 0 &&
- this.get("usersNotSeen") !== response.usernames.join("-")
- ) {
- this.set("usersNotSeen", response.usernames.join("-"));
- this.messagesByTemplate["education"] = undefined;
-
- let usernames = [];
- response.usernames.forEach((username, index) => {
- usernames[
- index
- ] = `@${username}`;
- });
-
- let body_key = "composer.user_not_seen_in_a_while.single";
- if (response.user_count > 1) {
- body_key = "composer.user_not_seen_in_a_while.multiple";
- }
- const message = composer.store.createRecord("composer-message", {
- id: "user-not-seen",
- templateName: "education",
- body: I18n.t(body_key, {
- usernames: usernames.join(", "),
- time_ago: response.time_ago,
- }),
- });
- this.send("popup", message);
+ const response = await ajax(
+ `/composer_messages/user_not_seen_in_a_while`,
+ {
+ type: "GET",
+ data: {
+ usernames: recipient_names,
+ },
}
- });
- }
+ );
- if (
- recipients.length > 0 &&
- recipients.every((r) => r.name === this.currentUser.get("username"))
- ) {
- const message =
- this._yourselfConfirm ||
- composer.store.createRecord("composer-message", {
- id: "yourself_confirm",
- templateName: "education",
- title: I18n.t("composer.yourself_confirm.title"),
- body: I18n.t("composer.yourself_confirm.body"),
+ if (this.isDestroying || this.isDestroyed) {
+ return;
+ }
+
+ if (
+ response.user_count > 0 &&
+ this.usersNotSeen !== response.usernames.join("-")
+ ) {
+ this.set("usersNotSeen", response.usernames.join("-"));
+ this.messagesByTemplate["education"] = undefined;
+
+ let usernames = [];
+ response.usernames.forEach((username, index) => {
+ usernames[
+ index
+ ] = `@${username}`;
});
- this.send("popup", message);
+
+ let body_key;
+ if (response.user_count === 1) {
+ body_key = "composer.user_not_seen_in_a_while.single";
+ } else {
+ body_key = "composer.user_not_seen_in_a_while.multiple";
+ }
+
+ const message = this.composer.store.createRecord("composer-message", {
+ id: "user-not-seen",
+ templateName: "education",
+ body: I18n.t(body_key, {
+ usernames: usernames.join(", "),
+ time_ago: response.time_ago,
+ }),
+ });
+
+ this.popup(message);
+ }
}
}
+ }
- this.queuedForTyping.forEach((msg) => {
- if (composer.whisper && msg.hide_if_whisper) {
- return;
- }
- this.send("popup", msg);
- });
- },
-
- _create(info) {
- this.reset();
- this.send("popup", EmberObject.create(info));
- },
-
- _findSimilar() {
- const composer = this.composer;
-
- // We don't care about similar topics unless creating a topic
- if (!composer.get("creatingTopic")) {
+ async _findSimilar() {
+ if (this.isDestroying || this.isDestroyed) {
return;
}
- // TODO pass the 200 in from somewhere
- const raw = (composer.get("reply") || "").slice(0, 200);
- const title = composer.get("title") || "";
+ // We don't care about similar topics unless creating a topic
+ if (!this.composer.creatingTopic) {
+ return;
+ }
+
+ // TODO: pass the 200 in from somewhere
+ const raw = (this.composer.reply || "").slice(0, 200);
+ const title = this.composer.title || "";
// Ensure we have at least a title
if (title.length < this.siteSettings.min_title_similar_length) {
@@ -223,83 +197,132 @@ export default Component.extend({
if (concat === this._lastSimilaritySearch) {
return;
}
- this._lastSimilaritySearch = concat;
- const similarTopics = this.similarTopics;
- const message =
- this._similarTopicsMessage ||
- composer.store.createRecord("composer-message", {
+ this._lastSimilaritySearch = concat;
+ this._similarTopicsMessage ||= this.composer.store.createRecord(
+ "composer-message",
+ {
id: "similar_topics",
templateName: "similar-topics",
extraClass: "similar-topics",
- });
-
- this._similarTopicsMessage = message;
-
- composer.store.find("similar-topic", { title, raw }).then((topics) => {
- if (this.isDestroying || this.isDestroyed) {
- return;
}
+ );
- similarTopics.clear();
- similarTopics.pushObjects(topics.get("content"));
-
- if (similarTopics.get("length") > 0) {
- message.set("similarTopics", similarTopics);
- this.send("popup", message);
- } else if (message) {
- this.send("hideMessage", message);
- }
+ const topics = await this.composer.store.find("similar-topic", {
+ title,
+ raw,
});
- },
+
+ if (this.isDestroying || this.isDestroyed) {
+ return;
+ }
+
+ this.similarTopics.clear();
+ this.similarTopics.pushObjects(topics.content);
+
+ if (this.similarTopics.length > 0) {
+ this._similarTopicsMessage.set("similarTopics", this.similarTopics);
+ this.popup(this._similarTopicsMessage);
+ } else if (this._similarTopicsMessage) {
+ this.hideMessage(this._similarTopicsMessage);
+ }
+ }
// Figure out if there are any messages that should be displayed above the composer.
- _findMessages() {
+ async _findMessages() {
+ if (this.isDestroying || this.isDestroyed) {
+ return;
+ }
+
if (this.checkedMessages) {
return;
}
- const composer = this.composer;
- const args = { composer_action: composer.get("action") };
- const topicId = composer.get("topic.id");
- const postId = composer.get("post.id");
+ const args = { composer_action: this.composer.action };
+ const topicId = this.composer.topic?.id;
+ const postId = this.composer.post?.id;
if (topicId) {
args.topic_id = topicId;
}
+
if (postId) {
args.post_id = postId;
}
const cacheKey = `${args.composer_action}${args.topic_id}${args.post_id}`;
- const processMessages = (messages) => {
+ let messages;
+ if (_messagesCache.cacheKey === cacheKey) {
+ messages = _messagesCache.messages;
+ } else {
+ messages = await this.composer.store.find("composer-message", args);
if (this.isDestroying || this.isDestroyed) {
return;
}
- // Checking composer messages on replies can give us a list of links to check for
- // duplicates
- if (messages.extras && messages.extras.duplicate_lookup) {
- this.addLinkLookup(new LinkLookup(messages.extras.duplicate_lookup));
- }
-
- this.set("checkedMessages", true);
- const queuedForTyping = this.queuedForTyping;
- messages.forEach((msg) =>
- msg.wait_for_typing
- ? queuedForTyping.addObject(msg)
- : this.send("popup", msg)
- );
- };
-
- if (_messagesCache.cacheKey === cacheKey) {
- processMessages(_messagesCache.messages);
- } else {
- composer.store.find("composer-message", args).then((messages) => {
- _messagesCache = { messages, cacheKey };
- processMessages(messages);
- });
+ _messagesCache = { messages, cacheKey };
}
- },
-});
+
+ // Checking composer messages on replies can give us a list of links to check for
+ // duplicates
+ if (messages.extras?.duplicate_lookup) {
+ this.addLinkLookup(new LinkLookup(messages.extras.duplicate_lookup));
+ }
+
+ this.set("checkedMessages", true);
+
+ for (const msg of messages) {
+ if (msg.wait_for_typing) {
+ this.queuedForTyping.addObject(msg);
+ } else {
+ this.popup(msg);
+ }
+ }
+ }
+
+ @action
+ closeMessage(message, event) {
+ event?.preventDefault();
+ this._removeMessage(message);
+ }
+
+ @action
+ hideMessage(message) {
+ this._removeMessage(message);
+
+ // kind of hacky but the visibility depends on this
+ this.messagesByTemplate[message.templateName] = undefined;
+ }
+
+ @action
+ popup(message) {
+ if (!this.messagesByTemplate[message.templateName]) {
+ this.messages.pushObject(message);
+ this.set("messageCount", this.messages.length);
+ this.messagesByTemplate[message.templateName] = message;
+ }
+ }
+
+ @action
+ shareModal() {
+ const { topic } = this.composer;
+ const controller = showModal("share-topic", { model: topic.category });
+
+ controller.setProperties({
+ allowInvites:
+ topic.details.can_invite_to &&
+ !topic.archived &&
+ !topic.closed &&
+ !topic.deleted,
+ topic,
+ });
+ }
+
+ @action
+ switchPM(message) {
+ this.composer.set("action", "privateMessage");
+ this.composer.set("targetRecipients", message.reply_username);
+ this._removeMessage(message);
+ }
+}