mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	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:
		@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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"}],
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user