FIX: auto fill was not happening on first load (#21809)

This commit attempts to fix the case where the messages loaded initially don't fill the screen. It would prevent user to scroll and as a result to load more.

There are multiple fixes in this commit:
- the main fix is removing this code which was preventing the actual fill:

```javascript
        // prevents an edge case where user clicks bottom arrow
        // just after scrolling to top
        if (loadingPast && this.#isAtBottom()) {
          return;
        }
```

- ensures we always give a page site to the `chatApi.channel(...)` call if we have one, in the current state when `fetchFromLastRead` was `true` we would not set `args.page_size`
- ensures the `query_paginated_messages` is having a valid page size, which is not nil and not > `MAX_PAGE_SIZE`
- write a spec for the autofill, it was a challenging spec to write but it should give us the confidence we need here
This commit is contained in:
Joffrey JAFFEUX 2023-05-29 17:34:44 +02:00 committed by GitHub
parent 4198ac43b4
commit 55ef2d0698
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 70 additions and 57 deletions

View File

@ -125,6 +125,8 @@ module Chat
messages, messages,
target_message_id = nil target_message_id = nil
) )
page_size = [page_size || MAX_PAGE_SIZE, MAX_PAGE_SIZE].min
if target_message_id.present? if target_message_id.present?
condition = direction == PAST ? "<" : ">" condition = direction == PAST ? "<" : ">"
messages = messages.where("id #{condition} ?", target_message_id.to_i) messages = messages.where("id #{condition} ?", target_message_id.to_i)

View File

@ -94,7 +94,7 @@ export default class ChatLivePane extends Component {
@action @action
didResizePane() { didResizePane() {
this.fillPaneAttempt(); this.debounceFillPaneAttempt();
this.computeDatesSeparators(); this.computeDatesSeparators();
this.forceRendering(); this.forceRendering();
} }
@ -228,7 +228,7 @@ export default class ChatLivePane extends Component {
this.loadedOnce = true; this.loadedOnce = true;
this.requestedTargetMessageId = null; this.requestedTargetMessageId = null;
this.loadingMorePast = false; this.loadingMorePast = false;
this.fillPaneAttempt(); this.debounceFillPaneAttempt();
this.updateLastReadMessage(); this.updateLastReadMessage();
this.subscribeToUpdates(this.args.channel); this.subscribeToUpdates(this.args.channel);
}); });
@ -276,12 +276,6 @@ export default class ChatLivePane extends Component {
return; return;
} }
// prevents an edge case where user clicks bottom arrow
// just after scrolling to top
if (loadingPast && this.#isAtBottom()) {
return;
}
const [messages, meta] = this.afterFetchCallback( const [messages, meta] = this.afterFetchCallback(
this.args.channel, this.args.channel,
result result
@ -318,11 +312,23 @@ export default class ChatLivePane extends Component {
.catch(this._handleErrors) .catch(this._handleErrors)
.finally(() => { .finally(() => {
this[loadingMoreKey] = false; this[loadingMoreKey] = false;
this.fillPaneAttempt(); this.debounceFillPaneAttempt();
}); });
} }
@debounce(500) debounceFillPaneAttempt() {
if (!this.loadedOnce) {
return;
}
this._debouncedFillPaneAttemptHandler = discourseDebounce(
this,
this.fillPaneAttempt,
500
);
}
@bind
fillPaneAttempt() { fillPaneAttempt() {
if (this._selfDeleted) { if (this._selfDeleted) {
return; return;
@ -333,11 +339,12 @@ export default class ChatLivePane extends Component {
return; return;
} }
if (!this.args.channel?.messagesManager?.canLoadMorePast) { if (!this.args.channel?.canLoadMorePast) {
return; return;
} }
const firstMessage = this.args.channel?.messages?.[0]; const firstMessage = this.args.channel?.messages?.firstObject;
if (!firstMessage?.visible) { if (!firstMessage?.visible) {
return; return;
} }
@ -1055,6 +1062,7 @@ export default class ChatLivePane extends Component {
} }
#cancelHandlers() { #cancelHandlers() {
cancel(this._debouncedFillPaneAttemptHandler);
cancel(this._onScrollEndedHandler); cancel(this._onScrollEndedHandler);
cancel(this._laterComputeHandler); cancel(this._laterComputeHandler);
cancel(this._debounceFetchMessagesHandler); cancel(this._debounceFetchMessagesHandler);

View File

@ -24,14 +24,13 @@ export default class ChatApi extends Service {
*/ */
channel(channelId, data = {}) { channel(channelId, data = {}) {
const args = {}; const args = {};
args.page_size = data.pageSize;
if (data.targetMessageId) { if (data.targetMessageId) {
args.target_message_id = data.targetMessageId; args.target_message_id = data.targetMessageId;
} else if (data.fetchFromLastRead) { } else if (data.fetchFromLastRead) {
args.fetch_from_last_read = true; args.fetch_from_last_read = true;
} else { } else {
args.page_size = data.pageSize;
if (data.direction) { if (data.direction) {
args.direction = data.direction; args.direction = data.direction;
} }

View File

@ -71,6 +71,11 @@ RSpec.describe Chat::MessagesQuery do
end end
end end
it "limits results of paginated query when page_size is not set" do
options[:target_message_id] = nil
stub_const(described_class, "MAX_PAGE_SIZE", 1) { expect(subject[:messages].length).to eq(1) }
end
describe "when some messages are in threads" do describe "when some messages are in threads" do
fab!(:thread) { Fabricate(:chat_thread, channel: channel) } fab!(:thread) { Fabricate(:chat_thread, channel: channel) }

View File

@ -7,15 +7,41 @@ RSpec.describe "Chat channel", type: :system, js: true do
let(:chat) { PageObjects::Pages::Chat.new } let(:chat) { PageObjects::Pages::Chat.new }
let(:channel_page) { PageObjects::Pages::ChatChannel.new } let(:channel_page) { PageObjects::Pages::ChatChannel.new }
let(:sidebar_page) { PageObjects::Pages::Sidebar.new }
before { chat_system_bootstrap } before do
chat_system_bootstrap
channel_1.add(current_user)
sign_in(current_user)
end
context "when sending a message" do context "when first batch of messages doesnt fill page" do
before do before do
channel_1.add(current_user) 50.times do
sign_in(current_user) Fabricate(
:chat_message,
message: Faker::Lorem.characters(number: SiteSetting.chat_minimum_message_length),
user: current_user,
chat_channel: channel_1,
)
end
end end
it "autofills for more messages" do
chat.prefers_full_page
visit("/")
# cheap trick to ensure the messages don't fill the initial page
page.execute_script(
"document.head.insertAdjacentHTML('beforeend', `<style>.chat-message-text{font-size:3px;}</style>`)",
)
sidebar_page.open_channel(channel_1)
expect(channel_page).to have_no_loading_skeleton
expect(channel_page.messages).to have_x_messages(51)
end
end
context "when sending a message" do
context "with lots of messages" do context "with lots of messages" do
before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } } before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } }
@ -71,11 +97,7 @@ RSpec.describe "Chat channel", type: :system, js: true do
end end
context "when clicking the arrow button" do context "when clicking the arrow button" do
before do before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } }
channel_1.add(current_user)
50.times { Fabricate(:chat_message, chat_channel: channel_1) }
sign_in(current_user)
end
it "jumps to the bottom of the channel" do it "jumps to the bottom of the channel" do
unloaded_message = Fabricate(:chat_message, chat_channel: channel_1) unloaded_message = Fabricate(:chat_message, chat_channel: channel_1)
@ -92,11 +114,6 @@ RSpec.describe "Chat channel", type: :system, js: true do
end end
context "when returning to a channel where last read is not last message" do context "when returning to a channel where last read is not last message" do
before do
channel_1.add(current_user)
sign_in(current_user)
end
it "jumps to the bottom of the channel" do it "jumps to the bottom of the channel" do
channel_1.membership_for(current_user).update!(last_read_message: message_1) channel_1.membership_for(current_user).update!(last_read_message: message_1)
messages = 50.times.map { Fabricate(:chat_message, chat_channel: channel_1) } messages = 50.times.map { Fabricate(:chat_message, chat_channel: channel_1) }
@ -112,9 +129,7 @@ RSpec.describe "Chat channel", type: :system, js: true do
before do before do
channel_1.add(other_user) channel_1.add(other_user)
channel_1.add(current_user)
50.times { Fabricate(:chat_message, chat_channel: channel_1) } 50.times { Fabricate(:chat_message, chat_channel: channel_1) }
sign_in(current_user)
end end
it "doesnt scroll the pane" do it "doesnt scroll the pane" do
@ -142,11 +157,7 @@ RSpec.describe "Chat channel", type: :system, js: true do
) )
end end
before do before { channel_1.add(other_user) }
channel_1.add(other_user)
channel_1.add(current_user)
sign_in(current_user)
end
it "highlights the mentions" do it "highlights the mentions" do
chat.visit_channel(channel_1) chat.visit_channel(channel_1)
@ -185,8 +196,6 @@ RSpec.describe "Chat channel", type: :system, js: true do
before do before do
Fabricate(:chat_message, in_reply_to: message_1, user: other_user, chat_channel: channel_1) Fabricate(:chat_message, in_reply_to: message_1, user: other_user, chat_channel: channel_1)
channel_1.add(other_user) channel_1.add(other_user)
channel_1.add(current_user)
sign_in(current_user)
end end
it "doesnt show the reply-to line" do it "doesnt show the reply-to line" do
@ -203,8 +212,6 @@ RSpec.describe "Chat channel", type: :system, js: true do
Fabricate(:chat_message, user: other_user, chat_channel: channel_1) Fabricate(:chat_message, user: other_user, chat_channel: channel_1)
Fabricate(:chat_message, in_reply_to: message_1, user: other_user, chat_channel: channel_1) Fabricate(:chat_message, in_reply_to: message_1, user: other_user, chat_channel: channel_1)
channel_1.add(other_user) channel_1.add(other_user)
channel_1.add(current_user)
sign_in(current_user)
end end
it "shows the reply-to line" do it "shows the reply-to line" do
@ -229,8 +236,6 @@ RSpec.describe "Chat channel", type: :system, js: true do
Fabricate(:chat_message, user: other_user, chat_channel: channel_1) Fabricate(:chat_message, user: other_user, chat_channel: channel_1)
Fabricate(:chat_message, in_reply_to: message_2, user: current_user, chat_channel: channel_1) Fabricate(:chat_message, in_reply_to: message_2, user: current_user, chat_channel: channel_1)
channel_1.add(other_user) channel_1.add(other_user)
channel_1.add(current_user)
sign_in(current_user)
end end
it "renders text in the reply-to" do it "renders text in the reply-to" do
@ -241,12 +246,7 @@ RSpec.describe "Chat channel", type: :system, js: true do
end end
context "when messages are separated by a day" do context "when messages are separated by a day" do
before do before { Fabricate(:chat_message, chat_channel: channel_1, created_at: 2.days.ago) }
Fabricate(:chat_message, chat_channel: channel_1, created_at: 2.days.ago)
channel_1.add(current_user)
sign_in(current_user)
end
it "shows a date separator" do it "shows a date separator" do
chat.visit_channel(channel_1) chat.visit_channel(channel_1)
@ -264,11 +264,6 @@ RSpec.describe "Chat channel", type: :system, js: true do
\`\`\` \`\`\`
MESSAGE MESSAGE
before do
channel_1.add(current_user)
sign_in(current_user)
end
it "adds the correct lang" do it "adds the correct lang" do
chat.visit_channel(channel_1) chat.visit_channel(channel_1)
@ -277,11 +272,7 @@ RSpec.describe "Chat channel", type: :system, js: true do
end end
context "when scrolling" do context "when scrolling" do
before do before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } }
channel_1.add(current_user)
50.times { Fabricate(:chat_message, chat_channel: channel_1) }
sign_in(current_user)
end
it "resets the active message" do it "resets the active message" do
chat.visit_channel(channel_1) chat.visit_channel(channel_1)

View File

@ -12,6 +12,10 @@ module PageObjects
@context = context @context = context
end end
def component
find(context)
end
def has_message?(**args) def has_message?(**args)
PageObjects::Components::Chat::Message.new(".chat-channel").exists?(**args) PageObjects::Components::Chat::Message.new(".chat-channel").exists?(**args)
end end
@ -19,6 +23,10 @@ module PageObjects
def has_no_message?(**args) def has_no_message?(**args)
!has_message?(**args) !has_message?(**args)
end end
def has_x_messages?(count)
find(context).has_css?(SELECTOR, count: count, visible: :all)
end
end end
end end
end end