mirror of
https://github.com/zulip/zulip.git
synced 2025-11-13 18:36:36 +00:00
message_list: Fix message list missing messages.
Since we allow calling `add_messages` without checking fetch status, it can lead to non-contiguous message history due to latest message being added to a message list without previous messages being fetched. To fix it, we only allow adding new messages via message_fetch which properly sets `anchor` to the last message in the list before fetching and adding messages to the list.
This commit is contained in:
@@ -322,7 +322,7 @@ export function insert_new_messages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the message list's rendering for the newly arrived messages.
|
// Update the message list's rendering for the newly arrived messages.
|
||||||
const render_info = message_util.add_new_messages(messages, list);
|
const render_info = list.add_messages(messages);
|
||||||
|
|
||||||
// The render_info.need_user_to_scroll calculation, which
|
// The render_info.need_user_to_scroll calculation, which
|
||||||
// looks at message feed scroll positions to see whether the
|
// looks at message feed scroll positions to see whether the
|
||||||
@@ -341,7 +341,7 @@ export function insert_new_messages(
|
|||||||
// but it is not worth doing so for every new message.
|
// but it is not worth doing so for every new message.
|
||||||
message_list_data_cache.remove(msg_list_data.filter);
|
message_list_data_cache.remove(msg_list_data.filter);
|
||||||
} else {
|
} else {
|
||||||
message_util.add_new_messages_data(messages, msg_list_data);
|
msg_list_data.add_messages(messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ function process_result(data: MessageFetchResponse, opts: MessageFetchOptions):
|
|||||||
// messages not tracked in unread.ts during this fetching process.
|
// messages not tracked in unread.ts during this fetching process.
|
||||||
message_util.do_unread_count_updates(messages, true);
|
message_util.do_unread_count_updates(messages, true);
|
||||||
|
|
||||||
|
const ignore_found_newest = true;
|
||||||
if (messages.length > 0) {
|
if (messages.length > 0) {
|
||||||
if (opts.msg_list) {
|
if (opts.msg_list) {
|
||||||
if (opts.validate_filter_topic_post_fetch) {
|
if (opts.validate_filter_topic_post_fetch) {
|
||||||
@@ -160,9 +161,9 @@ function process_result(data: MessageFetchResponse, opts: MessageFetchOptions):
|
|||||||
}
|
}
|
||||||
// Since this adds messages to the MessageList and renders MessageListView,
|
// Since this adds messages to the MessageList and renders MessageListView,
|
||||||
// we don't need to call it if msg_list was not defined by the caller.
|
// we don't need to call it if msg_list was not defined by the caller.
|
||||||
message_util.add_old_messages(messages, opts.msg_list);
|
opts.msg_list.add_messages(messages, {}, ignore_found_newest);
|
||||||
} else {
|
} else {
|
||||||
opts.msg_list_data.add_messages(messages);
|
opts.msg_list_data.add_messages(messages, ignore_found_newest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -193,10 +193,11 @@ export class MessageList {
|
|||||||
add_messages(
|
add_messages(
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
append_to_view_opts: {messages_are_new?: boolean} = {},
|
append_to_view_opts: {messages_are_new?: boolean} = {},
|
||||||
|
ignore_found_newest = false,
|
||||||
): RenderInfo | undefined {
|
): RenderInfo | undefined {
|
||||||
// This adds all messages to our data, but only returns
|
// This adds all messages to our data, but only returns
|
||||||
// the currently viewable ones.
|
// the currently viewable ones.
|
||||||
const info = this.data.add_messages(messages);
|
const info = this.data.add_messages(messages, ignore_found_newest);
|
||||||
|
|
||||||
const top_messages = info.top_messages;
|
const top_messages = info.top_messages;
|
||||||
const bottom_messages = info.bottom_messages;
|
const bottom_messages = info.bottom_messages;
|
||||||
|
|||||||
@@ -295,7 +295,10 @@ export class MessageListData {
|
|||||||
return this._items.some((message) => message.unread);
|
return this._items.some((message) => message.unread);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_messages(messages: Message[]): {
|
add_messages(
|
||||||
|
messages: Message[],
|
||||||
|
ignore_found_newest = false,
|
||||||
|
): {
|
||||||
top_messages: Message[];
|
top_messages: Message[];
|
||||||
bottom_messages: Message[];
|
bottom_messages: Message[];
|
||||||
interior_messages: Message[];
|
interior_messages: Message[];
|
||||||
@@ -332,6 +335,13 @@ export class MessageListData {
|
|||||||
top_messages = this.prepend(top_messages);
|
top_messages = this.prepend(top_messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ignore_found_newest && !this.fetch_status.has_found_newest()) {
|
||||||
|
// Don't add messages at the bottom if we haven't found
|
||||||
|
// the latest message to avoid non-contiguous message history.
|
||||||
|
this.fetch_status.update_expected_max_message_id(bottom_messages);
|
||||||
|
bottom_messages = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (bottom_messages.length > 0) {
|
if (bottom_messages.length > 0) {
|
||||||
bottom_messages = this.append(bottom_messages);
|
bottom_messages = this.append(bottom_messages);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import assert from "minimalistic-assert";
|
|||||||
|
|
||||||
import {all_messages_data} from "./all_messages_data.ts";
|
import {all_messages_data} from "./all_messages_data.ts";
|
||||||
import type {MessageList, RenderInfo} from "./message_list.ts";
|
import type {MessageList, RenderInfo} from "./message_list.ts";
|
||||||
import type {MessageListData} from "./message_list_data.ts";
|
|
||||||
import * as message_lists from "./message_lists.ts";
|
import * as message_lists from "./message_lists.ts";
|
||||||
import * as message_store from "./message_store.ts";
|
import * as message_store from "./message_store.ts";
|
||||||
import type {Message} from "./message_store.ts";
|
import type {Message} from "./message_store.ts";
|
||||||
@@ -40,13 +39,6 @@ export function add_messages(
|
|||||||
return render_info;
|
return render_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function add_old_messages(
|
|
||||||
messages: Message[],
|
|
||||||
msg_list: MessageList,
|
|
||||||
): RenderInfo | undefined {
|
|
||||||
return add_messages(messages, msg_list, {messages_are_new: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function add_new_messages(
|
export function add_new_messages(
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
msg_list: MessageList,
|
msg_list: MessageList,
|
||||||
@@ -61,29 +53,6 @@ export function add_new_messages(
|
|||||||
}
|
}
|
||||||
return add_messages(messages, msg_list, {messages_are_new: true});
|
return add_messages(messages, msg_list, {messages_are_new: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function add_new_messages_data(
|
|
||||||
messages: Message[],
|
|
||||||
msg_list_data: MessageListData,
|
|
||||||
):
|
|
||||||
| {
|
|
||||||
top_messages: Message[];
|
|
||||||
bottom_messages: Message[];
|
|
||||||
interior_messages: Message[];
|
|
||||||
}
|
|
||||||
| undefined {
|
|
||||||
if (!msg_list_data.fetch_status.has_found_newest()) {
|
|
||||||
const filtered_msgs = msg_list_data.valid_non_duplicated_messages(messages);
|
|
||||||
// The reasoning in add_new_messages applies here as well;
|
|
||||||
// we're trying to maintain a data structure that's a
|
|
||||||
// contiguous range of message history, so we can't append a
|
|
||||||
// new message that might not be adjacent to that range.
|
|
||||||
msg_list_data.fetch_status.update_expected_max_message_id(filtered_msgs);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return msg_list_data.add_messages(messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_count_of_messages_in_topic_sent_after_current_message(
|
export function get_count_of_messages_in_topic_sent_after_current_message(
|
||||||
stream_id: number,
|
stream_id: number,
|
||||||
topic: string,
|
topic: string,
|
||||||
|
|||||||
@@ -912,7 +912,8 @@ function load_local_messages(msg_data: MessageListData, superset_data: MessageLi
|
|||||||
// one message the user will expect to see in the new narrow.
|
// one message the user will expect to see in the new narrow.
|
||||||
|
|
||||||
const in_msgs = superset_data.all_messages();
|
const in_msgs = superset_data.all_messages();
|
||||||
msg_data.add_messages(in_msgs);
|
const ignore_found_newest = true;
|
||||||
|
msg_data.add_messages(in_msgs, ignore_found_newest);
|
||||||
|
|
||||||
return !msg_data.visibly_empty();
|
return !msg_data.visibly_empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ run_test("reply_label", () => {
|
|||||||
stream_id: 2,
|
stream_id: 2,
|
||||||
};
|
};
|
||||||
stream_data.add_sub(stream_two);
|
stream_data.add_sub(stream_two);
|
||||||
list.add_messages([
|
list.add_messages(
|
||||||
|
[
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
stream_id: stream_one.stream_id,
|
stream_id: stream_one.stream_id,
|
||||||
@@ -95,7 +96,10 @@ run_test("reply_label", () => {
|
|||||||
id: 5,
|
id: 5,
|
||||||
display_reply_to: "some user, other user",
|
display_reply_to: "some user, other user",
|
||||||
},
|
},
|
||||||
]);
|
],
|
||||||
|
{},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
const expected_labels = [
|
const expected_labels = [
|
||||||
"#first_stream > first_topic",
|
"#first_stream > first_topic",
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ const {run_test, noop} = require("./lib/test.cjs");
|
|||||||
const direct_message_group_data = mock_esm("../src/direct_message_group_data");
|
const direct_message_group_data = mock_esm("../src/direct_message_group_data");
|
||||||
const message_lists = mock_esm("../src/message_lists");
|
const message_lists = mock_esm("../src/message_lists");
|
||||||
const message_notifications = mock_esm("../src/message_notifications");
|
const message_notifications = mock_esm("../src/message_notifications");
|
||||||
const message_util = mock_esm("../src/message_util");
|
|
||||||
const pm_list = mock_esm("../src/pm_list");
|
const pm_list = mock_esm("../src/pm_list");
|
||||||
const stream_list = mock_esm("../src/stream_list");
|
const stream_list = mock_esm("../src/stream_list");
|
||||||
const unread_ui = mock_esm("../src/unread_ui");
|
const unread_ui = mock_esm("../src/unread_ui");
|
||||||
const activity = mock_esm("../src/activity");
|
const activity = mock_esm("../src/activity");
|
||||||
|
|
||||||
|
let added_message = false;
|
||||||
message_lists.current = {
|
message_lists.current = {
|
||||||
data: {
|
data: {
|
||||||
filter: {
|
filter: {
|
||||||
@@ -38,6 +38,9 @@ message_lists.current = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
add_messages() {
|
||||||
|
added_message = true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
message_lists.all_rendered_message_lists = () => [message_lists.current];
|
message_lists.all_rendered_message_lists = () => [message_lists.current];
|
||||||
message_lists.non_rendered_data = () => [];
|
message_lists.non_rendered_data = () => [];
|
||||||
@@ -103,7 +106,6 @@ run_test("insert_message", ({override}) => {
|
|||||||
|
|
||||||
helper.redirect(direct_message_group_data, "process_loaded_messages");
|
helper.redirect(direct_message_group_data, "process_loaded_messages");
|
||||||
helper.redirect(message_notifications, "received_messages");
|
helper.redirect(message_notifications, "received_messages");
|
||||||
helper.redirect(message_util, "add_new_messages");
|
|
||||||
helper.redirect(stream_list, "update_streams_sidebar");
|
helper.redirect(stream_list, "update_streams_sidebar");
|
||||||
helper.redirect(unread_ui, "update_unread_counts");
|
helper.redirect(unread_ui, "update_unread_counts");
|
||||||
helper.redirect(activity, "set_received_new_messages");
|
helper.redirect(activity, "set_received_new_messages");
|
||||||
@@ -116,12 +118,12 @@ run_test("insert_message", ({override}) => {
|
|||||||
// comes in:
|
// comes in:
|
||||||
assert.deepEqual(helper.events, [
|
assert.deepEqual(helper.events, [
|
||||||
[direct_message_group_data, "process_loaded_messages"],
|
[direct_message_group_data, "process_loaded_messages"],
|
||||||
[message_util, "add_new_messages"],
|
|
||||||
[unread_ui, "update_unread_counts"],
|
[unread_ui, "update_unread_counts"],
|
||||||
[activity, "set_received_new_messages"],
|
[activity, "set_received_new_messages"],
|
||||||
[message_notifications, "received_messages"],
|
[message_notifications, "received_messages"],
|
||||||
[stream_list, "update_streams_sidebar"],
|
[stream_list, "update_streams_sidebar"],
|
||||||
]);
|
]);
|
||||||
|
assert.ok(added_message);
|
||||||
|
|
||||||
// Despite all of our stubbing/mocking, the call to
|
// Despite all of our stubbing/mocking, the call to
|
||||||
// insert_new_messages will have created a very important
|
// insert_new_messages will have created a very important
|
||||||
|
|||||||
@@ -463,7 +463,7 @@ run_test("add_remove_rerender", ({override}) => {
|
|||||||
|
|
||||||
const messages = [{id: 1}, {id: 2}, {id: 3}];
|
const messages = [{id: 1}, {id: 2}, {id: 3}];
|
||||||
|
|
||||||
list.add_messages(messages);
|
list.add_messages(messages, {}, true);
|
||||||
assert.equal(list.num_items(), 3);
|
assert.equal(list.num_items(), 3);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ run_test("basics", () => {
|
|||||||
assert_contents(mld, [15, 25, 35, 45]);
|
assert_contents(mld, [15, 25, 35, 45]);
|
||||||
|
|
||||||
const new_msgs = make_msgs([10, 20, 30, 40, 50, 60, 70]);
|
const new_msgs = make_msgs([10, 20, 30, 40, 50, 60, 70]);
|
||||||
const info = mld.add_messages(new_msgs);
|
const info = mld.add_messages(new_msgs, true);
|
||||||
|
|
||||||
assert.deepEqual(info, {
|
assert.deepEqual(info, {
|
||||||
top_messages: make_msgs([10]),
|
top_messages: make_msgs([10]),
|
||||||
@@ -103,7 +103,7 @@ run_test("basics", () => {
|
|||||||
{id: 9, sender_id: 11, type: "private", to_user_ids: "9"},
|
{id: 9, sender_id: 11, type: "private", to_user_ids: "9"},
|
||||||
...msgs_sent_by_6,
|
...msgs_sent_by_6,
|
||||||
];
|
];
|
||||||
mld.add_messages(msgs_with_sender_ids);
|
mld.add_messages(msgs_with_sender_ids, true);
|
||||||
assert.deepEqual(mld.get_messages_sent_by_user(6), msgs_sent_by_6);
|
assert.deepEqual(mld.get_messages_sent_by_user(6), msgs_sent_by_6);
|
||||||
|
|
||||||
mld.clear();
|
mld.clear();
|
||||||
@@ -111,7 +111,7 @@ run_test("basics", () => {
|
|||||||
assert.equal(mld.closest_id(99), -1);
|
assert.equal(mld.closest_id(99), -1);
|
||||||
assert.equal(mld.get_last_message_sent_by_me(), undefined);
|
assert.equal(mld.get_last_message_sent_by_me(), undefined);
|
||||||
|
|
||||||
mld.add_messages(make_msgs([120, 125.01, 130, 140]));
|
mld.add_messages(make_msgs([120, 125.01, 130, 140]), true);
|
||||||
assert_contents(mld, [120, 125.01, 130, 140]);
|
assert_contents(mld, [120, 125.01, 130, 140]);
|
||||||
mld.set_selected_id(125.01);
|
mld.set_selected_id(125.01);
|
||||||
assert.equal(mld.selected_id(), 125.01);
|
assert.equal(mld.selected_id(), 125.01);
|
||||||
@@ -270,7 +270,7 @@ run_test("muting", () => {
|
|||||||
{id: 8, type: "stream", stream_id: 1, topic: "whatever"},
|
{id: 8, type: "stream", stream_id: 1, topic: "whatever"},
|
||||||
];
|
];
|
||||||
|
|
||||||
const orig_info = mld.add_messages(orig_messages);
|
const orig_info = mld.add_messages(orig_messages, true);
|
||||||
assert.deepEqual(orig_info, {
|
assert.deepEqual(orig_info, {
|
||||||
top_messages: [],
|
top_messages: [],
|
||||||
interior_messages: [],
|
interior_messages: [],
|
||||||
@@ -293,7 +293,7 @@ run_test("muting", () => {
|
|||||||
{id: 10, type: "stream", stream_id: 1, topic: "whatever"},
|
{id: 10, type: "stream", stream_id: 1, topic: "whatever"},
|
||||||
];
|
];
|
||||||
|
|
||||||
const more_info = mld.add_messages(more_messages);
|
const more_info = mld.add_messages(more_messages, true);
|
||||||
|
|
||||||
assert_msg_ids(mld._all_items, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
assert_msg_ids(mld._all_items, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
assert_msg_ids(mld._items, [2, 4, 6, 8, 10]);
|
assert_msg_ids(mld._items, [2, 4, 6, 8, 10]);
|
||||||
|
|||||||
@@ -778,7 +778,7 @@ test("render_windows", ({mock_template}) => {
|
|||||||
list.view.clear_table = noop;
|
list.view.clear_table = noop;
|
||||||
list.clear();
|
list.clear();
|
||||||
|
|
||||||
list.add_messages(messages, {});
|
list.add_messages(messages, {}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function verify_no_move_range(start, end) {
|
function verify_no_move_range(start, end) {
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ test("all_topics_in_cache", ({override}) => {
|
|||||||
assert.equal(stream_topic_history.all_topics_in_cache(sub), false);
|
assert.equal(stream_topic_history.all_topics_in_cache(sub), false);
|
||||||
|
|
||||||
all_messages_data.all_messages_data.clear();
|
all_messages_data.all_messages_data.clear();
|
||||||
all_messages_data.all_messages_data.add_messages(messages);
|
all_messages_data.all_messages_data.add_messages(messages, true);
|
||||||
|
|
||||||
let has_found_newest = false;
|
let has_found_newest = false;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user