user_settings: Add option to only read messages on scroll in topic.

After merging #24309, we want to add an additional option to the "mark
messages as read on scroll" setting where we only mark messages as read
on scroll in conversation views.
This commit is contained in:
Joelute 2023-04-19 01:00:47 -04:00 committed by Tim Abbott
parent 596abf6190
commit e999518d9a
8 changed files with 78 additions and 5 deletions

View File

@ -1101,4 +1101,12 @@ export class Filter {
}
return true;
}
is_conversation_view() {
const term_type = this.sorted_term_types();
if (_.isEqual(term_type, ["stream", "topic"]) || _.isEqual(term_type, ["pm-with"])) {
return true;
}
return false;
}
}

View File

@ -184,12 +184,19 @@ export class MessageList {
* The user has "Mark messages as read on scroll" option
turned on in their user settings.
*/
const filter = this.data.filter;
const is_conversation_view = filter === undefined ? false : filter.is_conversation_view();
return (
this.data.can_mark_messages_read() &&
!this.reading_prevented &&
!(
user_settings.web_mark_read_on_scroll_policy ===
web_mark_read_on_scroll_policy_values.never.code
) &&
!(
user_settings.web_mark_read_on_scroll_policy ===
web_mark_read_on_scroll_policy_values.conversation_only.code &&
!is_conversation_view
)
);
}

View File

@ -594,6 +594,7 @@ export function activate(raw_operators, opts) {
stream_list.handle_narrow_activated(current_filter);
typing_events.render_notifications_for_narrow();
message_view_header.initialize();
unread_ui.update_unread_banner();
// It is important to call this after other important updates
// like narrow filter and compose recipients happen.

View File

@ -44,11 +44,10 @@ export const web_mark_read_on_scroll_policy_values = {
code: 1,
description: $t({defaultMessage: "Always"}),
},
// The `conversation_only` option is not yet implemented.
// conversation_only: {
// code: 2,
// description: $t({defaultMessage: "Only in conversation views"}),
// },
conversation_only: {
code: 2,
description: $t({defaultMessage: "Only in conversation views"}),
},
never: {
code: 3,
description: $t({defaultMessage: "Never"}),

View File

@ -1,10 +1,12 @@
import $ from "jquery";
import render_mark_as_read_disabled_banner from "../templates/mark_as_read_disabled_banner.hbs";
import render_mark_as_read_only_in_conversation_view from "../templates/mark_as_read_only_in_conversation_view.hbs";
import render_mark_as_read_turned_off_banner from "../templates/mark_as_read_turned_off_banner.hbs";
import * as activity from "./activity";
import * as message_lists from "./message_lists";
import * as narrow_state from "./narrow_state";
import * as notifications from "./notifications";
import {page_params} from "./page_params";
import * as pm_list from "./pm_list";
@ -19,11 +21,19 @@ import {user_settings} from "./user_settings";
let user_closed_unread_banner = false;
export function update_unread_banner() {
const filter = narrow_state.filter();
const is_conversation_view = filter === undefined ? false : filter.is_conversation_view();
if (
user_settings.web_mark_read_on_scroll_policy ===
web_mark_read_on_scroll_policy_values.never.code
) {
$("#mark_as_read_turned_off_banner").html(render_mark_as_read_disabled_banner());
} else if (
user_settings.web_mark_read_on_scroll_policy ===
web_mark_read_on_scroll_policy_values.conversation_only.code &&
!is_conversation_view
) {
$("#mark_as_read_turned_off_banner").html(render_mark_as_read_only_in_conversation_view());
} else {
$("#mark_as_read_turned_off_banner").html(render_mark_as_read_turned_off_banner());

View File

@ -0,0 +1,12 @@
<p id="mark_as_read_turned_off_content">
{{#tr}}
Your Zulip app is <z-link>configured</z-link> to mark messages as read on scroll only in conversation views.
{{#*inline "z-link"}}<a href='help/marking-messages-as-read'>{{> @partial-block}}</a>{{/inline}}
{{/tr}}
</p>
<div id="mark_as_read_controls">
<button id="mark_view_read" class="btn btn-warning">
{{t 'Mark as read' }}
</button>
</div>
<button type="button" id="mark_as_read_close" class="close">×</button>

View File

@ -96,6 +96,7 @@ test("basics", () => {
assert.ok(filter.includes_full_stream_history());
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [
{operator: "stream", operand: "foo"},
@ -113,6 +114,7 @@ test("basics", () => {
assert.ok(!filter.is_personal_filter());
assert.ok(filter.can_bucket_by("stream"));
assert.ok(filter.can_bucket_by("stream", "topic"));
assert.ok(!filter.is_conversation_view());
operators = [
{operator: "stream", operand: "foo"},
@ -130,6 +132,7 @@ test("basics", () => {
assert.ok(!filter.is_personal_filter());
assert.ok(filter.can_bucket_by("stream"));
assert.ok(filter.can_bucket_by("stream", "topic"));
assert.ok(!filter.is_conversation_view());
// If our only stream operator is negated, then for all intents and purposes,
// we don't consider ourselves to have a stream operator, because we don't
@ -141,6 +144,7 @@ test("basics", () => {
assert.ok(!filter.can_mark_messages_read());
assert.ok(filter.supports_collapsing_recipients());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
// Negated searches are just like positive searches for our purposes, since
// the search logic happens on the backend and we need to have can_apply_locally()
@ -153,6 +157,7 @@ test("basics", () => {
assert.ok(!filter.can_mark_messages_read());
assert.ok(!filter.supports_collapsing_recipients());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
// Similar logic applies to negated "has" searches.
operators = [{operator: "has", operand: "images", negated: true}];
@ -164,6 +169,7 @@ test("basics", () => {
assert.ok(!filter.can_mark_messages_read());
assert.ok(!filter.supports_collapsing_recipients());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "streams", operand: "public", negated: true}];
filter = new Filter(operators);
@ -174,6 +180,7 @@ test("basics", () => {
assert.ok(filter.has_negated_operand("streams", "public"));
assert.ok(!filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "streams", operand: "public"}];
filter = new Filter(operators);
@ -185,6 +192,7 @@ test("basics", () => {
assert.ok(!filter.can_apply_locally());
assert.ok(filter.includes_full_stream_history());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "is", operand: "private"}];
filter = new Filter(operators);
@ -194,6 +202,7 @@ test("basics", () => {
assert.ok(!filter.has_operator("search"));
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "is", operand: "mentioned"}];
filter = new Filter(operators);
@ -203,6 +212,7 @@ test("basics", () => {
assert.ok(!filter.has_operator("search"));
assert.ok(filter.can_apply_locally());
assert.ok(filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "is", operand: "starred"}];
filter = new Filter(operators);
@ -212,6 +222,7 @@ test("basics", () => {
assert.ok(!filter.has_operator("search"));
assert.ok(filter.can_apply_locally());
assert.ok(filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "pm-with", operand: "joe@example.com"}];
filter = new Filter(operators);
@ -222,6 +233,7 @@ test("basics", () => {
assert.ok(!filter.has_operator("search"));
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(filter.is_conversation_view());
operators = [{operator: "pm-with", operand: "joe@example.com,jack@example.com"}];
filter = new Filter(operators);
@ -231,6 +243,7 @@ test("basics", () => {
assert.ok(filter.supports_collapsing_recipients());
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(filter.is_conversation_view());
operators = [{operator: "group-pm-with", operand: "joe@example.com"}];
filter = new Filter(operators);
@ -241,6 +254,7 @@ test("basics", () => {
assert.ok(filter.supports_collapsing_recipients());
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "is", operand: "resolved"}];
filter = new Filter(operators);
@ -250,6 +264,7 @@ test("basics", () => {
assert.ok(filter.supports_collapsing_recipients());
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
// Highly complex query to exercise
// filter.supports_collapsing_recipients loop.
@ -271,6 +286,23 @@ test("basics", () => {
// comment in the can_apply_locally implementation.
assert.ok(!filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [
{operator: "stream", operand: "foo"},
{operator: "topic", operand: "bar"},
];
filter = new Filter(operators);
assert.ok(!filter.is_search());
assert.ok(filter.can_mark_messages_read());
assert.ok(filter.supports_collapsing_recipients());
assert.ok(!filter.contains_only_private_messages());
assert.ok(filter.allow_use_first_unread_when_narrowing());
assert.ok(filter.includes_full_stream_history());
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(filter.is_conversation_view());
});
function assert_not_mark_read_with_has_operands(additional_operators_to_test) {

View File

@ -39,6 +39,10 @@ mock_esm("../src/recent_topics_util", {
mock_esm("../src/pm_list", {
handle_narrow_activated() {},
});
mock_esm("../src/unread_ui", {
reset_unread_banner() {},
update_unread_banner() {},
});
//
// We have strange hacks in narrow.activate to sleep 0