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 matching user expectations well for whether the product should treat
them as having read a set of messages (or not). 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 ## Testing and development
In a Zulip development environment, you can use `manage.py In a Zulip development environment, you can use `manage.py

View File

@ -82,6 +82,9 @@ function stub_selected_message(msg) {
selected_message: function () { selected_message: function () {
return msg; 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.has_operand('stream', 'nada'));
assert(!filter.is_search()); assert(!filter.is_search());
assert(filter.can_mark_messages_read());
assert(filter.can_apply_locally()); assert(filter.can_apply_locally());
operators = [ operators = [
@ -78,6 +79,7 @@ run_test('basics', () => {
filter = new Filter(operators); filter = new Filter(operators);
assert(filter.is_search()); assert(filter.is_search());
assert(!filter.can_mark_messages_read());
assert(!filter.can_apply_locally()); assert(!filter.can_apply_locally());
assert(!filter.is_exactly('stream')); assert(!filter.is_exactly('stream'));

View File

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

View File

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

View File

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

View File

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

View File

@ -375,6 +375,10 @@ Filter.prototype = {
return this.has_operator('search'); return this.has_operator('search');
}, },
can_mark_messages_read: function () {
return !this.has_operator('search');
},
can_apply_locally: function () { can_apply_locally: function () {
if (this.is_search()) { if (this.is_search()) {
// The semantics for matching keywords are implemented // 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; 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); unread_ops.notify_server_message_read(message);
ui.update_starred_view(message.id, message.starred); ui.update_starred_view(message.id, message.starred);

View File

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

View File

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

View File

@ -592,7 +592,12 @@ exports.by_topic = function (target_id, opts) {
exports.by_recipient(target_id, opts); exports.by_recipient(target_id, opts);
return; 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); unread_ops.notify_server_message_read(original);
var search_terms = [ var search_terms = [
{operator: 'stream', operand: original.stream}, {operator: 'stream', operand: original.stream},
{operator: 'topic', operand: util.get_message_topic(original)}, {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}); 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 // 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); 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); unread_ops.notify_server_message_read(message);
switch (message.type) { switch (message.type) {
case 'private': case 'private':
exports.by('pm-with', message.reply_to, opts); exports.by('pm-with', message.reply_to, opts);

View File

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

View File

@ -124,8 +124,10 @@ exports.initialize = function initialize() {
} else { } else {
messages = event.msg_list.message_range(event.previously_selected, event.id); messages = event.msg_list.message_range(event.previously_selected, event.id);
} }
if (event.msg_list.can_mark_messages_read()) {
unread_ops.notify_server_messages_read(messages, {from: 'pointer'}); 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. // Skips any messages that are already marked as read.
exports.notify_server_messages_read = function (messages, options) { exports.notify_server_messages_read = function (messages, options) {
options = options || {}; options = options || {};
messages = unread.get_unread_messages(messages); messages = unread.get_unread_messages(messages);
if (messages.length === 0) { if (messages.length === 0) {
return; return;
@ -91,7 +90,8 @@ exports.process_visible = function () {
return; 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(); exports.mark_current_list_as_read();
} }
}; };