search: Don't mark messages as read in search narrow.

Don't mark unread messages as read while searching.
This behavior will be extended to other narrows later.

Fixes: #12556.
This commit is contained in:
Mohit Gupta 2019-07-10 05:33:41 +05:30 committed by Tim Abbott
parent bd52ec9f95
commit 6ec40cf9a0
15 changed files with 66 additions and 10 deletions

View File

@ -99,6 +99,10 @@ These two simple rules, combined with the pointer logic above, end up
matching user expectations well for whether the product should treat
them as having read a set of messages (or not).
One key detail to highlight is that we only mark messages as read
through these processes in views that contain all messages in a
thread; search views will never mark messages as read.
## Testing and development
In a Zulip development environment, you can use `manage.py

View File

@ -82,6 +82,9 @@ function stub_selected_message(msg) {
selected_message: function () {
return msg;
},
can_mark_messages_read: function () {
return true;
},
});
}

View File

@ -68,6 +68,7 @@ run_test('basics', () => {
assert(!filter.has_operand('stream', 'nada'));
assert(!filter.is_search());
assert(filter.can_mark_messages_read());
assert(filter.can_apply_locally());
operators = [
@ -78,6 +79,7 @@ run_test('basics', () => {
filter = new Filter(operators);
assert(filter.is_search());
assert(!filter.can_mark_messages_read());
assert(!filter.can_apply_locally());
assert(!filter.is_exactly('stream'));

View File

@ -527,8 +527,11 @@ run_test('unread_ops', () => {
// Make us not be in a narrow (somewhat hackily).
message_list.narrowed = undefined;
// Set current_message_list containing messages that
// can be marked read
set_global('current_msg_list', {
all_messages: () => test_messages,
can_mark_messages_read: () => true,
});
// Ignore these interactions for now:
@ -544,6 +547,12 @@ run_test('unread_ops', () => {
channel_post_opts = opts;
};
// First, test for a message list that cannot read messages
current_msg_list.can_mark_messages_read = () => false;
unread_ops.process_visible();
assert.deepEqual(channel_post_opts, undefined);
current_msg_list.can_mark_messages_read = () => true;
// Do the main thing we're testing!
unread_ops.process_visible();
@ -556,6 +565,7 @@ run_test('unread_ops', () => {
data: { messages: '[50]', op: 'add', flag: 'read' },
success: channel_post_opts.success,
});
});
/*

View File

@ -13,7 +13,10 @@ run_test('starred', () => {
const message = {
id: 50,
};
set_global('current_msg_list', {
all_messages: () => [message],
is_search: () => false,
});
var ui_updated;
ui.update_starred_view = () => {

View File

@ -37,7 +37,7 @@ run_test('basics', () => {
});
assert.equal(mld.is_search(), false);
assert(mld.can_mark_messages_read());
mld.add_anywhere(make_msgs([35, 25, 15, 45]));
assert_contents(mld, [15, 25, 35, 45]);

View File

@ -301,7 +301,9 @@ exports.respond_to_message = function (opts) {
return;
}
unread_ops.notify_server_message_read(message);
if (current_msg_list.can_mark_messages_read()) {
unread_ops.notify_server_message_read(message);
}
var stream = '';
var topic = '';

View File

@ -375,6 +375,10 @@ Filter.prototype = {
return this.has_operator('search');
},
can_mark_messages_read: function () {
return !this.has_operator('search');
},
can_apply_locally: function () {
if (this.is_search()) {
// The semantics for matching keywords are implemented

View File

@ -101,6 +101,10 @@ exports.toggle_starred_and_update_server = function (message) {
message.starred = !message.starred;
// Unlike most calls to mark messages as read, we don't check
// msg_list.can_mark_messages_read, because starring a message is an
// explicit interaction and we'd like to preserve the user
// expectation invariant that all starred messages are read.
unread_ops.notify_server_message_read(message);
ui.update_starred_view(message.id, message.starred);

View File

@ -122,6 +122,10 @@ exports.MessageList.prototype = {
return this.data.is_search();
},
can_mark_messages_read: function () {
return this.data.can_mark_messages_read();
},
clear: function MessageList_clear(opts) {
opts = _.extend({clear_selected_id: true}, opts);

View File

@ -142,7 +142,9 @@ MessageListData.prototype = {
is_search: function () {
return this.filter.is_search();
},
can_mark_messages_read: function () {
return this.filter.can_mark_messages_read();
},
_get_predicate: function () {
// We cache this.
if (!this.predicate) {

View File

@ -592,7 +592,12 @@ exports.by_topic = function (target_id, opts) {
exports.by_recipient(target_id, opts);
return;
}
// We don't check msg_list.can_mark_messages_read here only because
// the target msg_list isn't initialized yet; in any case, the
// message is about to be marked read in the new view.
unread_ops.notify_server_message_read(original);
var search_terms = [
{operator: 'stream', operand: original.stream},
{operator: 'topic', operand: util.get_message_topic(original)},
@ -606,7 +611,12 @@ exports.by_recipient = function (target_id, opts) {
opts = _.defaults({}, opts, {then_select_id: target_id});
// don't use current_msg_list as it won't work for muted messages or for out-of-narrow links
var message = message_store.get(target_id);
// We don't check msg_list.can_mark_messages_read here only because
// the target msg_list isn't initialized yet; in any case, the
// message is about to be marked read in the new view.
unread_ops.notify_server_message_read(message);
switch (message.type) {
case 'private':
exports.by('pm-with', message.reply_to, opts);

View File

@ -28,7 +28,9 @@ exports.down = function (with_centering) {
var current_msg_table = rows.get_table(current_msg_list.table_name);
message_viewport.scrollTop(current_msg_table.safeOuterHeight(true) -
message_viewport.height() * 0.1);
unread_ops.mark_current_list_as_read();
if (current_msg_list.can_mark_messages_read()) {
unread_ops.mark_current_list_as_read();
}
}
return;
@ -54,7 +56,9 @@ exports.to_end = function () {
message_viewport.set_last_movement_direction(1);
current_msg_list.select_id(next_id, {then_scroll: true,
from_scroll: true});
unread_ops.mark_current_list_as_read();
if (current_msg_list.can_mark_messages_read()) {
unread_ops.mark_current_list_as_read();
}
};
function amount_to_paginate() {
@ -114,7 +118,9 @@ exports.page_up = function () {
exports.page_down = function () {
if (message_viewport.at_bottom() && !current_msg_list.empty()) {
current_msg_list.select_id(current_msg_list.last().id, {then_scroll: false});
unread_ops.mark_current_list_as_read();
if (current_msg_list.can_mark_messages_read()) {
unread_ops.mark_current_list_as_read();
}
} else {
exports.page_down_the_right_amount();
}

View File

@ -124,7 +124,9 @@ exports.initialize = function initialize() {
} else {
messages = event.msg_list.message_range(event.previously_selected, event.id);
}
unread_ops.notify_server_messages_read(messages, {from: 'pointer'});
if (event.msg_list.can_mark_messages_read()) {
unread_ops.notify_server_messages_read(messages, {from: 'pointer'});
}
}
});
};

View File

@ -60,7 +60,6 @@ exports.process_read_messages_event = function (message_ids) {
// Skips any messages that are already marked as read.
exports.notify_server_messages_read = function (messages, options) {
options = options || {};
messages = unread.get_unread_messages(messages);
if (messages.length === 0) {
return;
@ -91,7 +90,8 @@ exports.process_visible = function () {
return;
}
if (message_viewport.bottom_message_visible()) {
if (message_viewport.bottom_message_visible() &&
current_msg_list.can_mark_messages_read()) {
exports.mark_current_list_as_read();
}
};