resolve topic: Add frontend support for is:resolved search keyword/filtering.

This commit adds the frontend support for `is:resolved` search
keyword. Test cases are added for the same.

Fixes: #18990.
This commit is contained in:
akshatdalton
2021-07-13 18:28:39 +00:00
committed by Tim Abbott
parent f5c4d51ed2
commit 6a45379a30
4 changed files with 115 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ const {run_test} = require("../zjsunit/test");
const $ = require("../zjsunit/zjquery");
const {page_params} = require("../zjsunit/zpage_params");
const message_edit = mock_esm("../../static/js/message_edit");
const message_store = mock_esm("../../static/js/message_store");
const stream_data = zrequire("stream_data");
@@ -201,6 +202,14 @@ test("basics", () => {
assert.ok(filter.contains_only_private_messages());
assert.ok(!filter.has_operator("search"));
assert.ok(filter.can_apply_locally());
operators = [{operator: "is", operand: "resolved"}];
filter = new Filter(operators);
assert.ok(!filter.contains_only_private_messages());
assert.ok(filter.can_mark_messages_read());
assert.ok(!filter.has_operator("search"));
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
});
function assert_not_mark_read_with_has_operands(additional_operators_to_test) {
@@ -266,6 +275,18 @@ function assert_not_mark_read_with_is_operands(additional_operators_to_test) {
is_operator = [{operator: "is", operand: "unread", negated: true}];
filter = new Filter(additional_operators_to_test.concat(is_operator));
assert.ok(!filter.can_mark_messages_read());
is_operator = [{operator: "is", operand: "resolved"}];
filter = new Filter(additional_operators_to_test.concat(is_operator));
if (additional_operators_to_test.length === 0) {
assert.ok(filter.can_mark_messages_read());
} else {
assert.ok(!filter.can_mark_messages_read());
}
is_operator = [{operator: "is", operand: "resolved", negated: true}];
filter = new Filter(additional_operators_to_test.concat(is_operator));
assert.ok(!filter.can_mark_messages_read());
}
function assert_not_mark_read_when_searching(additional_operators_to_test) {
@@ -605,6 +626,12 @@ test("predicate_basics", () => {
predicate = get_predicate([["in", "all"]]);
assert.ok(predicate({}));
predicate = get_predicate([["is", "resolved"]]);
const resolved_topic_name = message_edit.RESOLVED_TOPIC_PREFIX + "foo";
assert.ok(predicate({type: "stream", topic: resolved_topic_name}));
assert.ok(!predicate({topic: resolved_topic_name}));
assert.ok(!predicate({type: "stream", topic: "foo"}));
const unknown_stream_id = 999;
predicate = get_predicate([["in", "home"]]);
assert.ok(!predicate({stream_id: unknown_stream_id, stream: "unknown"}));
@@ -1190,6 +1217,14 @@ test("can_bucket_by", () => {
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-private"), false);
terms = [{operator: "is", operand: "resolved"}];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("stream", "topic"), false);
assert.equal(filter.can_bucket_by("stream"), false);
assert.equal(filter.can_bucket_by("pm-with"), false);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-private"), false);
});
test("term_type", () => {
@@ -1358,6 +1393,7 @@ test("navbar_helpers", () => {
const is_starred = [{operator: "is", operand: "starred"}];
const is_private = [{operator: "is", operand: "private"}];
const is_mentioned = [{operator: "is", operand: "mentioned"}];
const is_resolved = [{operator: "is", operand: "resolved"}];
const streams_public = [{operator: "streams", operand: "public"}];
const stream_topic_operators = [
{operator: "stream", operand: "foo"},
@@ -1416,6 +1452,13 @@ test("navbar_helpers", () => {
title: "translated: Mentions",
redirect_url_with_search: "/#narrow/is/mentioned",
},
{
operator: is_resolved,
is_common_narrow: true,
icon: "check",
title: "translated: Topics marked as resolved",
redirect_url_with_search: "/#narrow/topics/is/resolved",
},
{
operator: stream_topic_operators,
is_common_narrow: true,

View File

@@ -6,6 +6,7 @@ const {mock_esm, zrequire} = require("../zjsunit/namespace");
const {run_test} = require("../zjsunit/test");
const all_messages_data = mock_esm("../../static/js/all_messages_data");
const message_edit = mock_esm("../../static/js/message_edit");
const {Filter} = zrequire("../js/filter");
const {MessageListData} = zrequire("../js/message_list_data");
@@ -300,6 +301,54 @@ run_test("is:alerted with no unreads and one match", () => {
test_with(fixture);
});
run_test("is:resolved with one unread", () => {
const resolved_topic_name = message_edit.RESOLVED_TOPIC_PREFIX + "foo";
const fixture = {
filter_terms: [{operator: "is", operand: "resolved"}],
unread_info: {
flavor: "found",
msg_id: 56,
},
has_found_newest: true,
all_messages: [
{id: 55, type: "stream", topic: resolved_topic_name},
{id: 56, type: "stream", topic: resolved_topic_name},
{id: 57, type: "stream", topic: "foo"},
],
expected_id_info: {
target_id: undefined,
final_select_id: 56,
local_select_id: 56,
},
expected_msg_ids: [55, 56],
};
test_with(fixture);
});
run_test("is:resolved with no unreads", () => {
const resolved_topic_name = message_edit.RESOLVED_TOPIC_PREFIX + "foo";
const fixture = {
filter_terms: [{operator: "is", operand: "resolved"}],
unread_info: {
flavor: "not_found",
},
has_found_newest: true,
all_messages: [
{id: 55, type: "stream", topic: resolved_topic_name},
{id: 57, type: "stream", topic: "foo"},
],
expected_id_info: {
target_id: undefined,
final_select_id: 55,
local_select_id: 55,
},
expected_msg_ids: [55],
};
test_with(fixture);
});
run_test("search", () => {
const fixture = {
filter_terms: [{operator: "search", operand: "whatever"}],

View File

@@ -130,6 +130,11 @@ run_test("get_unread_ids", () => {
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, [stream_msg.id]);
terms = [{operator: "is", operand: "resolved"}];
set_filter(terms);
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, [stream_msg.id]);
terms = [{operator: "sender", operand: "me@example.com"}];
set_filter(terms);
// note that our candidate ids are just "all" ids now

View File

@@ -2,6 +2,7 @@ import Handlebars from "handlebars/runtime";
import _ from "lodash";
import {$t} from "./i18n";
import * as message_edit from "./message_edit";
import * as message_parser from "./message_parser";
import * as message_store from "./message_store";
import {page_params} from "./page_params";
@@ -92,6 +93,11 @@ function message_matches_search_term(message, operator, operand) {
return message.alerted;
case "unread":
return unread.message_unread(message);
case "resolved":
return (
message.type === "stream" &&
message.topic.startsWith(message_edit.RESOLVED_TOPIC_PREFIX)
);
default:
return false; // is:whatever returns false
}
@@ -451,6 +457,10 @@ export class Filter {
return true;
}
if (_.isEqual(term_types, ["is-resolved"])) {
return true;
}
if (_.isEqual(term_types, [])) {
// All view
return true;
@@ -484,7 +494,7 @@ export class Filter {
// can_mark_messages_read tests the following filters:
// stream, stream + topic,
// is: private, pm-with:,
// is: mentioned
// is: mentioned, is: resolved
if (this.can_mark_messages_read()) {
return true;
}
@@ -554,6 +564,8 @@ export class Filter {
"/#narrow/pm-with/" +
people.emails_to_slug(this.operands("pm-with").join(","))
);
case "is-resolved":
return "/#narrow/topics/is/resolved";
// TODO: It is ambiguous how we want to handle the 'sender' case,
// we may remove it in the future based on design decisions
case "sender":
@@ -590,6 +602,8 @@ export class Filter {
return "at";
case "pm-with":
return "envelope";
case "is-resolved":
return "check";
default:
return undefined;
}
@@ -640,6 +654,8 @@ export class Filter {
// can have the same return type as other cases.
return names.join(", ");
}
case "is-resolved":
return $t({defaultMessage: "Topics marked as resolved"});
}
}
/* istanbul ignore next */
@@ -849,6 +865,7 @@ export class Filter {
"is-private",
"is-starred",
"is-unread",
"is-resolved",
"has-link",
"has-image",
"has-attachment",