mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	unresolved: Add support for searching for unresolved topics.
Fixes: #31725.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							3f88fe5903
						
					
				
				
					commit
					d8f609d088
				
			@@ -750,6 +750,20 @@ export class Filter {
 | 
				
			|||||||
            const operand = term.operand;
 | 
					            const operand = term.operand;
 | 
				
			||||||
            const canonicalized_operator = Filter.canonicalize_operator(term.operator);
 | 
					            const canonicalized_operator = Filter.canonicalize_operator(term.operator);
 | 
				
			||||||
            if (canonicalized_operator === "is") {
 | 
					            if (canonicalized_operator === "is") {
 | 
				
			||||||
 | 
					                // Some operands have their own negative words, like
 | 
				
			||||||
 | 
					                // unresolved, rather than the default "exclude " prefix.
 | 
				
			||||||
 | 
					                const custom_negated_operand_phrases: Record<string, string> = {
 | 
				
			||||||
 | 
					                    resolved: "unresolved",
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                const negated_phrase = custom_negated_operand_phrases[operand];
 | 
				
			||||||
 | 
					                if (term.negated && negated_phrase !== undefined) {
 | 
				
			||||||
 | 
					                    return {
 | 
				
			||||||
 | 
					                        type: "is_operator",
 | 
				
			||||||
 | 
					                        verb: "",
 | 
				
			||||||
 | 
					                        operand: negated_phrase,
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const verb = term.negated ? "exclude " : "";
 | 
					                const verb = term.negated ? "exclude " : "";
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                    type: "is_operator",
 | 
					                    type: "is_operator",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -589,12 +589,29 @@ function get_special_filter_suggestions(
 | 
				
			|||||||
    // Negating suggestions on is_search_operand_negated is required for
 | 
					    // Negating suggestions on is_search_operand_negated is required for
 | 
				
			||||||
    // suggesting negated terms.
 | 
					    // suggesting negated terms.
 | 
				
			||||||
    if (last.negated === true || is_search_operand_negated) {
 | 
					    if (last.negated === true || is_search_operand_negated) {
 | 
				
			||||||
        suggestions = suggestions.map((suggestion) => ({
 | 
					        suggestions = suggestions
 | 
				
			||||||
            search_string: "-" + suggestion.search_string,
 | 
					            .map((suggestion) => {
 | 
				
			||||||
            description_html: "exclude " + suggestion.description_html,
 | 
					                // If the search_string is "is:resolved", we want to suggest "Unresolved topics"
 | 
				
			||||||
            incompatible_patterns: suggestion.incompatible_patterns,
 | 
					                // instead of "Exclude resolved topics".
 | 
				
			||||||
            is_people: false,
 | 
					                if (suggestion.search_string === "is:resolved") {
 | 
				
			||||||
        }));
 | 
					                    return {
 | 
				
			||||||
 | 
					                        ...suggestion,
 | 
				
			||||||
 | 
					                        search_string: "-" + suggestion.search_string,
 | 
				
			||||||
 | 
					                        description_html: "unresolved topics",
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                } else if (suggestion.search_string === "-is:resolved") {
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                    ...suggestion,
 | 
				
			||||||
 | 
					                    search_string: "-" + suggestion.search_string,
 | 
				
			||||||
 | 
					                    description_html: "exclude " + suggestion.description_html,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .filter(
 | 
				
			||||||
 | 
					                (suggestion): suggestion is SuggestionAndIncompatiblePatterns =>
 | 
				
			||||||
 | 
					                    suggestion !== null,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const last_string = Filter.unparse([last]).toLowerCase();
 | 
					    const last_string = Filter.unparse([last]).toLowerCase();
 | 
				
			||||||
@@ -713,6 +730,17 @@ function get_is_filter_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Sugge
 | 
				
			|||||||
                {operator: "dm-including"},
 | 
					                {operator: "dm-including"},
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            search_string: "-is:resolved",
 | 
				
			||||||
 | 
					            description_html: "unresolved topics",
 | 
				
			||||||
 | 
					            is_people: false,
 | 
				
			||||||
 | 
					            incompatible_patterns: [
 | 
				
			||||||
 | 
					                {operator: "is", operand: "resolved"},
 | 
				
			||||||
 | 
					                {operator: "is", operand: "dm"},
 | 
				
			||||||
 | 
					                {operator: "dm"},
 | 
				
			||||||
 | 
					                {operator: "dm-including"},
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    const special_filtered_suggestions = get_special_filter_suggestions(last, terms, suggestions);
 | 
					    const special_filtered_suggestions = get_special_filter_suggestions(last, terms, suggestions);
 | 
				
			||||||
    // Suggest "is:dm" to anyone with "is:private" in their muscle memory
 | 
					    // Suggest "is:dm" to anyone with "is:private" in their muscle memory
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,10 @@
 | 
				
			|||||||
            {{~!-- squash whitespace --~}}
 | 
					            {{~!-- squash whitespace --~}}
 | 
				
			||||||
            {{this.verb}}followed topics
 | 
					            {{this.verb}}followed topics
 | 
				
			||||||
            {{~!-- squash whitespace --~}}
 | 
					            {{~!-- squash whitespace --~}}
 | 
				
			||||||
 | 
					        {{else if (eq this.operand "unresolved")}}
 | 
				
			||||||
 | 
					            {{~!-- squash whitespace --~}}
 | 
				
			||||||
 | 
					            {{this.verb}}unresolved topics
 | 
				
			||||||
 | 
					            {{~!-- squash whitespace --~}}
 | 
				
			||||||
        {{else}}
 | 
					        {{else}}
 | 
				
			||||||
            {{~!-- squash whitespace --~}}
 | 
					            {{~!-- squash whitespace --~}}
 | 
				
			||||||
            invalid {{this.operand}} operand for is operator
 | 
					            invalid {{this.operand}} operand for is operator
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -133,6 +133,12 @@
 | 
				
			|||||||
                            {{t 'Narrow to messages in resolved topics.'}}
 | 
					                            {{t 'Narrow to messages in resolved topics.'}}
 | 
				
			||||||
                        </td>
 | 
					                        </td>
 | 
				
			||||||
                    </tr>
 | 
					                    </tr>
 | 
				
			||||||
 | 
					                    <tr>
 | 
				
			||||||
 | 
					                        <td class="operator">-is:resolved</td>
 | 
				
			||||||
 | 
					                        <td class="definition">
 | 
				
			||||||
 | 
					                            {{t 'Narrow to messages in unresolved topics.'}}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
                    <tr>
 | 
					                    <tr>
 | 
				
			||||||
                        <td class="operator">is:followed</td>
 | 
					                        <td class="operator">is:followed</td>
 | 
				
			||||||
                        <td class="definition">
 | 
					                        <td class="definition">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1563,13 +1563,18 @@ test("describe", ({mock_template, override}) => {
 | 
				
			|||||||
    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
					    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    narrow = [{operator: "is", operand: "resolved"}];
 | 
					    narrow = [{operator: "is", operand: "resolved"}];
 | 
				
			||||||
    string = "topics marked as resolved";
 | 
					    string = "resolved topics";
 | 
				
			||||||
    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
					    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    narrow = [{operator: "is", operand: "followed"}];
 | 
					    narrow = [{operator: "is", operand: "followed"}];
 | 
				
			||||||
    string = "followed topics";
 | 
					    string = "followed topics";
 | 
				
			||||||
    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
					    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // operands with their own negative words, like resolved.
 | 
				
			||||||
 | 
					    narrow = [{operator: "is", operand: "resolved", negated: true}];
 | 
				
			||||||
 | 
					    string = "unresolved topics";
 | 
				
			||||||
 | 
					    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    narrow = [{operator: "is", operand: "something_we_do_not_support"}];
 | 
					    narrow = [{operator: "is", operand: "something_we_do_not_support"}];
 | 
				
			||||||
    string = "invalid something_we_do_not_support operand for is operator";
 | 
					    string = "invalid something_we_do_not_support operand for is operator";
 | 
				
			||||||
    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
					    assert.equal(Filter.search_description_as_html(narrow, false), string);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -377,6 +377,7 @@ test("empty_query_suggestions", () => {
 | 
				
			|||||||
        "is:alerted",
 | 
					        "is:alerted",
 | 
				
			||||||
        "is:unread",
 | 
					        "is:unread",
 | 
				
			||||||
        "is:resolved",
 | 
					        "is:resolved",
 | 
				
			||||||
 | 
					        "-is:resolved",
 | 
				
			||||||
        "sender:myself@zulip.com",
 | 
					        "sender:myself@zulip.com",
 | 
				
			||||||
        `channel:${devel_id}`,
 | 
					        `channel:${devel_id}`,
 | 
				
			||||||
        `channel:${office_id}`,
 | 
					        `channel:${office_id}`,
 | 
				
			||||||
@@ -518,7 +519,7 @@ test("check_is_suggestions", ({override, mock_template}) => {
 | 
				
			|||||||
    assert.equal(describe("-is:mentioned"), "Exclude @-mentions");
 | 
					    assert.equal(describe("-is:mentioned"), "Exclude @-mentions");
 | 
				
			||||||
    assert.equal(describe("-is:alerted"), "Exclude alerted messages");
 | 
					    assert.equal(describe("-is:alerted"), "Exclude alerted messages");
 | 
				
			||||||
    assert.equal(describe("-is:unread"), "Exclude unread messages");
 | 
					    assert.equal(describe("-is:unread"), "Exclude unread messages");
 | 
				
			||||||
    assert.equal(describe("-is:resolved"), "Exclude topics marked as resolved");
 | 
					    assert.equal(describe("-is:resolved"), "Unresolved topics");
 | 
				
			||||||
    assert.equal(describe("-is:followed"), "Exclude followed topics");
 | 
					    assert.equal(describe("-is:followed"), "Exclude followed topics");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // operand suggestions follow.
 | 
					    // operand suggestions follow.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user