diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-composer.hbs b/plugins/chat/assets/javascripts/discourse/components/chat-composer.hbs
index 6bf6f972012..3b401ab1ea9 100644
--- a/plugins/chat/assets/javascripts/discourse/components/chat-composer.hbs
+++ b/plugins/chat/assets/javascripts/discourse/components/chat-composer.hbs
@@ -93,7 +93,9 @@
{{#if this.shouldRenderReplyingIndicator}}
{{#if this.shouldRender}}
diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-replying-indicator.js b/plugins/chat/assets/javascripts/discourse/components/chat-replying-indicator.js
index 056c1c64a3a..9aad844a5d4 100644
--- a/plugins/chat/assets/javascripts/discourse/components/chat-replying-indicator.js
+++ b/plugins/chat/assets/javascripts/discourse/components/chat-replying-indicator.js
@@ -10,7 +10,6 @@ export default class ChatReplyingIndicator extends Component {
@service presence;
@tracked presenceChannel = null;
- @tracked users = [];
@action
async updateSubscription() {
@@ -20,31 +19,29 @@ export default class ChatReplyingIndicator extends Component {
@action
async subscribe() {
- this.presenceChannel = this.presence.getChannel(this.channelName);
+ this.presenceChannel = this.presence.getChannel(
+ this.args.presenceChannelName
+ );
await this.presenceChannel.subscribe();
- this.users = this.presenceChannel.users || [];
- this.presenceChannel.on("change", this.handlePresenceChange);
}
@action
async unsubscribe() {
- this.users = [];
-
- if (this.presenceChannel.subscribed) {
- this.presenceChannel.off("change", this.handlePresenceChange);
+ if (this.presenceChannel?.subscribed) {
await this.presenceChannel.unsubscribe();
}
}
- @action
- handlePresenceChange(presenceChannel) {
- this.users = presenceChannel.users || [];
+ get users() {
+ return (
+ this.presenceChannel
+ ?.get("users")
+ ?.filter((u) => u.id !== this.currentUser.id) || []
+ );
}
get usernames() {
- return this.users
- .filter((u) => u.id !== this.currentUser.id)
- .mapBy("username");
+ return this.users.mapBy("username");
}
get text() {
@@ -77,8 +74,4 @@ export default class ChatReplyingIndicator extends Component {
get shouldRender() {
return isPresent(this.usernames);
}
-
- get channelName() {
- return `/chat-reply/${this.args.channel.id}`;
- }
}
diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-thread.hbs b/plugins/chat/assets/javascripts/discourse/components/chat-thread.hbs
index 976cdc33bdf..c2789dc3dd0 100644
--- a/plugins/chat/assets/javascripts/discourse/components/chat-thread.hbs
+++ b/plugins/chat/assets/javascripts/discourse/components/chat-thread.hbs
@@ -60,6 +60,7 @@
{{else}}
diff --git a/plugins/chat/assets/javascripts/discourse/components/chat/composer/channel.js b/plugins/chat/assets/javascripts/discourse/components/chat/composer/channel.js
index ab17c22186d..26f5ea2ebd3 100644
--- a/plugins/chat/assets/javascripts/discourse/components/chat/composer/channel.js
+++ b/plugins/chat/assets/javascripts/discourse/components/chat/composer/channel.js
@@ -12,6 +12,11 @@ export default class ChatComposerChannel extends ChatComposer {
composerId = "channel-composer";
+ get presenceChannelName() {
+ const channel = this.args.channel;
+ return `/chat-reply/${channel.id}`;
+ }
+
@action
persistDraft() {
if (this.args.channel?.isDraft) {
diff --git a/plugins/chat/assets/javascripts/discourse/components/chat/composer/thread.js b/plugins/chat/assets/javascripts/discourse/components/chat/composer/thread.js
index e1a1086035e..fdb94e77b03 100644
--- a/plugins/chat/assets/javascripts/discourse/components/chat/composer/thread.js
+++ b/plugins/chat/assets/javascripts/discourse/components/chat/composer/thread.js
@@ -13,6 +13,10 @@ export default class ChatComposerThread extends ChatComposer {
composerId = "thread-composer";
+ get presenceChannelName() {
+ return `/chat-reply/${this.args.channel.id}/thread/${this.args.thread.id}`;
+ }
+
get placeholder() {
return I18n.t("chat.placeholder_thread");
}
diff --git a/plugins/chat/assets/javascripts/discourse/services/chat-composer-presence-manager.js b/plugins/chat/assets/javascripts/discourse/services/chat-composer-presence-manager.js
index 57ddd682dbf..25c0490b6c4 100644
--- a/plugins/chat/assets/javascripts/discourse/services/chat-composer-presence-manager.js
+++ b/plugins/chat/assets/javascripts/discourse/services/chat-composer-presence-manager.js
@@ -2,7 +2,6 @@ import Service, { inject as service } from "@ember/service";
import { cancel, debounce } from "@ember/runloop";
import { isTesting } from "discourse-common/config/environment";
-const CHAT_PRESENCE_CHANNEL_PREFIX = "/chat-reply";
const KEEP_ALIVE_DURATION_SECONDS = 10;
// This service is loosely based on discourse-presence's ComposerPresenceManager service
@@ -16,15 +15,15 @@ export default class ChatComposerPresenceManager extends Service {
this.leave();
}
- notifyState(chatChannelId, replying) {
+ notifyState(channelName, replying) {
if (!replying) {
this.leave();
return;
}
- if (this._chatChannelId !== chatChannelId) {
- this._enter(chatChannelId);
- this._chatChannelId = chatChannelId;
+ if (this._channelName !== channelName) {
+ this._enter(channelName);
+ this._channelName = channelName;
}
if (!isTesting()) {
@@ -39,17 +38,16 @@ export default class ChatComposerPresenceManager extends Service {
leave() {
this._presentChannel?.leave();
this._presentChannel = null;
- this._chatChannelId = null;
+ this._channelName = null;
if (this._autoLeaveTimer) {
cancel(this._autoLeaveTimer);
this._autoLeaveTimer = null;
}
}
- _enter(chatChannelId) {
+ _enter(channelName) {
this.leave();
- let channelName = `${CHAT_PRESENCE_CHANNEL_PREFIX}/${chatChannelId}`;
this._presentChannel = this.presence.getChannel(channelName);
this._presentChannel.enter();
}
diff --git a/plugins/chat/assets/stylesheets/common/chat-thread.scss b/plugins/chat/assets/stylesheets/common/chat-thread.scss
index cbe7f8d58f1..fa2c3e4c822 100644
--- a/plugins/chat/assets/stylesheets/common/chat-thread.scss
+++ b/plugins/chat/assets/stylesheets/common/chat-thread.scss
@@ -27,8 +27,4 @@
display: flex;
flex-direction: column;
}
-
- .chat-composer__wrapper {
- padding-bottom: 27px;
- }
}
diff --git a/plugins/chat/test/javascripts/components/chat-replying-indicator-test.js b/plugins/chat/test/javascripts/components/chat-replying-indicator-test.js
index 8fd4e68204c..e63fa273e39 100644
--- a/plugins/chat/test/javascripts/components/chat-replying-indicator-test.js
+++ b/plugins/chat/test/javascripts/components/chat-replying-indicator-test.js
@@ -4,35 +4,81 @@ import hbs from "htmlbars-inline-precompile";
import fabricators from "../helpers/fabricators";
import { module, test } from "qunit";
import { render } from "@ember/test-helpers";
-import { joinChannel } from "discourse/tests/helpers/presence-pretender";
+import {
+ joinChannel,
+ leaveChannel,
+} from "discourse/tests/helpers/presence-pretender";
-async function addUserToChannel(channelId, id, username) {
- await joinChannel(`/chat-reply/${channelId}`, {
+async function addUser(id, username, channelName = "/chat-reply/1") {
+ await joinChannel(channelName, {
id,
avatar_template: "/images/avatar.png",
username,
});
}
+async function removeUser(id, channelName = "/chat-reply/1") {
+ await leaveChannel(channelName, {
+ id,
+ });
+}
+
module(
"Discourse Chat | Component | chat-replying-indicator",
function (hooks) {
setupRenderingTest(hooks);
test("not displayed when no one is replying", async function (assert) {
- this.channel = fabricators.chatChannel();
-
- await render(hbs`
`);
+ await render(
+ hbs`
`
+ );
assert.dom(".chat-replying-indicator__text").doesNotExist();
});
+ test("working for thread", async function (assert) {
+ await render(
+ hbs`
`
+ );
+
+ await addUser(1, "sam", "/chat-reply/1/thread/1");
+
+ assert.strictEqual(
+ query(".chat-replying-indicator__text").innerText,
+ "sam is typing"
+ );
+ });
+
+ test("doesn’t leak in other indicators", async function (assert) {
+ await render(
+ hbs`
+
+
+ `
+ );
+
+ await addUser(1, "sam");
+
+ assert
+ .dom(".channel .chat-replying-indicator__text")
+ .hasText("sam is typing");
+ assert.dom(".thread .chat-replying-indicator__text").doesNotExist();
+
+ await addUser(2, "mark", "/chat-reply/1/thread/1");
+ await removeUser(1);
+
+ assert.dom(".channel .chat-replying-indicator__text").doesNotExist();
+ assert
+ .dom(".thread .chat-replying-indicator__text")
+ .hasText("mark is typing");
+ });
+
test("displays indicator when user is replying", async function (assert) {
- this.channel = fabricators.chatChannel();
+ await render(
+ hbs`
`
+ );
- await render(hbs`
`);
-
- await addUserToChannel(1, 1, "sam");
+ await addUser(1, "sam");
assert.strictEqual(
query(".chat-replying-indicator__text").innerText,
@@ -43,10 +89,12 @@ module(
test("displays indicator when 2 or 3 users are replying", async function (assert) {
this.channel = fabricators.chatChannel();
- await render(hbs`
`);
+ await render(
+ hbs`
`
+ );
- await addUserToChannel(1, 1, "sam");
- await addUserToChannel(1, 2, "mark");
+ await addUser(1, "sam");
+ await addUser(2, "mark");
assert
.dom(".chat-replying-indicator__text")
@@ -56,11 +104,13 @@ module(
test("displays indicator when 3 users are replying", async function (assert) {
this.channel = fabricators.chatChannel();
- await render(hbs`
`);
+ await render(
+ hbs`
`
+ );
- await addUserToChannel(1, 1, "sam");
- await addUserToChannel(1, 2, "mark");
- await addUserToChannel(1, 3, "joffrey");
+ await addUser(1, "sam");
+ await addUser(2, "mark");
+ await addUser(3, "joffrey");
assert
.dom(".chat-replying-indicator__text")
@@ -70,12 +120,14 @@ module(
test("displays indicator when more than 3 users are replying", async function (assert) {
this.channel = fabricators.chatChannel();
- await render(hbs`
`);
+ await render(
+ hbs`
`
+ );
- await addUserToChannel(1, 1, "sam");
- await addUserToChannel(1, 2, "mark");
- await addUserToChannel(1, 3, "joffrey");
- await addUserToChannel(1, 4, "taylor");
+ await addUser(1, "sam");
+ await addUser(2, "mark");
+ await addUser(3, "joffrey");
+ await addUser(4, "taylor");
assert
.dom(".chat-replying-indicator__text")
@@ -85,24 +137,28 @@ module(
test("filters current user from list of repliers", async function (assert) {
this.channel = fabricators.chatChannel();
- await render(hbs`
`);
+ await render(
+ hbs`
`
+ );
- await addUserToChannel(1, 1, "sam");
- await addUserToChannel(1, this.currentUser.id, this.currentUser.username);
+ await addUser(1, "sam");
+ await addUser(this.currentUser.id, this.currentUser.username);
assert.dom(".chat-replying-indicator__text").hasText("sam is typing");
});
test("resets presence when channel changes", async function (assert) {
- this.set("channel", fabricators.chatChannel());
+ this.set("presenceChannelName", "/chat-reply/1");
- await addUserToChannel(1, 1, "sam");
+ await addUser(1, "sam");
- await render(hbs`
`);
+ await render(
+ hbs`
`
+ );
assert.dom(".chat-replying-indicator__text").hasText("sam is typing");
- this.set("channel", fabricators.chatChannel({ id: 2 }));
+ this.set("presenceChannelName", "/chat-reply/2");
assert.dom(".chat-replying-indicator__text").doesNotExist();
});