mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 21:43:21 +00:00
Previously, if you were in the process of editing the last message in a narrow and a new message came in, we'd rerender that second-to-last message, causing your editing widget to lose focus (and thus the next few keys you typed to be interpreted as keyboard shortcuts, which had a good chance of resulting in your navigating somewhere random). This rerendering was essentially unnecessary; the only change to state going into the rendering process was the next_is_same_sender CSS class being toggled on the messagebox in the message. So, at most, we should have been just toggling that CSS class (and this commit makes us do precisely that). It seems like we could further improve this code by just removing the next_is_same_sender CSS class entirely and removing this block, but I'm leaving that for follow-up work. Fixes #11656.
549 lines
18 KiB
JavaScript
549 lines
18 KiB
JavaScript
set_global('$', global.make_zjquery());
|
|
set_global('document', 'document-stub');
|
|
|
|
zrequire('util');
|
|
zrequire('XDate', 'node_modules/xdate/src/xdate');
|
|
zrequire('Filter', 'js/filter');
|
|
zrequire('FetchStatus', 'js/fetch_status');
|
|
zrequire('MessageListData', 'js/message_list_data');
|
|
zrequire('MessageListView', 'js/message_list_view');
|
|
zrequire('message_list');
|
|
|
|
var noop = function () {};
|
|
|
|
set_global('page_params', {
|
|
twenty_four_hour_time: false,
|
|
});
|
|
set_global('home_msg_list', null);
|
|
set_global('feature_flags', {twenty_four_hour_time: false});
|
|
set_global('people', {small_avatar_url: function () { return ''; }});
|
|
set_global('unread', {message_unread: function () {}});
|
|
// timerender calls setInterval when imported
|
|
set_global('timerender', {
|
|
render_date: function (time1, time2) {
|
|
if (time2 === undefined) {
|
|
return [{outerHTML: String(time1.getTime())}];
|
|
}
|
|
return [{outerHTML: String(time1.getTime()) + ' - ' + String(time2.getTime())}];
|
|
},
|
|
stringify_time: function (time) {
|
|
if (page_params.twenty_four_hour_time) {
|
|
return time.toString('HH:mm');
|
|
}
|
|
return time.toString('h:mm TT');
|
|
},
|
|
});
|
|
|
|
set_global('rows', {
|
|
get_table: function () {
|
|
return {
|
|
children: function () {
|
|
return {
|
|
detach: noop,
|
|
};
|
|
},
|
|
};
|
|
},
|
|
});
|
|
|
|
run_test('merge_message_groups', () => {
|
|
// MessageListView has lots of DOM code, so we are going to test the message
|
|
// group mearging logic on its own.
|
|
|
|
function build_message_context(message, message_context) {
|
|
if (message_context === undefined) {
|
|
message_context = {};
|
|
}
|
|
if (message === undefined) {
|
|
message = {};
|
|
}
|
|
message_context = _.defaults(message_context, {
|
|
include_sender: true,
|
|
});
|
|
message_context.msg = _.defaults(message, {
|
|
id: _.uniqueId('test_message_'),
|
|
status_message: false,
|
|
type: 'stream',
|
|
stream: 'Test Stream 1',
|
|
topic: 'Test Subject 1',
|
|
sender_email: 'test@example.com',
|
|
timestamp: _.uniqueId(),
|
|
});
|
|
return message_context;
|
|
}
|
|
|
|
function build_message_group(messages) {
|
|
return {
|
|
message_containers: messages,
|
|
message_group_id: _.uniqueId('test_message_group_'),
|
|
};
|
|
}
|
|
|
|
function build_list(message_groups) {
|
|
var list = new MessageListView(undefined, undefined, true);
|
|
list._message_groups = message_groups;
|
|
list.list = {
|
|
unsubscribed_bookend_content: function () {},
|
|
subscribed_bookend_content: function () {},
|
|
};
|
|
return list;
|
|
}
|
|
|
|
function extract_message_ids(lst) {
|
|
return _.map(lst, (item) => {
|
|
return item.msg.id;
|
|
});
|
|
}
|
|
|
|
function assert_message_list_equal(list1, list2) {
|
|
var ids1 = extract_message_ids(list1);
|
|
var ids2 = extract_message_ids(list2);
|
|
assert(ids1.length);
|
|
assert.deepEqual(ids1, ids2);
|
|
}
|
|
|
|
function extract_group(group) {
|
|
return extract_message_ids(group.message_containers);
|
|
}
|
|
|
|
function assert_message_groups_list_equal(list1, list2) {
|
|
var ids1 = _.map(list1, extract_group);
|
|
var ids2 = _.map(list2, extract_group);
|
|
assert(ids1.length);
|
|
assert.deepEqual(ids1, ids2);
|
|
}
|
|
|
|
(function test_empty_list_bottom() {
|
|
var list = build_list([]);
|
|
var message_group = build_message_group([
|
|
build_message_context(),
|
|
]);
|
|
|
|
var result = list.merge_message_groups([message_group], 'bottom');
|
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group]);
|
|
assert_message_groups_list_equal(result.append_groups, [message_group]);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_append_message_same_subject() {
|
|
|
|
var message1 = build_message_context();
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context();
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'bottom');
|
|
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[build_message_group([message1, message2])]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert_message_list_equal(result.append_messages, [message2]);
|
|
assert_message_list_equal(result.rerender_messages_next_same_sender, [message1]);
|
|
}());
|
|
|
|
(function test_append_message_different_subject() {
|
|
|
|
var message1 = build_message_context();
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({topic: 'Test subject 2'});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'bottom');
|
|
|
|
assert(!message_group2.group_date_divider_html);
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group1, message_group2]);
|
|
assert_message_groups_list_equal(result.append_groups, [message_group2]);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_append_message_different_subject_and_days() {
|
|
|
|
var message1 = build_message_context({timestamp: 1000});
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({topic: 'Test subject 2',
|
|
timestamp: 900000});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'bottom');
|
|
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group1, message_group2]);
|
|
assert_message_groups_list_equal(result.append_groups, [message_group2]);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
assert.equal(
|
|
message_group2.group_date_divider_html,
|
|
'900000000 - 1000000');
|
|
}());
|
|
|
|
(function test_append_message_different_day() {
|
|
|
|
var message1 = build_message_context({timestamp: 1000});
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({timestamp: 900000});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'bottom');
|
|
|
|
assert_message_groups_list_equal(list._message_groups, [message_group1]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, [message2]);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, [message1]);
|
|
assert(list._message_groups[0].message_containers[1].want_date_divider);
|
|
}());
|
|
|
|
(function test_append_message_historical() {
|
|
|
|
var message1 = build_message_context({historical: false});
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({historical: true});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'bottom');
|
|
|
|
assert(message_group2.bookend_top);
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group1, message_group2]);
|
|
assert_message_groups_list_equal(result.append_groups, [message_group2]);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_append_message_same_subject_me_message() {
|
|
|
|
var message1 = build_message_context();
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({is_me_message: true});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'bottom');
|
|
|
|
assert(message2.include_sender);
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[build_message_group([message1, message2])]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert_message_list_equal(result.append_messages, [message2]);
|
|
assert_message_list_equal(result.rerender_messages_next_same_sender, [message1]);
|
|
}());
|
|
|
|
|
|
(function test_prepend_message_same_subject() {
|
|
|
|
var message1 = build_message_context();
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context();
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'top');
|
|
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[build_message_group([message2, message1])]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert_message_groups_list_equal(result.rerender_groups,
|
|
[build_message_group([message2, message1])]);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_prepend_message_different_subject() {
|
|
|
|
var message1 = build_message_context();
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({topic: 'Test Subject 2'});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'top');
|
|
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group2, message_group1]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_prepend_message_different_subject_and_day() {
|
|
|
|
var message1 = build_message_context({timestamp: 900000});
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({topic: 'Test Subject 2',
|
|
timestamp: 1000});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'top');
|
|
|
|
// We should have a group date divider between the recipient blocks.
|
|
assert.equal(
|
|
message_group1.group_date_divider_html,
|
|
'900000000 - 1000000');
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group2, message_group1]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
|
|
assert.deepEqual(result.rerender_groups, [message_group1]);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_prepend_message_different_day() {
|
|
|
|
var message1 = build_message_context({timestamp: 900000});
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({timestamp: 1000});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'top');
|
|
|
|
// We should have a group date divider within the single recipient block.
|
|
assert.equal(
|
|
message_group2.message_containers[1].date_divider_html,
|
|
'900000000 - 1000000');
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group2]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert.deepEqual(result.prepend_groups, []);
|
|
assert_message_groups_list_equal(result.rerender_groups, [message_group2]);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
(function test_prepend_message_historical() {
|
|
|
|
var message1 = build_message_context({historical: false});
|
|
var message_group1 = build_message_group([
|
|
message1,
|
|
]);
|
|
|
|
var message2 = build_message_context({historical: true});
|
|
var message_group2 = build_message_group([
|
|
message2,
|
|
]);
|
|
|
|
var list = build_list([message_group1]);
|
|
var result = list.merge_message_groups([message_group2], 'top');
|
|
|
|
assert(message_group1.bookend_top);
|
|
assert_message_groups_list_equal(
|
|
list._message_groups,
|
|
[message_group2, message_group1]);
|
|
assert.deepEqual(result.append_groups, []);
|
|
assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
|
|
assert.deepEqual(result.rerender_groups, []);
|
|
assert.deepEqual(result.append_messages, []);
|
|
assert.deepEqual(result.rerender_messages_next_same_sender, []);
|
|
}());
|
|
|
|
});
|
|
|
|
// TODO: Add a test suite for rerender_messages_next_same_sender() that includes cases
|
|
// where new messages added via local echo have a different date from
|
|
// the older messages.
|
|
|
|
run_test('render_windows', () => {
|
|
// We only render up to 400 messages at a time in our message list,
|
|
// and we only change the window (which is a range, really, with
|
|
// start/end) when the pointer moves outside of the window or close
|
|
// to the edges.
|
|
|
|
var view = (function make_view() {
|
|
var table_name = 'zfilt';
|
|
var filter = new Filter();
|
|
|
|
var list = new message_list.MessageList({
|
|
table_name: table_name,
|
|
filter: filter,
|
|
});
|
|
|
|
var view = list.view;
|
|
|
|
// Stub out functionality that is not core to the rendering window
|
|
// logic.
|
|
list.data.unmuted_messages = function (messages) {
|
|
return messages;
|
|
};
|
|
|
|
// We don't need to actually render the DOM. The windowing logic
|
|
// sits above that layer.
|
|
view.render = noop;
|
|
view.rerender_preserving_scrolltop = noop;
|
|
|
|
return view;
|
|
}());
|
|
|
|
var list = view.list;
|
|
|
|
(function test_with_empty_list() {
|
|
// The function should early exit here.
|
|
var rendered = view.maybe_rerender();
|
|
assert.equal(rendered, false);
|
|
}());
|
|
|
|
var messages;
|
|
|
|
function reset_list(opts) {
|
|
messages = _.map(_.range(opts.count), function (i) {
|
|
return {
|
|
id: i,
|
|
};
|
|
});
|
|
list.selected_idx = function () { return 0; };
|
|
list.clear();
|
|
|
|
list.add_messages(messages, {});
|
|
}
|
|
|
|
|
|
function verify_no_move_range(start, end) {
|
|
// In our render window, there are up to 300 positions in
|
|
// the list where we can move the pointer without forcing
|
|
// a re-render. The code avoids hasty re-renders for
|
|
// performance reasons.
|
|
_.each(_.range(start, end), function (idx) {
|
|
list.selected_idx = function () { return idx; };
|
|
var rendered = view.maybe_rerender();
|
|
assert.equal(rendered, false);
|
|
});
|
|
}
|
|
|
|
function verify_move(idx, range) {
|
|
var start = range[0];
|
|
var end = range[1];
|
|
|
|
list.selected_idx = function () { return idx; };
|
|
var rendered = view.maybe_rerender();
|
|
assert.equal(rendered, true);
|
|
assert.equal(view._render_win_start, start);
|
|
assert.equal(view._render_win_end, end);
|
|
}
|
|
|
|
reset_list({count: 51});
|
|
verify_no_move_range(0, 51);
|
|
|
|
reset_list({count: 450});
|
|
verify_no_move_range(0, 350);
|
|
|
|
verify_move(350, [150, 450]);
|
|
verify_no_move_range(200, 400);
|
|
|
|
verify_move(199, [0, 400]);
|
|
verify_no_move_range(50, 350);
|
|
|
|
verify_move(350, [150, 450]);
|
|
verify_no_move_range(200, 400);
|
|
|
|
verify_move(199, [0, 400]);
|
|
verify_no_move_range(0, 350);
|
|
|
|
verify_move(400, [200, 450]);
|
|
|
|
reset_list({count: 800});
|
|
verify_no_move_range(0, 350);
|
|
|
|
verify_move(350, [150, 550]);
|
|
verify_no_move_range(200, 500);
|
|
|
|
verify_move(500, [300, 700]);
|
|
verify_no_move_range(350, 650);
|
|
|
|
verify_move(650, [450, 800]);
|
|
verify_no_move_range(500, 750);
|
|
|
|
verify_move(499, [299, 699]);
|
|
verify_no_move_range(349, 649);
|
|
|
|
verify_move(348, [148, 548]);
|
|
verify_no_move_range(198, 398);
|
|
|
|
verify_move(197, [0, 400]);
|
|
verify_no_move_range(0, 350);
|
|
});
|