filter: Use "channel" operator for the Filter class.

Update the Filter class to use "channel" as the canoncial operator
for streams, but keep using "stream" for user-facing text and URLs.

When searching, "channel" does not create any suggestions until
a colon is added and then it shows suggestions with "stream". And
when a search string with channel as an operator is entered, then
it is replaced by "stream" as well.

Part of stream to channel rename project.
This commit is contained in:
Lauryn Menard
2024-04-05 19:41:15 +02:00
committed by Tim Abbott
parent e3a521f4ba
commit 1e7c5b38f8
14 changed files with 208 additions and 127 deletions

View File

@@ -164,9 +164,9 @@ export function initialize(): void {
if (narrow_filter === undefined) {
display_current_view = $t({defaultMessage: "Currently viewing all messages."});
} else if (
_.isEqual(narrow_filter.sorted_term_types(), ["stream"]) &&
_.isEqual(narrow_filter.sorted_term_types(), ["channel"]) &&
compose_state.get_message_type() === "stream" &&
narrow_filter.operands("stream")[0] === compose_state.stream_name()
narrow_filter.operands("channel")[0] === compose_state.stream_name()
) {
display_current_view = $t({
defaultMessage: "Currently viewing the entire stream.",

View File

@@ -56,6 +56,11 @@ type Part =
operand: string;
};
// TODO: When "stream" is renamed to "channel", this placeholder
// should be removed, or replaced with helper functions similar
// to util.is_topic_synonym.
const CHANNEL_SYNONYM = "stream";
function zephyr_stream_name_match(message: Message & {type: "stream"}, operand: string): boolean {
// Zephyr users expect narrowing to "social" to also show messages to /^(un)*social(.d)*$/
// (unsocial, ununsocial, social.d, etc)
@@ -172,7 +177,7 @@ function message_matches_search_term(message: Message, operator: string, operand
case "id":
return message.id.toString() === operand;
case "stream": {
case "channel": {
if (message.type !== "stream") {
return false;
}
@@ -244,8 +249,8 @@ export class Filter {
constructor(terms: NarrowTerm[]) {
this._terms = this.fix_terms(terms);
if (this.has_operator("stream")) {
this._sub = stream_data.get_sub_by_name(this.operands("stream")[0]);
if (this.has_operator("channel")) {
this._sub = stream_data.get_sub_by_name(this.operands("channel")[0]);
}
}
@@ -269,6 +274,10 @@ export class Filter {
if (util.is_topic_synonym(operator)) {
return "topic";
}
if (operator === CHANNEL_SYNONYM) {
return "channel";
}
return operator;
}
@@ -289,7 +298,7 @@ export class Filter {
operand = operand.replace(/s$/, "");
break;
case "stream":
case "channel":
operand = stream_data.get_name(operand);
break;
case "topic":
@@ -397,6 +406,9 @@ export class Filter {
negated = true;
operator = operator.slice(1);
}
if (operator === CHANNEL_SYNONYM) {
operator = "channel";
}
operand = Filter.decodeOperand(parts.join(":"), operator);
// We use Filter.operator_to_prefix() to check if the
@@ -442,7 +454,8 @@ export class Filter {
if (term.operator === "") {
return term.operand;
}
return sign + term.operator + ":" + Filter.encodeOperand(term.operand.toString());
const operator = util.canonicalize_stream_synonym(term.operator);
return sign + operator + ":" + Filter.encodeOperand(term.operand.toString());
});
return term_strings.join(" ");
}
@@ -467,7 +480,7 @@ export class Filter {
const levels = [
"in",
"streams-public",
"stream",
"channel",
"topic",
"dm",
"dm-including",
@@ -515,8 +528,8 @@ export class Filter {
const verb = negated ? "exclude " : "";
switch (operator) {
case "stream":
return verb + "stream";
case "channel":
return verb + CHANNEL_SYNONYM;
case "streams":
return verb + "streams";
case "near":
@@ -562,9 +575,9 @@ export class Filter {
if (terms.length >= 2) {
const is = (term: NarrowTerm, expected: string): boolean =>
term.operator === expected && !term.negated;
Filter.canonicalize_operator(term.operator) === expected && !term.negated;
if (is(terms[0], "stream") && is(terms[1], "topic")) {
if (is(terms[0], "channel") && is(terms[1], "topic")) {
const stream = terms[0].operand;
const topic = terms[1].operand;
parts.push({
@@ -659,7 +672,7 @@ export class Filter {
(term) =>
!(
page_params.narrow_stream !== undefined &&
term.operator === "stream" &&
term.operator === "channel" &&
term.operand.toLowerCase() === page_params.narrow_stream.toLowerCase()
),
);
@@ -717,8 +730,8 @@ export class Filter {
// message sharing its recipient (stream/topic or direct
// message recipient) must also be present in the view.
const valid_term_types = new Set([
"stream",
"not-stream",
"channel",
"not-channel",
"topic",
"not-topic",
"dm",
@@ -766,10 +779,10 @@ export class Filter {
const term_types = this.sorted_term_types();
// "topic" alone cannot guarantee all messages of a conversation because
// it is limited by the user's message history. Therefore, we check "stream"
// it is limited by the user's message history. Therefore, we check "channel"
// and "topic" together to ensure that the current filter will return all the
// messages of a conversation.
if (_.isEqual(term_types, ["stream", "topic"])) {
if (_.isEqual(term_types, ["channel", "topic"])) {
return true;
}
@@ -777,7 +790,7 @@ export class Filter {
return true;
}
if (_.isEqual(term_types, ["stream"])) {
if (_.isEqual(term_types, ["channel"])) {
return true;
}
@@ -842,14 +855,16 @@ export class Filter {
const term_types = this.sorted_term_types();
// this comes first because it has 3 term_types but is not a "complex filter"
if (_.isEqual(term_types, ["stream", "topic", "search"])) {
if (_.isEqual(term_types, ["channel", "topic", "search"])) {
// if stream does not exist, redirect to All
if (!this._sub) {
return "#";
}
return (
"/#narrow/stream/" +
stream_data.name_to_slug(this.operands("stream")[0]) +
"/#narrow/" +
CHANNEL_SYNONYM +
"/" +
stream_data.name_to_slug(this.operands("channel")[0]) +
"/topic/" +
this.operands("topic")[0]
);
@@ -862,13 +877,16 @@ export class Filter {
if (term_types[1] === "search") {
switch (term_types[0]) {
case "stream":
case "channel":
// if stream does not exist, redirect to All
if (!this._sub) {
return "#";
}
return (
"/#narrow/stream/" + stream_data.name_to_slug(this.operands("stream")[0])
"/#narrow/" +
CHANNEL_SYNONYM +
"/" +
stream_data.name_to_slug(this.operands("channel")[0])
);
case "is-dm":
return "/#narrow/is/dm";
@@ -902,7 +920,7 @@ export class Filter {
case "in-all":
icon = "home";
break;
case "stream":
case "channel":
if (!this._sub) {
icon = "question-circle-o";
break;
@@ -946,12 +964,12 @@ export class Filter {
// Nice explanatory titles for common views.
const term_types = this.sorted_term_types();
if (
(term_types.length === 3 && _.isEqual(term_types, ["stream", "topic", "near"])) ||
(term_types.length === 2 && _.isEqual(term_types, ["stream", "topic"])) ||
(term_types.length === 1 && _.isEqual(term_types, ["stream"]))
(term_types.length === 3 && _.isEqual(term_types, ["channel", "topic", "near"])) ||
(term_types.length === 2 && _.isEqual(term_types, ["channel", "topic"])) ||
(term_types.length === 1 && _.isEqual(term_types, ["channel"]))
) {
if (!this._sub) {
const search_text = this.operands("stream")[0];
const search_text = this.operands("channel")[0];
return $t({defaultMessage: "Unknown stream #{search_text}"}, {search_text});
}
return this._sub.name;
@@ -1039,7 +1057,7 @@ export class Filter {
}
includes_full_stream_history(): boolean {
return this.has_operator("stream") || this.has_operator("streams");
return this.has_operator("channel") || this.has_operator("streams");
}
is_personal_filter(): boolean {
@@ -1112,7 +1130,7 @@ export class Filter {
}
has_topic(stream_name: string, topic: string): boolean {
return this.has_operand("stream", stream_name) && this.has_operand("topic", topic);
return this.has_operand("channel", stream_name) && this.has_operand("topic", topic);
}
sorted_term_types(): string[] {
@@ -1131,7 +1149,7 @@ export class Filter {
can_bucket_by(...wanted_term_types: string[]): boolean {
// Examples call:
// filter.can_bucket_by('stream', 'topic')
// filter.can_bucket_by('channel', 'topic')
//
// The use case of this function is that we want
// to know if a filter can start with a bucketing
@@ -1204,7 +1222,7 @@ export class Filter {
is_conversation_view(): boolean {
const term_type = this.sorted_term_types();
if (_.isEqual(term_type, ["stream", "topic"]) || _.isEqual(term_type, ["dm"])) {
if (_.isEqual(term_type, ["channel", "topic"]) || _.isEqual(term_type, ["dm"])) {
return true;
}
return false;
@@ -1213,7 +1231,7 @@ export class Filter {
excludes_muted_topics(): boolean {
return (
// not narrowed to a topic
!(this.has_operator("stream") && this.has_operator("topic")) &&
!(this.has_operator("channel") && this.has_operator("topic")) &&
// not narrowed to search
!this.is_keyword_search() &&
// not narrowed to dms

View File

@@ -92,6 +92,7 @@ export function is_create_new_stream_narrow(): boolean {
}
export const allowed_web_public_narrows = [
"channel",
"streams",
"stream",
"topic",

View File

@@ -11,6 +11,7 @@ import * as sub_store from "./sub_store";
import type {StreamSubscription} from "./sub_store";
import * as user_groups from "./user_groups";
import type {UserGroup} from "./user_groups";
import * as util from "./util";
export function build_reload_url(): string {
let hash = window.location.hash;
@@ -93,7 +94,7 @@ export function search_terms_to_hash(terms?: NarrowTerm[]): string {
for (const term of terms) {
// Support legacy tuples.
const operator = term.operator;
const operator = util.canonicalize_stream_synonym(term.operator);
const operand = term.operand;
const sign = term.negated ? "-" : "";

View File

@@ -472,7 +472,7 @@ export class MessageListView {
// we can infer that whenever the historical flag flips
// between adjacent messages, the current user must have
// (un)subscribed in between those messages.
if (!this.list.data.filter.has_operator("stream")) {
if (!this.list.data.filter.has_operator("channel")) {
return;
}
if (last_msg_container === undefined) {

View File

@@ -59,7 +59,7 @@ function get_message_view_header_context(filter: Filter | undefined): MessageVie
title,
is_spectator: page_params.is_spectator,
});
if (filter.has_operator("stream") && !filter._sub) {
if (filter.has_operator("channel") && !filter._sub) {
return {
...icon_data,
sub_count: "0",

View File

@@ -220,7 +220,7 @@ export function activate(raw_terms, opts) {
// message in case the message was moved after the link was
// created. This ensures near / id links work and will redirect
// correctly if the topic was moved (including being resolved).
if (id_info.target_id && filter.has_operator("stream") && filter.has_operator("topic")) {
if (id_info.target_id && filter.has_operator("channel") && filter.has_operator("topic")) {
const target_message = message_store.get(id_info.target_id);
function adjusted_terms_if_moved(terms, message) {
@@ -230,7 +230,7 @@ export function activate(raw_terms, opts) {
for (const term of terms) {
const adjusted_term = {...term};
if (
term.operator === "stream" &&
term.operator === "channel" &&
!util.lower_same(term.operand, message.display_recipient)
) {
adjusted_term.operand = message.display_recipient;
@@ -262,7 +262,7 @@ export function activate(raw_terms, opts) {
// location, then we should retarget this narrow operation
// to where the message is located now.
const narrow_topic = filter.operands("topic")[0];
const narrow_stream_name = filter.operands("stream")[0];
const narrow_stream_name = filter.operands("channel")[0];
const narrow_stream_data = stream_data.get_sub(narrow_stream_name);
if (!narrow_stream_data) {
// The id of the target message is correct but the stream name is

View File

@@ -40,8 +40,8 @@ function retrieve_search_query_data(): SearchData {
};
// Add in stream:foo and topic:bar if present
if (current_filter.has_operator("stream") || current_filter.has_operator("topic")) {
const stream = current_filter.operands("stream")[0];
if (current_filter.has_operator("channel") || current_filter.has_operator("topic")) {
const stream = current_filter.operands("channel")[0];
const topic = current_filter.operands("topic")[0];
if (stream) {
search_string_result.stream_query = stream;
@@ -103,7 +103,7 @@ function pick_empty_narrow_banner(): NarrowBannerData {
if (num_terms !== 1) {
// For invalid-multi-operator narrows, we display an invalid narrow message
const streams = current_filter.operands("stream");
const streams = current_filter.operands("channel");
const topics = current_filter.operands("topic");
// No message can have multiple streams
@@ -147,7 +147,7 @@ function pick_empty_narrow_banner(): NarrowBannerData {
if (
page_params.is_spectator &&
first_operator === "stream" &&
first_operator === "channel" &&
!stream_data.is_web_public_by_stream_name(first_operand)
) {
// For non web-public streams, show `login_to_access` modal.
@@ -242,7 +242,7 @@ function pick_empty_narrow_banner(): NarrowBannerData {
}
// fallthrough to default case if no match is found
break;
case "stream":
case "channel":
if (!stream_data.is_subscribed_by_name(first_operand)) {
// You are narrowed to a stream which does not exist or is a private stream
// in which you were never subscribed.

View File

@@ -97,7 +97,7 @@ export function set_compose_defaults(): {
// Set the stream, topic, and/or direct message recipient
// if they are uniquely specified in the narrow view.
if (single.has("stream")) {
if (single.has("channel")) {
// The raw stream name from collect_single may be an arbitrary
// unvalidated string from the URL fragment and thus not be valid.
// So we look up the resolved stream and return that if appropriate.
@@ -125,7 +125,7 @@ export function stream_name(current_filter: Filter | undefined = filter()): stri
if (current_filter === undefined) {
return undefined;
}
const stream_operands = current_filter.operands("stream");
const stream_operands = current_filter.operands("channel");
if (stream_operands.length === 1) {
const name = stream_operands[0];
@@ -142,7 +142,7 @@ export function stream_sub(
if (current_filter === undefined) {
return undefined;
}
const stream_operands = current_filter.operands("stream");
const stream_operands = current_filter.operands("channel");
if (stream_operands.length !== 1) {
return undefined;
}
@@ -269,7 +269,7 @@ export function _possible_unread_message_ids(
let topic_name;
let current_filter_pm_string;
if (current_filter.can_bucket_by("stream", "topic")) {
if (current_filter.can_bucket_by("channel", "topic")) {
sub = stream_sub(current_filter);
topic_name = topic(current_filter);
if (sub === undefined || topic_name === undefined) {
@@ -278,7 +278,7 @@ export function _possible_unread_message_ids(
return unread.get_msg_ids_for_topic(sub.stream_id, topic_name);
}
if (current_filter.can_bucket_by("stream")) {
if (current_filter.can_bucket_by("channel")) {
sub = stream_sub(current_filter);
if (sub === undefined) {
return [];
@@ -342,7 +342,7 @@ export function narrowed_by_topic_reply(current_filter: Filter | undefined = fil
const terms = current_filter.terms();
return (
terms.length === 2 &&
current_filter.operands("stream").length === 1 &&
current_filter.operands("channel").length === 1 &&
current_filter.operands("topic").length === 1
);
}
@@ -359,14 +359,14 @@ export function narrowed_by_stream_reply(current_filter: Filter | undefined = fi
return false;
}
const terms = current_filter.terms();
return terms.length === 1 && current_filter.operands("stream").length === 1;
return terms.length === 1 && current_filter.operands("channel").length === 1;
}
export function narrowed_to_topic(current_filter: Filter | undefined = filter()): boolean {
if (current_filter === undefined) {
return false;
}
return current_filter.has_operator("stream") && current_filter.has_operator("topic");
return current_filter.has_operator("channel") && current_filter.has_operator("topic");
}
export function is_for_stream_id(stream_id: number, filter?: Filter): boolean {

View File

@@ -33,7 +33,7 @@ export function compute_narrow_title(filter?: Filter): string {
return $t({defaultMessage: "Search results"});
}
if (filter.has_operator("stream")) {
if (filter.has_operator("channel")) {
if (!filter._sub) {
// The stream is not set because it does not currently
// exist (possibly due to a stream name change), or it

View File

@@ -120,9 +120,9 @@ function compare_by_huddle(huddle_emails: string[]): (person1: User, person2: Us
}
function get_stream_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestion[] {
const valid = ["stream", "search", ""];
const valid = ["channel", "search", ""];
const incompatible_patterns = [
{operator: "stream"},
{operator: "channel"},
{operator: "streams"},
{operator: "is", operand: "dm"},
{operator: "dm"},
@@ -148,7 +148,7 @@ function get_stream_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggesti
const verb = last.negated ? "exclude " : "";
const description_html = verb + prefix + " " + highlighted_stream;
const term = {
operator: "stream",
operator: "channel",
operand: stream,
negated: last.negated,
};
@@ -162,7 +162,7 @@ function get_stream_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggesti
function get_group_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestion[] {
// For users with "pm-with" in their muscle memory, still
// have group direct message suggestions with "dm:" operator.
if (!check_validity(last, terms, ["dm", "pm-with"], [{operator: "stream"}])) {
if (!check_validity(last, terms, ["dm", "pm-with"], [{operator: "channel"}])) {
return [];
}
@@ -286,14 +286,14 @@ function get_person_suggestions(
switch (autocomplete_operator) {
case "dm-including":
incompatible_patterns = [{operator: "stream"}, {operator: "is", operand: "resolved"}];
incompatible_patterns = [{operator: "channel"}, {operator: "is", operand: "resolved"}];
break;
case "dm":
case "pm-with":
incompatible_patterns = [
{operator: "dm"},
{operator: "pm-with"},
{operator: "stream"},
{operator: "channel"},
{operator: "is", operand: "resolved"},
];
break;
@@ -397,7 +397,7 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio
{operator: "dm-including"},
{operator: "topic"},
];
if (!check_validity(last, terms, ["stream", "topic", "search"], incompatible_patterns)) {
if (!check_validity(last, terms, ["channel", "topic", "search"], incompatible_patterns)) {
return [];
}
@@ -425,7 +425,7 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio
// in terms of telling us whether they provided the operator,
// i.e. "foo" and "search:foo" both become [{operator: 'search', operand: 'foo'}].
switch (operator) {
case "stream":
case "channel":
guess = "";
stream = operand;
suggest_terms.push(last);
@@ -433,12 +433,12 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio
case "topic":
case "search":
guess = operand;
if (filter.has_operator("stream")) {
stream = filter.operands("stream")[0];
if (filter.has_operator("channel")) {
stream = filter.operands("channel")[0];
} else {
stream = narrow_state.stream_name();
if (stream) {
suggest_terms.push({operator: "stream", operand: stream});
suggest_terms.push({operator: "channel", operand: stream});
}
}
break;
@@ -553,7 +553,7 @@ function get_streams_filter_suggestions(last: NarrowTerm, terms: NarrowTerm[]):
description_html: "All public streams in organization",
incompatible_patterns: [
{operator: "is", operand: "dm"},
{operator: "stream"},
{operator: "channel"},
{operator: "dm-including"},
{operator: "dm"},
{operator: "in"},
@@ -571,7 +571,7 @@ function get_is_filter_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Sugge
incompatible_patterns: [
{operator: "is", operand: "dm"},
{operator: "is", operand: "resolved"},
{operator: "stream"},
{operator: "channel"},
{operator: "dm"},
{operator: "in"},
],

View File

@@ -674,7 +674,7 @@ export function get_sidebar_stream_topic_info(filter: Filter): {
topic_selected: false,
};
const op_stream = filter.operands("stream");
const op_stream = filter.operands("channel");
if (op_stream.length === 0) {
return result;
}

View File

@@ -83,21 +83,50 @@ function test(label, f) {
test("basics", () => {
let terms = [
{operator: "stream", operand: "foo"},
{operator: "stream", operand: "exclude_stream", negated: true},
{operator: "channel", operand: "foo"},
{operator: "channel", operand: "exclude_stream", negated: true},
{operator: "topic", operand: "bar"},
];
let filter = new Filter(terms);
assert_same_terms(filter.terms(), terms);
assert.deepEqual(filter.operands("stream"), ["foo"]);
assert.deepEqual(filter.operands("channel"), ["foo"]);
assert.ok(filter.has_operator("stream"));
assert.ok(filter.has_operator("channel"));
assert.ok(!filter.has_operator("search"));
assert.ok(filter.has_operand("stream", "foo"));
assert.ok(!filter.has_operand("stream", "exclude_stream"));
assert.ok(!filter.has_operand("stream", "nada"));
assert.ok(filter.has_operand("channel", "foo"));
assert.ok(!filter.has_operand("channel", "exclude_stream"));
assert.ok(!filter.has_operand("channel", "nada"));
assert.ok(!filter.is_keyword_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());
assert.ok(filter.can_bucket_by("channel"));
assert.ok(filter.can_bucket_by("channel", "topic"));
terms = [
{operator: "stream", operand: "foo"},
{operator: "stream", operand: "exclude_stream", negated: true},
{operator: "topic", operand: "bar"},
];
filter = new Filter(terms);
assert.deepEqual(filter.operands("channel"), ["foo"]);
assert.ok(filter.has_operator("channel"));
assert.ok(!filter.has_operator("search"));
assert.ok(filter.has_operand("channel", "foo"));
assert.ok(!filter.has_operand("channel", "exclude_stream"));
assert.ok(!filter.has_operand("channel", "nada"));
assert.ok(!filter.is_keyword_search());
assert.ok(!filter.can_mark_messages_read());
@@ -123,8 +152,8 @@ test("basics", () => {
assert.ok(!filter.allow_use_first_unread_when_narrowing());
assert.ok(!filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(filter.can_bucket_by("stream"));
assert.ok(filter.can_bucket_by("stream", "topic"));
assert.ok(filter.can_bucket_by("channel"));
assert.ok(filter.can_bucket_by("channel", "topic"));
assert.ok(!filter.is_conversation_view());
terms = [
@@ -141,8 +170,8 @@ test("basics", () => {
assert.ok(!filter.allow_use_first_unread_when_narrowing());
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(filter.can_bucket_by("stream"));
assert.ok(filter.can_bucket_by("stream", "topic"));
assert.ok(filter.can_bucket_by("channel"));
assert.ok(filter.can_bucket_by("channel", "topic"));
assert.ok(!filter.is_conversation_view());
// If our only stream operator is negated, then for all intents and purposes,
@@ -151,7 +180,7 @@ test("basics", () => {
terms = [{operator: "stream", operand: "exclude", negated: true}];
filter = new Filter(terms);
assert.ok(!filter.contains_only_private_messages());
assert.ok(!filter.has_operator("stream"));
assert.ok(!filter.has_operator("channel"));
assert.ok(!filter.can_mark_messages_read());
assert.ok(filter.supports_collapsing_recipients());
assert.ok(!filter.is_personal_filter());
@@ -332,6 +361,22 @@ test("basics", () => {
assert.ok(filter.can_apply_locally());
assert.ok(!filter.is_personal_filter());
assert.ok(filter.is_conversation_view());
terms = [
{operator: "channel", operand: "foo"},
{operator: "topic", operand: "bar"},
];
filter = new Filter(terms);
assert.ok(!filter.is_keyword_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_terms_to_test) {
@@ -557,7 +602,7 @@ test("filter_with_new_params_topic", () => {
operand: "new topic",
});
assert.deepEqual(new_filter.operands("stream"), ["foo"]);
assert.deepEqual(new_filter.operands("channel"), ["foo"]);
assert.deepEqual(new_filter.operands("topic"), ["new topic"]);
});
@@ -577,7 +622,7 @@ test("filter_with_new_params_stream", () => {
operand: "new stream",
});
assert.deepEqual(new_filter.operands("stream"), ["new stream"]);
assert.deepEqual(new_filter.operands("channel"), ["new stream"]);
assert.deepEqual(new_filter.operands("topic"), ["old topic"]);
});
@@ -589,8 +634,8 @@ test("new_style_terms", () => {
const terms = [term];
const filter = new Filter(terms);
assert.deepEqual(filter.operands("stream"), ["foo"]);
assert.ok(filter.can_bucket_by("stream"));
assert.deepEqual(filter.operands("channel"), ["foo"]);
assert.ok(filter.can_bucket_by("channel"));
});
test("public_terms", ({override}) => {
@@ -600,13 +645,17 @@ test("public_terms", ({override}) => {
{operator: "in", operand: "all"},
{operator: "topic", operand: "bar"},
];
let filter = new Filter(terms);
const expected_terms = [
{operator: "channel", operand: "some_stream"},
{operator: "in", operand: "all"},
{operator: "topic", operand: "bar"},
];
override(page_params, "narrow_stream", undefined);
assert_same_terms(filter.public_terms(), terms);
assert.ok(filter.can_bucket_by("stream"));
assert_same_terms(filter.public_terms(), expected_terms);
assert.ok(filter.can_bucket_by("channel"));
terms = [{operator: "stream", operand: "default"}];
terms = [{operator: "channel", operand: "default"}];
filter = new Filter(terms);
override(page_params, "narrow_stream", "default");
assert_same_terms(filter.public_terms(), []);
@@ -633,13 +682,17 @@ test("redundancies", () => {
test("canonicalization", () => {
assert.equal(Filter.canonicalize_operator("Is"), "is");
assert.equal(Filter.canonicalize_operator("Stream"), "stream");
assert.equal(Filter.canonicalize_operator("Stream"), "channel");
assert.equal(Filter.canonicalize_operator("Subject"), "topic");
assert.equal(Filter.canonicalize_operator("FROM"), "sender");
let term;
term = Filter.canonicalize_term({operator: "Stream", operand: "Denmark"});
assert.equal(term.operator, "stream");
assert.equal(term.operator, "channel");
assert.equal(term.operand, "Denmark");
term = Filter.canonicalize_term({operator: "Channel", operand: "Denmark"});
assert.equal(term.operator, "channel");
assert.equal(term.operand, "Denmark");
term = Filter.canonicalize_term({operator: "sender", operand: "me"});
@@ -1027,9 +1080,17 @@ test("parse", () => {
assert_same_terms(result, terms);
}
string = "channel:Foo topic:Bar yo";
terms = [
{operator: "channel", operand: "Foo"},
{operator: "topic", operand: "Bar"},
{operator: "search", operand: "yo"},
];
_test();
string = "stream:Foo topic:Bar yo";
terms = [
{operator: "stream", operand: "Foo"},
{operator: "channel", operand: "Foo"},
{operator: "topic", operand: "Bar"},
{operator: "search", operand: "yo"},
];
@@ -1044,23 +1105,23 @@ test("parse", () => {
_test();
string = "stream:With+Space";
terms = [{operator: "stream", operand: "With Space"}];
terms = [{operator: "channel", operand: "With Space"}];
_test();
string = 'stream:"with quoted space" topic:and separate';
terms = [
{operator: "stream", operand: "with quoted space"},
{operator: "channel", operand: "with quoted space"},
{operator: "topic", operand: "and"},
{operator: "search", operand: "separate"},
];
_test();
string = 'stream:"unclosed quote';
terms = [{operator: "stream", operand: "unclosed quote"}];
terms = [{operator: "channel", operand: "unclosed quote"}];
_test();
string = 'stream:""';
terms = [{operator: "stream", operand: ""}];
terms = [{operator: "channel", operand: ""}];
_test();
string = "https://www.google.com";
@@ -1069,15 +1130,15 @@ test("parse", () => {
string = "stream:foo -stream:exclude";
terms = [
{operator: "stream", operand: "foo"},
{operator: "stream", operand: "exclude", negated: true},
{operator: "channel", operand: "foo"},
{operator: "channel", operand: "exclude", negated: true},
];
_test();
string = "text stream:foo more text";
terms = [
{operator: "search", operand: "text"},
{operator: "stream", operand: "foo"},
{operator: "channel", operand: "foo"},
{operator: "search", operand: "more text"},
];
_test();
@@ -1100,7 +1161,7 @@ test("parse", () => {
string = "stream:foo :emoji: are cool";
terms = [
{operator: "stream", operand: "foo"},
{operator: "channel", operand: "foo"},
{operator: "search", operand: ":emoji: are cool"},
];
_test();
@@ -1108,7 +1169,7 @@ test("parse", () => {
string = ":stream: stream:foo :emoji: are cool";
terms = [
{operator: "search", operand: ":stream:"},
{operator: "stream", operand: "foo"},
{operator: "channel", operand: "foo"},
{operator: "search", operand: ":emoji: are cool"},
];
_test();
@@ -1116,7 +1177,7 @@ test("parse", () => {
string = ":stream: stream:foo -:emoji: are cool";
terms = [
{operator: "search", operand: ":stream:"},
{operator: "stream", operand: "foo"},
{operator: "channel", operand: "foo"},
{operator: "search", operand: "-:emoji: are cool"},
];
_test();
@@ -1127,7 +1188,7 @@ test("parse", () => {
string = 'stream: separated topic: "with space"';
terms = [
{operator: "stream", operand: "separated"},
{operator: "channel", operand: "separated"},
{operator: "topic", operand: "with space"},
];
_test();
@@ -1138,7 +1199,7 @@ test("unparse", () => {
let terms;
terms = [
{operator: "stream", operand: "Foo"},
{operator: "channel", operand: "Foo"},
{operator: "topic", operand: "Bar", negated: true},
{operator: "search", operand: "yo"},
];
@@ -1294,8 +1355,8 @@ test("describe", ({mock_template}) => {
test("can_bucket_by", () => {
let terms = [{operator: "stream", operand: "My stream"}];
let filter = new Filter(terms);
assert.equal(filter.can_bucket_by("stream"), true);
assert.equal(filter.can_bucket_by("stream", "topic"), false);
assert.equal(filter.can_bucket_by("channel"), true);
assert.equal(filter.can_bucket_by("channel", "topic"), false);
assert.equal(filter.can_bucket_by("dm"), false);
terms = [
@@ -1304,8 +1365,8 @@ test("can_bucket_by", () => {
{operator: "stream", operand: "My stream"},
];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("stream"), true);
assert.equal(filter.can_bucket_by("stream", "topic"), true);
assert.equal(filter.can_bucket_by("channel"), true);
assert.equal(filter.can_bucket_by("channel", "topic"), true);
assert.equal(filter.can_bucket_by("dm"), false);
terms = [
@@ -1313,20 +1374,20 @@ test("can_bucket_by", () => {
{operator: "topic", operand: "My topic"},
];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("stream"), false);
assert.equal(filter.can_bucket_by("stream", "topic"), false);
assert.equal(filter.can_bucket_by("channel"), false);
assert.equal(filter.can_bucket_by("channel", "topic"), false);
assert.equal(filter.can_bucket_by("dm"), false);
terms = [{operator: "dm", operand: "foo@example.com", negated: true}];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("stream"), false);
assert.equal(filter.can_bucket_by("stream", "topic"), false);
assert.equal(filter.can_bucket_by("channel"), false);
assert.equal(filter.can_bucket_by("channel", "topic"), false);
assert.equal(filter.can_bucket_by("dm"), false);
terms = [{operator: "dm", operand: "foo@example.com,bar@example.com"}];
filter = new Filter(terms);
assert.equal(filter.can_bucket_by("stream"), false);
assert.equal(filter.can_bucket_by("stream", "topic"), false);
assert.equal(filter.can_bucket_by("channel"), false);
assert.equal(filter.can_bucket_by("channel", "topic"), false);
assert.equal(filter.can_bucket_by("dm"), true);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-dm"), false);
@@ -1361,8 +1422,8 @@ test("can_bucket_by", () => {
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("channel", "topic"), false);
assert.equal(filter.can_bucket_by("channel"), false);
assert.equal(filter.can_bucket_by("dm"), false);
assert.equal(filter.can_bucket_by("is-mentioned"), false);
assert.equal(filter.can_bucket_by("is-dm"), false);
@@ -1382,7 +1443,7 @@ test("term_type", () => {
}
assert_term_type(term("streams", "public"), "streams-public");
assert_term_type(term("stream", "whatever"), "stream");
assert_term_type(term("channel", "whatever"), "channel");
assert_term_type(term("dm", "whomever"), "dm");
assert_term_type(term("dm", "whomever", true), "not-dm");
assert_term_type(term("is", "dm"), "is-dm");
@@ -1395,27 +1456,27 @@ test("term_type", () => {
assert.deepEqual(sorted_terms, expected);
}
assert_term_sort(["topic", "stream", "sender"], ["stream", "topic", "sender"]);
assert_term_sort(["topic", "channel", "sender"], ["channel", "topic", "sender"]);
assert_term_sort(
["has-link", "near", "is-unread", "dm"],
["dm", "near", "is-unread", "has-link"],
);
assert_term_sort(["bogus", "stream", "topic"], ["stream", "topic", "bogus"]);
assert_term_sort(["stream", "topic", "stream"], ["stream", "stream", "topic"]);
assert_term_sort(["bogus", "channel", "topic"], ["channel", "topic", "bogus"]);
assert_term_sort(["channel", "topic", "channel"], ["channel", "channel", "topic"]);
assert_term_sort(["search", "streams-public"], ["streams-public", "search"]);
const terms = [
{operator: "topic", operand: "lunch"},
{operator: "sender", operand: "steve@foo.com"},
{operator: "stream", operand: "Verona"},
{operator: "channel", operand: "Verona"},
];
let filter = new Filter(terms);
const term_types = filter.sorted_term_types();
assert.deepEqual(term_types, ["stream", "topic", "sender"]);
assert.deepEqual(term_types, ["channel", "topic", "sender"]);
// test caching of term types
// init and stub
@@ -1429,13 +1490,13 @@ test("term_type", () => {
// uncached trial
filter._build_sorted_term_types_called = false;
const built_terms = filter.sorted_term_types();
assert.deepEqual(built_terms, ["stream", "topic", "sender"]);
assert.deepEqual(built_terms, ["channel", "topic", "sender"]);
assert.ok(filter._build_sorted_term_types_called);
// cached trial
filter._build_sorted_term_types_called = false;
const cached_terms = filter.sorted_term_types();
assert.deepEqual(cached_terms, ["stream", "topic", "sender"]);
assert.deepEqual(cached_terms, ["channel", "topic", "sender"]);
assert.ok(!filter._build_sorted_term_types_called);
});
@@ -1471,7 +1532,7 @@ test("update_email", () => {
filter.update_email(steve.user_id, "showell@foo.com");
assert.deepEqual(filter.operands("dm"), ["showell@foo.com"]);
assert.deepEqual(filter.operands("sender"), ["showell@foo.com"]);
assert.deepEqual(filter.operands("stream"), ["steve@foo.com"]);
assert.deepEqual(filter.operands("channel"), ["steve@foo.com"]);
});
function make_private_sub(name, stream_id) {

View File

@@ -59,7 +59,7 @@ test("stream", () => {
assert.ok(narrow_state.is_for_stream_id(test_stream.stream_id));
const expected_terms = [
{negated: false, operator: "stream", operand: "Test"},
{negated: false, operator: "channel", operand: "Test"},
{negated: false, operator: "topic", operand: "Bar"},
{negated: false, operator: "search", operand: "yo"},
];
@@ -130,7 +130,7 @@ test("terms", () => {
]);
let result = narrow_state.search_terms();
assert.equal(result.length, 3);
assert.equal(result[0].operator, "stream");
assert.equal(result[0].operator, "channel");
assert.equal(result[0].operand, "Foo");
assert.equal(result[1].operator, "topic");
@@ -146,7 +146,7 @@ test("terms", () => {
page_params.narrow = [{operator: "stream", operand: "Foo"}];
result = narrow_state.search_terms();
assert.equal(result.length, 1);
assert.equal(result[0].operator, "stream");
assert.equal(result[0].operator, "channel");
assert.equal(result[0].operand, "Foo");
});
@@ -243,7 +243,7 @@ test("update_email", () => {
const filter = narrow_state.filter();
assert.deepEqual(filter.operands("dm"), ["showell@foo.com"]);
assert.deepEqual(filter.operands("sender"), ["showell@foo.com"]);
assert.deepEqual(filter.operands("stream"), ["steve@foo.com"]);
assert.deepEqual(filter.operands("channel"), ["steve@foo.com"]);
});
test("topic", () => {