diff --git a/plugins/chat/assets/javascripts/discourse/components/collapser.hbs b/plugins/chat/assets/javascripts/discourse/components/collapser.hbs
index 180186ee62..7bee61a37b 100644
--- a/plugins/chat/assets/javascripts/discourse/components/collapser.hbs
+++ b/plugins/chat/assets/javascripts/discourse/components/collapser.hbs
@@ -16,6 +16,11 @@
{{/if}}
- {{yield}}
+
+ {{yield this.collapsed}}
\ No newline at end of file
diff --git a/plugins/chat/assets/javascripts/discourse/components/collapser.js b/plugins/chat/assets/javascripts/discourse/components/collapser.js
index f7d05cf837..db6a355a6a 100644
--- a/plugins/chat/assets/javascripts/discourse/components/collapser.js
+++ b/plugins/chat/assets/javascripts/discourse/components/collapser.js
@@ -6,14 +6,17 @@ export default Component.extend({
collapsed: false,
header: null,
+ onToggle: null,
@action
open() {
this.set("collapsed", false);
+ this.onToggle?.(false);
},
@action
close() {
this.set("collapsed", true);
+ this.onToggle?.(true);
},
});
diff --git a/plugins/chat/assets/javascripts/discourse/models/chat-message.js b/plugins/chat/assets/javascripts/discourse/models/chat-message.js
index a7e5685bdc..ed8d519e81 100644
--- a/plugins/chat/assets/javascripts/discourse/models/chat-message.js
+++ b/plugins/chat/assets/javascripts/discourse/models/chat-message.js
@@ -47,11 +47,13 @@ export default class ChatMessage {
@tracked availableFlags;
@tracked newest = false;
@tracked highlighted = false;
+ @tracked firstOfResults = false;
constructor(channel, args = {}) {
this.channel = channel;
this.id = args.id;
this.newest = args.newest;
+ this.firstOfResults = args.firstOfResults;
this.staged = args.staged;
this.edited = args.edited;
this.availableFlags = args.available_flags;
diff --git a/plugins/chat/assets/javascripts/discourse/modifiers/chat/did-mutate-childlist.js b/plugins/chat/assets/javascripts/discourse/modifiers/chat/did-mutate-childlist.js
new file mode 100644
index 0000000000..5db323f49c
--- /dev/null
+++ b/plugins/chat/assets/javascripts/discourse/modifiers/chat/did-mutate-childlist.js
@@ -0,0 +1,24 @@
+import Modifier from "ember-modifier";
+import { registerDestructor } from "@ember/destroyable";
+
+export default class ChatDidMutateChildlist extends Modifier {
+ constructor(owner, args) {
+ super(owner, args);
+ registerDestructor(this, (instance) => instance.cleanup());
+ }
+
+ modify(element, [callback]) {
+ this.mutationObserver = new MutationObserver(() => {
+ callback();
+ });
+
+ this.mutationObserver.observe(element, {
+ childList: true,
+ subtree: true,
+ });
+ }
+
+ cleanup() {
+ this.mutationObserver?.disconnect();
+ }
+}
diff --git a/plugins/chat/assets/javascripts/discourse/modifiers/chat/on-throttled-scroll.js b/plugins/chat/assets/javascripts/discourse/modifiers/chat/on-throttled-scroll.js
new file mode 100644
index 0000000000..212493e53f
--- /dev/null
+++ b/plugins/chat/assets/javascripts/discourse/modifiers/chat/on-throttled-scroll.js
@@ -0,0 +1,36 @@
+import Modifier from "ember-modifier";
+import { registerDestructor } from "@ember/destroyable";
+import { cancel, throttle } from "@ember/runloop";
+import { bind } from "discourse-common/utils/decorators";
+
+export default class ChatOnThrottledScroll extends Modifier {
+ constructor(owner, args) {
+ super(owner, args);
+ registerDestructor(this, (instance) => instance.cleanup());
+ }
+
+ modify(element, [callback, options]) {
+ this.element = element;
+ this.callback = callback;
+ this.options = options;
+ this.element.addEventListener("scroll", this.throttledCallback, {
+ passive: true,
+ });
+ }
+
+ @bind
+ throttledCallback(event) {
+ this.throttledHandler = throttle(
+ this,
+ this.callback,
+ event,
+ this.options.delay ?? 100,
+ this.options.immediate ?? false
+ );
+ }
+
+ cleanup() {
+ cancel(this.throttledHandler);
+ this.element.removeEventListener("scroll", this.throttledCallback);
+ }
+}
diff --git a/plugins/chat/assets/javascripts/discourse/services/chat-api.js b/plugins/chat/assets/javascripts/discourse/services/chat-api.js
index 4b51143681..44ad75dc5c 100644
--- a/plugins/chat/assets/javascripts/discourse/services/chat-api.js
+++ b/plugins/chat/assets/javascripts/discourse/services/chat-api.js
@@ -243,7 +243,7 @@ export default class ChatApi extends Service {
* @param {integer} data.pageSize - Max number of messages to fetch.
* @returns {Promise}
*/
- async messages(channelId, data = {}) {
+ messages(channelId, data = {}) {
let path;
const args = {};
diff --git a/plugins/chat/assets/javascripts/discourse/services/chat-channels-manager.js b/plugins/chat/assets/javascripts/discourse/services/chat-channels-manager.js
index 47a4fb88d8..0905f46802 100644
--- a/plugins/chat/assets/javascripts/discourse/services/chat-channels-manager.js
+++ b/plugins/chat/assets/javascripts/discourse/services/chat-channels-manager.js
@@ -134,6 +134,10 @@ export default class ChatChannelsManager extends Service {
}
#cache(channel) {
+ if (!channel) {
+ return;
+ }
+
this._cached[channel.id] = channel;
}
diff --git a/plugins/chat/assets/stylesheets/common/chat-message-separator.scss b/plugins/chat/assets/stylesheets/common/chat-message-separator.scss
index 6b43783391..94c5aca2ff 100644
--- a/plugins/chat/assets/stylesheets/common/chat-message-separator.scss
+++ b/plugins/chat/assets/stylesheets/common/chat-message-separator.scss
@@ -44,7 +44,7 @@
justify-content: center;
pointer-events: none;
- &.last-visit {
+ &.with-last-visit {
& + .chat-message-separator__line-container {
.chat-message-separator__line {
border-color: var(--danger-medium);
@@ -57,11 +57,16 @@
position: sticky;
top: -1px;
- &.is-pinned {
+ &.is-pinned,
+ &.is-force-pinned {
.chat-message-separator__text {
border: 1px solid var(--primary-medium);
border-radius: 3px;
}
+
+ .chat-message-separator__last-visit {
+ display: none;
+ }
}
}
diff --git a/plugins/chat/assets/stylesheets/common/common.scss b/plugins/chat/assets/stylesheets/common/common.scss
index 4ef4c7cf17..e6f8e3fbf5 100644
--- a/plugins/chat/assets/stylesheets/common/common.scss
+++ b/plugins/chat/assets/stylesheets/common/common.scss
@@ -145,9 +145,13 @@ $float-height: 530px;
word-wrap: break-word;
white-space: normal;
position: relative;
+ will-change: transform;
+ transform: translateZ(0);
.chat-message-container {
display: grid;
+ will-change: transform;
+ transform: translateZ(0);
&.selecting-messages {
grid-template-columns: 1.5em 1fr;
@@ -332,7 +336,7 @@ $float-height: 530px;
position: absolute;
z-index: 1;
flex-direction: column;
- bottom: -75px;
+ bottom: -25px;
background: none;
opacity: 0;
transition: opacity 0.25s ease, transform 0.5s ease;
@@ -350,7 +354,7 @@ $float-height: 530px;
}
&.visible {
- transform: translateY(-75px) scale(1);
+ transform: translateY(-32px) scale(1);
opacity: 0.8;
}
diff --git a/plugins/chat/spec/system/bookmark_message_spec.rb b/plugins/chat/spec/system/bookmark_message_spec.rb
index e8afe05c91..1f62d0ebf6 100644
--- a/plugins/chat/spec/system/bookmark_message_spec.rb
+++ b/plugins/chat/spec/system/bookmark_message_spec.rb
@@ -52,7 +52,6 @@ RSpec.describe "Bookmark message", type: :system, js: true do
context "when mobile", mobile: true do
it "allows to bookmark a message" do
chat.visit_channel(category_channel_1)
- expect(channel).to have_no_loading_skeleton
i = 0.5
try_until_success(timeout: 20) do
diff --git a/plugins/chat/spec/system/chat_channel_spec.rb b/plugins/chat/spec/system/chat_channel_spec.rb
index 34d6a933f5..b2b8369397 100644
--- a/plugins/chat/spec/system/chat_channel_spec.rb
+++ b/plugins/chat/spec/system/chat_channel_spec.rb
@@ -35,7 +35,6 @@ RSpec.describe "Chat channel", type: :system, js: true do
it "allows to edit this message once persisted" do
chat.visit_channel(channel_1)
- expect(channel).to have_no_loading_skeleton
channel.send_message("aaaaaaaaaaaaaaaaaaaa")
expect(page).to have_no_css(".chat-message-staged")
last_message = find(".chat-message-container:last-child")
diff --git a/plugins/chat/spec/system/deleted_message_spec.rb b/plugins/chat/spec/system/deleted_message_spec.rb
index 8bea5d4a17..aa6d4f9237 100644
--- a/plugins/chat/spec/system/deleted_message_spec.rb
+++ b/plugins/chat/spec/system/deleted_message_spec.rb
@@ -16,7 +16,6 @@ RSpec.describe "Deleted message", type: :system, js: true do
context "when deleting a message" do
it "shows as deleted" do
chat_page.visit_channel(channel_1)
- expect(channel_page).to have_no_loading_skeleton
channel_page.send_message("aaaaaaaaaaaaaaaaaaaa")
expect(page).to have_no_css(".chat-message-staged")
last_message = find(".chat-message-container:last-child")
diff --git a/plugins/chat/spec/system/hashtag_autocomplete_spec.rb b/plugins/chat/spec/system/hashtag_autocomplete_spec.rb
index 47d4401c7a..a8514631e4 100644
--- a/plugins/chat/spec/system/hashtag_autocomplete_spec.rb
+++ b/plugins/chat/spec/system/hashtag_autocomplete_spec.rb
@@ -25,7 +25,6 @@ describe "Using #hashtag autocompletion to search for and lookup channels",
it "searches for channels, categories, and tags with # and prioritises channels in the results" do
chat_page.visit_channel(channel1)
- expect(chat_channel_page).to have_no_loading_skeleton
chat_channel_page.type_in_composer("this is #ra")
expect(page).to have_css(
".hashtag-autocomplete .hashtag-autocomplete__option .hashtag-autocomplete__link",
@@ -53,7 +52,6 @@ describe "Using #hashtag autocompletion to search for and lookup channels",
it "cooks the hashtags for channels, categories, and tags serverside when the chat message is saved to the database" do
chat_page.visit_channel(channel1)
- expect(chat_channel_page).to have_no_loading_skeleton
chat_channel_page.type_in_composer(
"this is #random and this is #raspberry-beret and this is #razed which is cool",
)
diff --git a/plugins/chat/spec/system/mention_warnings_spec.rb b/plugins/chat/spec/system/mention_warnings_spec.rb
index e217b9bb29..6ad80f2a75 100644
--- a/plugins/chat/spec/system/mention_warnings_spec.rb
+++ b/plugins/chat/spec/system/mention_warnings_spec.rb
@@ -21,7 +21,6 @@ RSpec.describe "Mentions warnings", type: :system, js: true do
it "displays a warning" do
chat_page.visit_channel(channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
chat_channel_page.type_in_composer("@#{admin_mentionable_group.name} ")
expect(page).to have_css(".chat-mention-warnings")
@@ -46,7 +45,6 @@ RSpec.describe "Mentions warnings", type: :system, js: true do
it "displays a warning" do
chat_page.visit_channel(channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
chat_channel_page.type_in_composer("@#{publicly_mentionable_group.name} ")
expect(page).to have_css(".chat-mention-warnings")
@@ -61,7 +59,6 @@ RSpec.describe "Mentions warnings", type: :system, js: true do
it "displays a warning" do
chat_page.visit_channel(channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
chat_channel_page.type_in_composer(
"@#{user_2.username} @#{publicly_mentionable_group.name} ",
)
diff --git a/plugins/chat/spec/system/page_objects/chat/chat.rb b/plugins/chat/spec/system/page_objects/chat/chat.rb
index 0ceceb7373..4ed41f580a 100644
--- a/plugins/chat/spec/system/page_objects/chat/chat.rb
+++ b/plugins/chat/spec/system/page_objects/chat/chat.rb
@@ -19,6 +19,7 @@ module PageObjects
def visit_channel(channel, mobile: false)
visit(channel.url + (mobile ? "?mobile_view=1" : ""))
+ has_no_css?(".not-loaded-once")
has_no_css?(".chat-skeleton")
end
diff --git a/plugins/chat/spec/system/reply_indicator_spec.rb b/plugins/chat/spec/system/reply_indicator_spec.rb
deleted file mode 100644
index bae2eac310..0000000000
--- a/plugins/chat/spec/system/reply_indicator_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "Reply indicator", type: :system, js: true do
- let(:chat_page) { PageObjects::Pages::Chat.new }
- let(:channel_page) { PageObjects::Pages::ChatChannel.new }
-
- fab!(:channel_1) { Fabricate(:category_channel) }
- fab!(:current_user) { Fabricate(:admin) }
-
- before do
- chat_system_bootstrap
- channel_1.add(current_user)
- sign_in(current_user)
- end
-
- context "when clicking on a reply indicator of a loaded message" do
- fab!(:replied_to_message) do
- Fabricate(:chat_message, chat_channel: channel_1, created_at: 2.hours.ago)
- end
- fab!(:reply) do
- Fabricate(
- :chat_message,
- chat_channel: channel_1,
- in_reply_to: replied_to_message,
- created_at: 1.minute.ago,
- )
- end
-
- before do
- 10.times { Fabricate(:chat_message, chat_channel: channel_1, created_at: 1.hour.ago) }
- end
-
- it "highlights the message without refreshing the pane" do
- chat_page.visit_channel(channel_1)
-
- find("[data-id='#{reply.id}'] .chat-reply").click
-
- expect(page).to have_no_selector(".chat-skeleton")
- expect(page).to have_selector("[data-id='#{replied_to_message.id}'].highlighted", wait: 0.1)
- end
- end
-end
diff --git a/plugins/chat/spec/system/replying_indicator_spec.rb b/plugins/chat/spec/system/replying_indicator_spec.rb
deleted file mode 100644
index d2c1dd1a25..0000000000
--- a/plugins/chat/spec/system/replying_indicator_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "Replying indicator", type: :system, js: true do
- fab!(:channel_1) { Fabricate(:chat_channel) }
- fab!(:current_user) { Fabricate(:user) }
- fab!(:other_user) { Fabricate(:user) }
-
- let(:chat) { PageObjects::Pages::Chat.new }
-
- before do
- channel_1.add(current_user)
- channel_1.add(other_user)
- chat_system_bootstrap
- sign_in(current_user)
- end
-
- context "when on a channel" do
- context "when another user is replying" do
- it "shows the replying indicator" do
- using_session(:user_1) do
- sign_in(other_user)
- chat.visit_channel(channel_1)
- find(".chat-composer-input").fill_in(with: "hello there")
- end
-
- chat.visit_channel(channel_1)
-
- expect(page).to have_selector(
- ".chat-replying-indicator",
- text: I18n.t("js.chat.replying_indicator.single_user", username: other_user.username),
- )
- end
- end
- end
-end
diff --git a/plugins/chat/spec/system/transcript_spec.rb b/plugins/chat/spec/system/transcript_spec.rb
index 886219096a..4f6297b181 100644
--- a/plugins/chat/spec/system/transcript_spec.rb
+++ b/plugins/chat/spec/system/transcript_spec.rb
@@ -93,8 +93,6 @@ RSpec.describe "Quoting chat message transcripts", type: :system, js: true do
it "quotes the message" do
chat_page.visit_channel(chat_channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
-
clip_text = copy_messages_to_clipboard(message_1)
topic_page.visit_topic_and_open_composer(post_1.topic)
topic_page.fill_in_composer("This is a new post!\n\n" + clip_text)
@@ -117,8 +115,6 @@ RSpec.describe "Quoting chat message transcripts", type: :system, js: true do
it "quotes the messages" do
chat_page.visit_channel(chat_channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
-
clip_text = copy_messages_to_clipboard([message_1, message_2])
topic_page.visit_topic_and_open_composer(post_1.topic)
topic_page.fill_in_composer("This is a new post!\n\n" + clip_text)
@@ -149,8 +145,6 @@ RSpec.describe "Quoting chat message transcripts", type: :system, js: true do
it "works" do
chat_page.visit_channel(chat_channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
-
clip_text = copy_messages_to_clipboard(message_1)
topic_page.visit_topic_and_open_composer(post_1.topic)
topic_page.fill_in_composer(clip_text)
@@ -167,8 +161,6 @@ RSpec.describe "Quoting chat message transcripts", type: :system, js: true do
it "quotes the message" do
chat_page.visit_channel(chat_channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
-
clip_text = copy_messages_to_clipboard(message_1)
click_selection_button("cancel")
chat_channel_page.send_message(clip_text)
@@ -191,8 +183,6 @@ RSpec.describe "Quoting chat message transcripts", type: :system, js: true do
it "opens the topic composer with correct state" do
chat_page.visit_channel(chat_channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
-
select_message_desktop(message_1)
click_selection_button("quote")
@@ -219,8 +209,6 @@ RSpec.describe "Quoting chat message transcripts", type: :system, js: true do
mobile: true do
chat_page.visit_channel(chat_channel_1)
- expect(chat_channel_page).to have_no_loading_skeleton
-
chat_channel_page.click_message_action_mobile(message_1, "selectMessage")
click_selection_button("quote")
diff --git a/plugins/chat/test/javascripts/components/chat-message-test.js b/plugins/chat/test/javascripts/components/chat-message-test.js
index 7ffa9da1d0..d75f7fd528 100644
--- a/plugins/chat/test/javascripts/components/chat-message-test.js
+++ b/plugins/chat/test/javascripts/components/chat-message-test.js
@@ -57,6 +57,7 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
onHoverMessage: () => {},
didShowMessage: () => {},
didHideMessage: () => {},
+ forceRendering: () => {},
};
}
@@ -75,6 +76,8 @@ module("Discourse Chat | Component | chat-message", function (hooks) {
@onHoverMessage={{this.onHoverMessage}}
@didShowMessage={{this.didShowMessage}}
@didHideMessage={{this.didHideMessage}}
+ @didHideMessage={{this.didHideMessage}}
+ @forceRendering={{this.forceRendering}}
/>
`;