narrow: Add frontend support for `is:dm` narrow and `/is/dm` URL.

Adds support in the web app for `is` operator with the `dm` operand.
This will deprecate the `is` operator with the `private` operand,
but we keep support for backwards-compatibility with links/URLs.

This commit updates the web app default behaviors to default to
the new narrow/URLs `is:dm` and `/#narrow/is/dm` when navigating
and searching in the app.

There is some general clean up of references to private messages
or PMs to be either direct messages or DMs in these changes.

The general API changelog and documentation updates will be done
in a final commit in the series of commits that adds support for
the various new direct message narrows.
This commit is contained in:
Lauryn Menard 2023-04-07 14:03:34 +02:00 committed by Tim Abbott
parent 33886575b2
commit d379020726
18 changed files with 215 additions and 171 deletions

View File

@ -730,7 +730,7 @@ export function initialize() {
},
);
/* The PRIVATE MESSAGES label's click behavior is complicated;
/* The DIRECT MESSAGES label's click behavior is complicated;
* only when zoomed in does it have a navigation effect, so we need
* this click handler rather than just a link. */
$("body").on(
@ -740,7 +740,7 @@ export function initialize() {
e.preventDefault();
e.stopPropagation();
window.location.hash = "narrow/is/private";
window.location.hash = "narrow/is/dm";
},
);

View File

@ -91,7 +91,7 @@ function message_matches_search_term(message, operator, operand) {
case "is":
switch (operand) {
case "private":
case "dm":
return message.type === "private";
case "starred":
return message.starred;
@ -228,6 +228,12 @@ export class Filter {
operator = Filter.canonicalize_operator(operator);
switch (operator) {
case "is":
// "is:private" was renamed to "is:dm"
if (operand === "private") {
operand = "dm";
}
break;
case "has":
// images -> image, etc.
operand = operand.replace(/s$/, "");
@ -451,7 +457,7 @@ export class Filter {
// All search/narrow term types, including negations, with the
// property that if a message is in the view, then any other
// message sharing its recipient (stream/topic or private
// message sharing its recipient (stream/topic or direct
// message recipient) must also be present in the view.
const valid_term_types = new Set([
"stream",
@ -461,8 +467,8 @@ export class Filter {
"pm-with",
"group-pm-with",
"not-group-pm-with",
"is-private",
"not-is-private",
"is-dm",
"not-is-dm",
"is-resolved",
"not-is-resolved",
"in-home",
@ -505,7 +511,7 @@ export class Filter {
return true;
}
if (_.isEqual(term_types, ["is-private"])) {
if (_.isEqual(term_types, ["is-dm"])) {
return true;
}
@ -549,8 +555,8 @@ export class Filter {
is_common_narrow() {
// can_mark_messages_read tests the following filters:
// stream, stream + topic,
// is: private, pm-with:,
// is: mentioned, is: resolved
// is:dm, pm-with:,
// is:mentioned, is:resolved
if (this.can_mark_messages_read()) {
return true;
}
@ -607,8 +613,8 @@ export class Filter {
return (
"/#narrow/stream/" + stream_data.name_to_slug(this.operands("stream")[0])
);
case "is-private":
return "/#narrow/is/private";
case "is-dm":
return "/#narrow/is/dm";
case "is-starred":
return "/#narrow/is/starred";
case "is-mentioned":
@ -655,7 +661,7 @@ export class Filter {
}
context.zulip_icon = "hashtag";
break;
case "is-private":
case "is-dm":
context.icon = "envelope";
break;
case "is-starred":
@ -719,7 +725,7 @@ export class Filter {
return $t({defaultMessage: "Starred messages"});
case "is-mentioned":
return $t({defaultMessage: "Mentions"});
case "is-private":
case "is-dm":
return $t({defaultMessage: "Direct messages"});
case "is-resolved":
return $t({defaultMessage: "Topics marked as resolved"});
@ -742,7 +748,7 @@ export class Filter {
contains_only_private_messages() {
return (
(this.has_operator("is") && this.operands("is")[0] === "private") ||
(this.has_operator("is") && this.operands("is")[0] === "dm") ||
this.has_operator("pm-with") ||
this.has_operator("group-pm-with")
);
@ -802,7 +808,7 @@ export class Filter {
return terms;
}
return terms.filter((term) => Filter.term_type(term) !== "is-private");
return terms.filter((term) => Filter.term_type(term) !== "is-dm");
}
_canonicalize_operators(operators_mixed_case) {
@ -938,7 +944,7 @@ export class Filter {
"id",
"is-alerted",
"is-mentioned",
"is-private",
"is-dm",
"is-starred",
"is-unread",
"is-resolved",
@ -1024,6 +1030,7 @@ export class Filter {
return verb + operand + " messages";
case "mentioned":
return verb + "@-mentions";
case "dm":
case "private":
return verb + "direct messages";
case "resolved":

View File

@ -273,7 +273,7 @@ export function is_spectator_compatible(hash) {
// This implementation should agree with the similar function in zerver/lib/narrow.py.
const web_public_allowed_hashes = [
"",
// full #narrow hash handled in narrow.is_spectator_compatible
// full #narrow hash handled in filter.is_spectator_compatible
"narrow",
// TODO/compatibility: #recent_topics was renamed to #recent
// in 2022. We should support the old URL fragment at least

View File

@ -824,7 +824,7 @@ export function process_hotkey(e, hotkey) {
switch (event_name) {
case "narrow_private":
return do_narrow_action((target, opts) => {
narrow.by("is", "private", opts);
narrow.by("is", "dm", opts);
});
case "query_streams":
stream_list.initiate_search();

View File

@ -994,7 +994,7 @@ export function to_compose_target() {
// If there are no recipients or any recipient is
// invalid, narrow to all PMs.
if (emails.length === 0 || invalid.length > 0) {
by("is", "private", opts);
by("is", "dm", opts);
return;
}
by("pm-with", util.normalize_recipients(recipient_string), opts);

View File

@ -187,8 +187,8 @@ function pick_empty_narrow_banner() {
},
),
};
case "private":
// You have no private messages.
case "dm":
// You have no direct messages.
if (
page_params.realm_private_message_policy ===
settings_config.private_message_policy_values.disabled.code
@ -308,7 +308,7 @@ function pick_empty_narrow_banner() {
};
}
if (!first_operand.includes(",")) {
// You have no private messages with this person
// You have no direct messages with this person
if (people.is_current_user(first_operand)) {
return {
title: $t({

View File

@ -255,7 +255,7 @@ export function _possible_unread_message_ids() {
return unread.get_msg_ids_for_user_ids_string(current_filter_pm_string);
}
if (current_filter.can_bucket_by("is-private")) {
if (current_filter.can_bucket_by("is-dm")) {
return unread.get_msg_ids_for_private();
}
@ -279,12 +279,13 @@ export function _possible_unread_message_ids() {
return undefined;
}
// Are we narrowed to PMs: all PMs or PMs with particular people.
// Are we narrowed to direct messages: all direct messages
// or direct messages with particular people.
export function narrowed_to_pms() {
if (current_filter === undefined) {
return false;
}
return current_filter.has_operator("pm-with") || current_filter.has_operand("is", "private");
return current_filter.has_operator("pm-with") || current_filter.has_operand("is", "dm");
}
export function narrowed_by_pm_reply() {

View File

@ -146,9 +146,7 @@ function scroll_all_private_into_view() {
export function handle_narrow_activated(filter) {
const active_filter = filter;
const is_all_private_message_view = _.isEqual(active_filter.sorted_term_types(), [
"is-private",
]);
const is_all_private_message_view = _.isEqual(active_filter.sorted_term_types(), ["is-dm"]);
const narrow_to_private_messages_section = active_filter.operands("pm-with").length !== 0;
if (is_all_private_message_view) {

View File

@ -100,7 +100,7 @@ function get_stream_suggestions(last, operators) {
const invalid = [
{operator: "stream"},
{operator: "streams"},
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "pm-with"},
{operator: "group-pm-with"},
];
@ -194,7 +194,7 @@ function get_group_suggestions(last, operators) {
let terms = [term];
if (negated) {
terms = [{operator: "is", operand: "private"}, term];
terms = [{operator: "is", operand: "dm"}, term];
}
return {
@ -222,9 +222,9 @@ function make_people_getter(last) {
let query;
// This next block is designed to match the behavior of the
// `is:private` block in get_person_suggestions
if (last.operator === "is" && last.operand === "private") {
// This next block is designed to match the behavior
// of the "is:dm" block in get_person_suggestions.
if (last.operator === "is" && last.operand === "dm") {
query = "";
} else {
query = last.operand;
@ -238,8 +238,8 @@ function make_people_getter(last) {
// Possible args for autocomplete_operator: pm-with, sender, from, group-pm-with
function get_person_suggestions(people_getter, last, operators, autocomplete_operator) {
if (last.operator === "is" && last.operand === "private") {
// Interpret 'is:private' as equivalent to 'pm-with:'
if (last.operator === "is" && last.operand === "dm") {
// Interpret "is:dm" as equivalent to "pm-with:".
last = {operator: "pm-with", operand: "", negated: false};
}
@ -289,9 +289,10 @@ function get_person_suggestions(people_getter, last, operators, autocomplete_ope
},
];
if (autocomplete_operator === "pm-with" && last.negated) {
// In the special case of '-pm-with', add 'is:private' before it
// because we assume the user still wants to narrow to PMs
terms.unshift({operator: "is", operand: "private"});
// In the special case of "-pm-with", add "is:dm" before it
// because we assume the user still wants to narrow to direct
// messages.
terms.unshift({operator: "is", operand: "dm"});
}
return {
@ -349,7 +350,7 @@ export function get_topic_suggestions_from_candidates({candidate_topics, guess})
function get_topic_suggestions(last, operators) {
const invalid = [
{operator: "pm-with"},
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "group-pm-with"},
{operator: "topic"},
];
@ -504,7 +505,7 @@ function get_streams_filter_suggestions(last, operators) {
search_string: "streams:public",
description_html: "All public streams in organization",
invalid: [
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "stream"},
{operator: "group-pm-with"},
{operator: "pm-with"},
@ -518,10 +519,10 @@ function get_streams_filter_suggestions(last, operators) {
function get_is_filter_suggestions(last, operators) {
const suggestions = [
{
search_string: "is:private",
search_string: "is:dm",
description_html: "direct messages",
invalid: [
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "is", operand: "resolved"},
{operator: "stream"},
{operator: "pm-with"},
@ -553,7 +554,7 @@ function get_is_filter_suggestions(last, operators) {
description_html: "topics marked as resolved",
invalid: [
{operator: "is", operand: "resolved"},
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "pm-with"},
{operator: "group-pm-with"},
],
@ -626,6 +627,14 @@ function get_sent_by_me_suggestions(last, operators) {
}
function get_operator_suggestions(last) {
// Suggest "is:dm" to anyone with "is:private" in their muscle memory
if (last.operator === "is" && common.phrase_match(last.operand, "private")) {
const is_dm = format_as_suggestion([
{operator: last.operator, operand: "dm", negated: last.negated},
]);
return [is_dm];
}
if (!(last.operator === "search")) {
return [];
}

View File

@ -400,7 +400,7 @@ export function initialize() {
defaultMessage: "Currently viewing the entire stream.",
});
} else if (
_.isEqual(narrow_filter.sorted_term_types(), ["is-private"]) &&
_.isEqual(narrow_filter.sorted_term_types(), ["is-dm"]) &&
compose_state.get_message_type() === "private"
) {
display_current_view = $t({

View File

@ -77,7 +77,7 @@
<h4 class="sidebar-title toggle_private_messages_section">{{t 'DIRECT MESSAGES' }}</h4>
</span>
<span class="unread_count"></span>
<a id="show_all_private_messages" href="#narrow/is/private" data-tooltip-template-id="show-all-pms-template">
<a id="show_all_private_messages" href="#narrow/is/dm" data-tooltip-template-id="show-all-pms-template">
<i class="fa fa-align-right" aria-label="{{t 'All direct messages' }}"></i>
</a>
<template id="show-all-pms-template">

View File

@ -21,7 +21,7 @@ function test(label, f) {
test("basics", () => {
const hash1 = "#settings/profile";
const hash2 = "#narrow/is/private";
const hash2 = "#narrow/is/dm";
browser_history.go_to_location(hash1);
assert.equal(window.location.hash, hash1);
@ -69,7 +69,7 @@ test("update internal hash if required", ({override_rewire}) => {
test("web-public view hash restore", () => {
browser_history.update("#");
assert.equal(window.location.hash, "");
const new_hash = "#narrow/is/private";
const new_hash = "#narrow/is/dm";
browser_history.update(new_hash);
assert.equal(window.location.hash, new_hash);
browser_history.return_to_web_public_hash();

View File

@ -194,7 +194,7 @@ test("basics", () => {
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
operators = [{operator: "is", operand: "private"}];
operators = [{operator: "is", operand: "dm"}];
filter = new Filter(operators);
assert.ok(filter.contains_only_private_messages());
assert.ok(filter.can_mark_messages_read());
@ -204,6 +204,12 @@ test("basics", () => {
assert.ok(!filter.is_personal_filter());
assert.ok(!filter.is_conversation_view());
// "is:private" was renamed to "is:dm"
operators = [{operator: "is", operand: "private"}];
filter = new Filter(operators);
assert.ok(filter.has_operand("is", "dm"));
assert.ok(!filter.has_operand("is", "private"));
operators = [{operator: "is", operand: "mentioned"}];
filter = new Filter(operators);
assert.ok(!filter.contains_only_private_messages());
@ -270,7 +276,7 @@ test("basics", () => {
// filter.supports_collapsing_recipients loop.
operators = [
{operator: "is", operand: "resolved", negated: true},
{operator: "is", operand: "private", negated: true},
{operator: "is", operand: "dm", negated: true},
{operator: "stream", operand: "stream_name", negated: true},
{operator: "streams", operand: "web-public", negated: true},
{operator: "streams", operand: "public"},
@ -444,12 +450,12 @@ test("can_mark_messages_read", () => {
assert_not_mark_read_when_searching(group_pm);
assert_not_mark_read_when_searching(pm_with);
const is_private = [{operator: "is", operand: "private"}];
filter = new Filter(is_private);
const is_dm = [{operator: "is", operand: "dm"}];
filter = new Filter(is_dm);
assert.ok(filter.can_mark_messages_read());
assert_not_mark_read_with_is_operands(is_private);
assert_not_mark_read_with_has_operands(is_private);
assert_not_mark_read_when_searching(is_private);
assert_not_mark_read_with_is_operands(is_dm);
assert_not_mark_read_with_has_operands(is_dm);
assert_not_mark_read_when_searching(is_dm);
const in_all = [{operator: "in", operand: "all"}];
filter = new Filter(in_all);
@ -593,17 +599,17 @@ test("redundancies", () => {
terms = [
{operator: "pm-with", operand: "joe@example.com,"},
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
];
filter = new Filter(terms);
assert.ok(filter.can_bucket_by("pm-with"));
terms = [
{operator: "pm-with", operand: "joe@example.com,", negated: true},
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
];
filter = new Filter(terms);
assert.ok(filter.can_bucket_by("is-private", "not-pm-with"));
assert.ok(filter.can_bucket_by("is-dm", "not-pm-with"));
});
test("canonicalization", () => {
@ -690,7 +696,7 @@ test("predicate_basics", () => {
predicate = get_predicate([["topic", "Bar"]]);
assert.ok(!predicate({type: "private"}));
predicate = get_predicate([["is", "private"]]);
predicate = get_predicate([["is", "dm"]]);
assert.ok(predicate({type: "private"}));
assert.ok(!predicate({type: "stream"}));
@ -814,7 +820,7 @@ test("predicate_basics", () => {
);
assert.ok(
!predicate({
// you must be a part of the group pm
// you must be a part of the group direct message
type: "private",
display_recipient: [{id: joe.user_id}, {id: steve.user_id}],
}),
@ -1167,7 +1173,7 @@ test("describe", () => {
assert.equal(Filter.describe(narrow), string);
narrow = [
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "search", operand: "lunch"},
];
string = "direct messages, search for lunch";
@ -1210,7 +1216,7 @@ test("describe", () => {
assert.equal(Filter.describe(narrow), string);
narrow = [
{operator: "is", operand: "private"},
{operator: "is", operand: "dm"},
{operator: "search", operand: "lunch", negated: true},
];
string = "direct messages, exclude lunch";
@ -1287,17 +1293,17 @@ test("can_bucket_by", () => {
assert.equal(filter.can_bucket_by("stream", "topic"), false);
assert.equal(filter.can_bucket_by("pm-with"), true);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-private"), false);
assert.equal(filter.can_bucket_by("is-dm"), false);
terms = [{operator: "is", operand: "private"}];
terms = [{operator: "is", operand: "dm"}];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-private"), true);
assert.equal(filter.can_bucket_by("is-dm"), true);
terms = [{operator: "is", operand: "mentioned"}];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("is-mentioned"), true);
assert.equal(filter.can_bucket_by("is-private"), false);
assert.equal(filter.can_bucket_by("is-dm"), false);
terms = [
{operator: "is", operand: "mentioned"},
@ -1305,17 +1311,17 @@ test("can_bucket_by", () => {
];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("is-mentioned"), true);
assert.equal(filter.can_bucket_by("is-private"), false);
assert.equal(filter.can_bucket_by("is-dm"), false);
// The call below returns false for somewhat arbitrary
// reasons -- we say is-private has precedence over
// reasons -- we say is-dm has precedence over
// is-starred.
assert.equal(filter.can_bucket_by("is-starred"), false);
terms = [{operator: "is", operand: "mentioned", negated: true}];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-private"), false);
assert.equal(filter.can_bucket_by("is-dm"), false);
terms = [{operator: "is", operand: "resolved"}];
filter = new Filter(terms);
@ -1323,7 +1329,7 @@ test("can_bucket_by", () => {
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);
assert.equal(filter.can_bucket_by("is-dm"), false);
});
test("term_type", () => {
@ -1343,6 +1349,7 @@ test("term_type", () => {
assert_term_type(term("stream", "whatever"), "stream");
assert_term_type(term("pm-with", "whomever"), "pm-with");
assert_term_type(term("pm-with", "whomever", true), "not-pm-with");
assert_term_type(term("is", "dm"), "is-dm");
assert_term_type(term("is", "private"), "is-private");
assert_term_type(term("has", "link"), "has-link");
assert_term_type(term("has", "attachment", true), "not-has-attachment");
@ -1493,7 +1500,7 @@ test("navbar_helpers", () => {
const in_home = [{operator: "in", operand: "home"}];
const in_all = [{operator: "in", operand: "all"}];
const is_starred = [{operator: "is", operand: "starred"}];
const is_private = [{operator: "is", operand: "private"}];
const is_dm = [{operator: "is", operand: "dm"}];
const is_mentioned = [{operator: "is", operand: "mentioned"}];
const is_resolved = [{operator: "is", operand: "resolved"}];
const streams_public = [{operator: "streams", operand: "public"}];
@ -1553,11 +1560,11 @@ test("navbar_helpers", () => {
redirect_url_with_search: "#",
},
{
operator: is_private,
operator: is_dm,
is_common_narrow: true,
icon: "envelope",
title: "translated: Direct messages",
redirect_url_with_search: "/#narrow/is/private",
redirect_url_with_search: "/#narrow/is/dm",
},
{
operator: is_mentioned,
@ -1783,8 +1790,12 @@ run_test("is_spectator_compatible", () => {
]),
);
assert.ok(!Filter.is_spectator_compatible([{operator: "is", operand: "starred"}]));
assert.ok(!Filter.is_spectator_compatible([{operator: "is", operand: "private"}]));
assert.ok(!Filter.is_spectator_compatible([{operator: "is", operand: "dm"}]));
assert.ok(Filter.is_spectator_compatible([{operator: "streams", operand: "public"}]));
// Malformed input not allowed
assert.ok(!Filter.is_spectator_compatible([{operator: "has"}]));
// "is:private" was renamed to "is:dm"
assert.ok(!Filter.is_spectator_compatible([{operator: "is", operand: "private"}]));
});

View File

@ -321,13 +321,13 @@ run_test("hash_interactions", ({override}) => {
run_test("save_narrow", ({override}) => {
const helper = test_helper({override});
let operators = [{operator: "is", operand: "private"}];
let operators = [{operator: "is", operand: "dm"}];
blueslip.expect("warn", "browser does not support pushState");
hashchange.save_narrow(operators);
helper.assert_events([[message_viewport, "stop_auto_scrolling"]]);
assert.equal(window.location.hash, "#narrow/is/private");
assert.equal(window.location.hash, "#narrow/is/dm");
let url_pushed;
override(history, "pushState", (state, title, url) => {

View File

@ -286,10 +286,10 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// organization has disabled sending private messages
// organization has disabled sending direct messages
page_params.realm_private_message_policy =
settings_config.private_message_policy_values.disabled.code;
set_filter([["is", "private"]]);
set_filter([["is", "dm"]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
$(".empty_feed_notice_main").html(),
@ -298,10 +298,10 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// sending private messages enabled
// sending direct messages enabled
page_params.realm_private_message_policy =
settings_config.private_message_policy_values.by_anyone.code;
set_filter([["is", "private"]]);
set_filter([["is", "dm"]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
$(".empty_feed_notice_main").html(),
@ -325,7 +325,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
empty_narrow_html("translated: No topics are marked as resolved."),
);
// organization has disabled sending private messages
// organization has disabled sending direct messages
page_params.realm_private_message_policy =
settings_config.private_message_policy_values.disabled.code;
@ -354,8 +354,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// private messages with a bot are possible even though
// the organization has disabled sending private messages
// direct messages with a bot are possible even though
// the organization has disabled sending direct messages
people.add_active_user(bot);
set_filter([["pm-with", "bot@example.com"]]);
narrow_banner.show_empty_narrow_message();
@ -367,8 +367,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// group private messages with bots are not possible when
// sending private messages is disabled
// group direct messages with bots are not possible when
// sending direct messages is disabled
set_filter([["pm-with", bot.email + "," + alice.email]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
@ -378,7 +378,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// sending private messages enabled
// sending direct messages enabled
page_params.realm_private_message_policy =
settings_config.private_message_policy_values.by_anyone.code;
set_filter([["pm-with", "alice@example.com"]]);
@ -413,7 +413,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// organization has disabled sending private messages
// organization has disabled sending direct messages
page_params.realm_private_message_policy =
settings_config.private_message_policy_values.disabled.code;
@ -434,8 +434,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// group private messages with bots are not possible when
// sending private messages is disabled
// group direct messages with bots are not possible when
// sending direct messages is disabled
set_filter([["group-pm-with", "bot@example.com"]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
@ -445,7 +445,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
// sending private messages enabled
// sending direct messages enabled
page_params.realm_private_message_policy =
settings_config.private_message_policy_values.by_anyone.code;
set_filter([["group-pm-with", "alice@example.com"]]);
@ -748,21 +748,21 @@ run_test("narrow_to_compose_target PMs", ({override, override_rewire}) => {
args.called = false;
narrow.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.operators, [{operator: "is", operand: "private"}]);
assert.deepEqual(args.operators, [{operator: "is", operand: "dm"}]);
// Test with all invalid persons
emails = "alice,random,ray";
args.called = false;
narrow.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.operators, [{operator: "is", operand: "private"}]);
assert.deepEqual(args.operators, [{operator: "is", operand: "dm"}]);
// Test with no persons
emails = "";
args.called = false;
narrow.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.operators, [{operator: "is", operand: "private"}]);
assert.deepEqual(args.operators, [{operator: "is", operand: "dm"}]);
});
run_test("narrow_compute_title", ({override}) => {

View File

@ -158,14 +158,20 @@ run_test("get_unread_ids", () => {
msg_id: private_msg.id,
});
// "is:private" was renamed to "is:dm"
terms = [{operator: "is", operand: "private"}];
set_filter(terms);
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, [private_msg.id]);
terms = [{operator: "is", operand: "dm"}];
set_filter(terms);
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, [private_msg.id]);
// For a negated search, our candidate ids will be all
// unread messages, even ones that don't pass the filter.
terms = [{operator: "is", operand: "private", negated: true}];
terms = [{operator: "is", operand: "dm", negated: true}];
set_filter(terms);
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, [stream_msg.id, private_msg.id]);

View File

@ -112,11 +112,11 @@ test("subset_suggestions", () => {
assert.deepEqual(suggestions.strings, expected);
});
test("private_suggestions", () => {
let query = "is:private";
test("dm_suggestions", () => {
let query = "is:dm";
let suggestions = get_suggestions("", query);
let expected = [
"is:private",
"is:dm",
"pm-with:alice@zulip.com",
"pm-with:bob@zulip.com",
"pm-with:jeff@zulip.com",
@ -126,7 +126,7 @@ test("private_suggestions", () => {
assert.deepEqual(suggestions.strings, expected);
query = "al";
let base_query = "is:private";
let base_query = "is:dm";
suggestions = get_suggestions(base_query, query);
expected = [
"al",
@ -137,6 +137,19 @@ test("private_suggestions", () => {
];
assert.deepEqual(suggestions.strings, expected);
// "is:private" was renamed to "is:dm", so
// we suggest "is:dm" to anyone with "is:private"
// in their muscle memory.
query = "is:pr";
suggestions = get_suggestions("", query);
expected = ["is:dm"];
assert.deepEqual(suggestions.strings, expected);
query = "is:private";
suggestions = get_suggestions("", query);
expected = ["is:dm"];
assert.deepEqual(suggestions.strings, expected);
query = "pm-with:t";
suggestions = get_suggestions("", query);
expected = ["pm-with:t", "pm-with:ted@zulip.com"];
@ -144,7 +157,7 @@ test("private_suggestions", () => {
query = "-pm-with:t";
suggestions = get_suggestions("", query);
expected = ["-pm-with:t", "is:private -pm-with:ted@zulip.com"];
expected = ["-pm-with:t", "is:dm -pm-with:ted@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "pm-with:ted@zulip.com";
@ -181,7 +194,7 @@ test("private_suggestions", () => {
// Users can enter bizarre queries, and if they do, we want to
// be conservative with suggestions.
query = "near:3";
base_query = "is:private";
base_query = "is:dm";
suggestions = get_suggestions(base_query, query);
expected = ["near:3"];
assert.deepEqual(suggestions.strings, expected);
@ -200,7 +213,7 @@ test("private_suggestions", () => {
assert.deepEqual(suggestions.strings, expected);
query = "al";
base_query = "is:starred has:link is:private";
base_query = "is:starred has:link is:dm";
suggestions = get_suggestions(base_query, query);
expected = [
"al",
@ -263,27 +276,27 @@ test("group_suggestions", () => {
expected = ["pm-with:bob@zulip.com,red"];
assert.deepEqual(suggestions.strings, expected);
// is:private should be properly prepended to each suggestion if the pm-with
// is:dm should be properly prepended to each suggestion if the pm-with
// operator is negated.
query = "-pm-with:bob@zulip.com,";
suggestions = get_suggestions("", query);
expected = [
"-pm-with:bob@zulip.com,",
"is:private -pm-with:bob@zulip.com,alice@zulip.com",
"is:private -pm-with:bob@zulip.com,jeff@zulip.com",
"is:private -pm-with:bob@zulip.com,ted@zulip.com",
"is:dm -pm-with:bob@zulip.com,alice@zulip.com",
"is:dm -pm-with:bob@zulip.com,jeff@zulip.com",
"is:dm -pm-with:bob@zulip.com,ted@zulip.com",
];
assert.deepEqual(suggestions.strings, expected);
query = "-pm-with:bob@zulip.com,t";
suggestions = get_suggestions("", query);
expected = ["-pm-with:bob@zulip.com,t", "is:private -pm-with:bob@zulip.com,ted@zulip.com"];
expected = ["-pm-with:bob@zulip.com,t", "is:dm -pm-with:bob@zulip.com,ted@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "-pm-with:bob@zulip.com,Smit";
suggestions = get_suggestions("", query);
expected = ["-pm-with:bob@zulip.com,Smit", "is:private -pm-with:bob@zulip.com,ted@zulip.com"];
expected = ["-pm-with:bob@zulip.com,Smit", "is:dm -pm-with:bob@zulip.com,ted@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "-pm-with:bob@zulip.com,red";
@ -369,7 +382,7 @@ test("empty_query_suggestions", () => {
const expected = [
"",
"streams:public",
"is:private",
"is:dm",
"is:starred",
"is:mentioned",
"is:alerted",
@ -388,7 +401,7 @@ test("empty_query_suggestions", () => {
function describe(q) {
return suggestions.lookup_table.get(q).description_html;
}
assert.equal(describe("is:private"), "Direct messages");
assert.equal(describe("is:dm"), "Direct messages");
assert.equal(describe("is:starred"), "Starred messages");
assert.equal(describe("is:mentioned"), "@-mentions");
assert.equal(describe("is:alerted"), "Alerted messages");
@ -466,7 +479,7 @@ test("check_is_suggestions", ({override}) => {
let suggestions = get_suggestions("", query);
let expected = [
"i",
"is:private",
"is:dm",
"is:starred",
"is:mentioned",
"is:alerted",
@ -483,7 +496,7 @@ test("check_is_suggestions", ({override}) => {
return suggestions.lookup_table.get(q).description_html;
}
assert.equal(describe("is:private"), "Direct messages");
assert.equal(describe("is:dm"), "Direct messages");
assert.equal(describe("is:starred"), "Starred messages");
assert.equal(describe("is:mentioned"), "@-mentions");
assert.equal(describe("is:alerted"), "Alerted messages");
@ -494,7 +507,7 @@ test("check_is_suggestions", ({override}) => {
suggestions = get_suggestions("", query);
expected = [
"-i",
"-is:private",
"-is:dm",
"-is:starred",
"-is:mentioned",
"-is:alerted",
@ -503,7 +516,7 @@ test("check_is_suggestions", ({override}) => {
];
assert.deepEqual(suggestions.strings, expected);
assert.equal(describe("-is:private"), "Exclude direct messages");
assert.equal(describe("-is:dm"), "Exclude direct messages");
assert.equal(describe("-is:starred"), "Exclude starred messages");
assert.equal(describe("-is:mentioned"), "Exclude @-mentions");
assert.equal(describe("-is:alerted"), "Exclude alerted messages");
@ -515,7 +528,7 @@ test("check_is_suggestions", ({override}) => {
expected = [
"",
"streams:public",
"is:private",
"is:dm",
"is:starred",
"is:mentioned",
"is:alerted",
@ -531,7 +544,7 @@ test("check_is_suggestions", ({override}) => {
assert.deepEqual(suggestions.strings, expected);
query = "";
let base_query = "is:private";
let base_query = "is:dm";
suggestions = get_suggestions(base_query, query);
expected = [
"is:starred",
@ -549,14 +562,7 @@ test("check_is_suggestions", ({override}) => {
query = "is:";
suggestions = get_suggestions("", query);
expected = [
"is:private",
"is:starred",
"is:mentioned",
"is:alerted",
"is:unread",
"is:resolved",
];
expected = ["is:dm", "is:starred", "is:mentioned", "is:alerted", "is:unread", "is:resolved"];
assert.deepEqual(suggestions.strings, expected);
query = "is:st";
@ -729,7 +735,7 @@ test("topic_suggestions", ({override}) => {
expected = ["topic:", "topic:REXX"];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("is:private stream:devel", "topic:");
suggestions = get_suggestions("is:dm stream:devel", "topic:");
expected = ["topic:"];
assert.deepEqual(suggestions.strings, expected);
@ -980,15 +986,15 @@ test("queries_with_spaces", () => {
// When input search query contains multiple operators
// and a pill hasn't been formed from those operators.
test("multiple_operators_without_pills", () => {
let query = "is:private al";
let query = "is:dm al";
let base_query = "";
let suggestions = get_suggestions(base_query, query);
let expected = [
"is:private al",
"is:private is:alerted",
"is:private sender:alice@zulip.com",
"is:private pm-with:alice@zulip.com",
"is:private group-pm-with:alice@zulip.com",
"is:dm al",
"is:dm is:alerted",
"is:dm sender:alice@zulip.com",
"is:dm pm-with:alice@zulip.com",
"is:dm group-pm-with:alice@zulip.com",
];
assert.deepEqual(suggestions.strings, expected);

View File

@ -114,11 +114,11 @@ test("subset_suggestions", () => {
assert.deepEqual(suggestions.strings, expected);
});
test("private_suggestions", () => {
let query = "is:private";
test("dm_suggestions", () => {
let query = "is:dm";
let suggestions = get_suggestions("", query);
let expected = [
"is:private",
"is:dm",
"pm-with:alice@zulip.com",
"pm-with:bob@zulip.com",
"pm-with:jeff@zulip.com",
@ -127,18 +127,31 @@ test("private_suggestions", () => {
];
assert.deepEqual(suggestions.strings, expected);
query = "is:private al";
query = "is:dm al";
suggestions = get_suggestions("", query);
expected = [
"is:private al",
"is:private is:alerted",
"is:private sender:alice@zulip.com",
"is:private pm-with:alice@zulip.com",
"is:private group-pm-with:alice@zulip.com",
"is:private",
"is:dm al",
"is:dm is:alerted",
"is:dm sender:alice@zulip.com",
"is:dm pm-with:alice@zulip.com",
"is:dm group-pm-with:alice@zulip.com",
"is:dm",
];
assert.deepEqual(suggestions.strings, expected);
// "is:private" was renamed to "is:dm", so
// we suggest "is:dm" to anyone with "is:private"
// in their muscle memory.
query = "is:pr";
suggestions = get_suggestions("", query);
expected = ["is:dm"];
assert.deepEqual(suggestions.strings, expected);
query = "is:private";
suggestions = get_suggestions("", query);
expected = ["is:dm"];
assert.deepEqual(suggestions.strings, expected);
query = "pm-with:t";
suggestions = get_suggestions("", query);
expected = ["pm-with:t", "pm-with:ted@zulip.com"];
@ -146,7 +159,7 @@ test("private_suggestions", () => {
query = "-pm-with:t";
suggestions = get_suggestions("", query);
expected = ["-pm-with:t", "is:private -pm-with:ted@zulip.com"];
expected = ["-pm-with:t", "is:dm -pm-with:ted@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "pm-with:ted@zulip.com";
@ -181,9 +194,9 @@ test("private_suggestions", () => {
// Users can enter bizarre queries, and if they do, we want to
// be conservative with suggestions.
query = "is:private near:3";
query = "is:dm near:3";
suggestions = get_suggestions("", query);
expected = ["is:private near:3", "is:private"];
expected = ["is:dm near:3", "is:dm"];
assert.deepEqual(suggestions.strings, expected);
query = "pm-with:ted@zulip.com near:3";
@ -197,15 +210,15 @@ test("private_suggestions", () => {
expected = ["is:alerted sender:ted@zulip.com", "is:alerted"];
assert.deepEqual(suggestions.strings, expected);
query = "is:starred has:link is:private al";
query = "is:starred has:link is:dm al";
suggestions = get_suggestions("", query);
expected = [
"is:starred has:link is:private al",
"is:starred has:link is:private is:alerted",
"is:starred has:link is:private sender:alice@zulip.com",
"is:starred has:link is:private pm-with:alice@zulip.com",
"is:starred has:link is:private group-pm-with:alice@zulip.com",
"is:starred has:link is:private",
"is:starred has:link is:dm al",
"is:starred has:link is:dm is:alerted",
"is:starred has:link is:dm sender:alice@zulip.com",
"is:starred has:link is:dm pm-with:alice@zulip.com",
"is:starred has:link is:dm group-pm-with:alice@zulip.com",
"is:starred has:link is:dm",
"is:starred has:link",
"is:starred",
];
@ -261,27 +274,27 @@ test("group_suggestions", () => {
expected = ["pm-with:bob@zulip.com,red"];
assert.deepEqual(suggestions.strings, expected);
// is:private should be properly prepended to each suggestion if the pm-with
// is:dm should be properly prepended to each suggestion if the pm-with
// operator is negated.
query = "-pm-with:bob@zulip.com,";
suggestions = get_suggestions("", query);
expected = [
"-pm-with:bob@zulip.com,",
"is:private -pm-with:bob@zulip.com,alice@zulip.com",
"is:private -pm-with:bob@zulip.com,jeff@zulip.com",
"is:private -pm-with:bob@zulip.com,ted@zulip.com",
"is:dm -pm-with:bob@zulip.com,alice@zulip.com",
"is:dm -pm-with:bob@zulip.com,jeff@zulip.com",
"is:dm -pm-with:bob@zulip.com,ted@zulip.com",
];
assert.deepEqual(suggestions.strings, expected);
query = "-pm-with:bob@zulip.com,t";
suggestions = get_suggestions("", query);
expected = ["-pm-with:bob@zulip.com,t", "is:private -pm-with:bob@zulip.com,ted@zulip.com"];
expected = ["-pm-with:bob@zulip.com,t", "is:dm -pm-with:bob@zulip.com,ted@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "-pm-with:bob@zulip.com,Smit";
suggestions = get_suggestions("", query);
expected = ["-pm-with:bob@zulip.com,Smit", "is:private -pm-with:bob@zulip.com,ted@zulip.com"];
expected = ["-pm-with:bob@zulip.com,Smit", "is:dm -pm-with:bob@zulip.com,ted@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "-pm-with:bob@zulip.com,red";
@ -374,7 +387,7 @@ test("empty_query_suggestions", () => {
const expected = [
"",
"streams:public",
"is:private",
"is:dm",
"is:starred",
"is:mentioned",
"is:alerted",
@ -393,7 +406,7 @@ test("empty_query_suggestions", () => {
function describe(q) {
return suggestions.lookup_table.get(q).description_html;
}
assert.equal(describe("is:private"), "Direct messages");
assert.equal(describe("is:dm"), "Direct messages");
assert.equal(describe("is:starred"), "Starred messages");
assert.equal(describe("is:mentioned"), "@-mentions");
assert.equal(describe("is:alerted"), "Alerted messages");
@ -474,7 +487,7 @@ test("check_is_suggestions", ({override}) => {
let suggestions = get_suggestions("", query);
let expected = [
"i",
"is:private",
"is:dm",
"is:starred",
"is:mentioned",
"is:alerted",
@ -491,7 +504,7 @@ test("check_is_suggestions", ({override}) => {
return suggestions.lookup_table.get(q).description_html;
}
assert.equal(describe("is:private"), "Direct messages");
assert.equal(describe("is:dm"), "Direct messages");
assert.equal(describe("is:starred"), "Starred messages");
assert.equal(describe("is:mentioned"), "@-mentions");
assert.equal(describe("is:alerted"), "Alerted messages");
@ -502,7 +515,7 @@ test("check_is_suggestions", ({override}) => {
suggestions = get_suggestions("", query);
expected = [
"-i",
"-is:private",
"-is:dm",
"-is:starred",
"-is:mentioned",
"-is:alerted",
@ -511,7 +524,7 @@ test("check_is_suggestions", ({override}) => {
];
assert.deepEqual(suggestions.strings, expected);
assert.equal(describe("-is:private"), "Exclude direct messages");
assert.equal(describe("-is:dm"), "Exclude direct messages");
assert.equal(describe("-is:starred"), "Exclude starred messages");
assert.equal(describe("-is:mentioned"), "Exclude @-mentions");
assert.equal(describe("-is:alerted"), "Exclude alerted messages");
@ -522,14 +535,7 @@ test("check_is_suggestions", ({override}) => {
query = "is:";
suggestions = get_suggestions("", query);
expected = [
"is:private",
"is:starred",
"is:mentioned",
"is:alerted",
"is:unread",
"is:resolved",
];
expected = ["is:dm", "is:starred", "is:mentioned", "is:alerted", "is:unread", "is:resolved"];
assert.deepEqual(suggestions.strings, expected);
query = "is:st";
@ -702,8 +708,8 @@ test("topic_suggestions", ({override}) => {
];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("", "is:private stream:devel topic:");
expected = ["is:private stream:devel topic:", "is:private stream:devel", "is:private"];
suggestions = get_suggestions("", "is:dm stream:devel topic:");
expected = ["is:dm stream:devel topic:", "is:dm stream:devel", "is:dm"];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("", "topic:REXX stream:devel topic:");