filter: Use stream id instead of stream name.

This commit is contained in:
evykassirer 2024-08-02 18:05:34 -07:00 committed by Tim Abbott
parent 2be181c367
commit dba1af84e0
38 changed files with 760 additions and 592 deletions

View File

@ -186,16 +186,16 @@ export function initialize() {
// Renarrow to the unresolved topic if currently viewing the resolved topic.
const current_filter = narrow_state.filter();
const channel_name = sub_store.maybe_get_stream_name(stream_id);
const stream_id_string = stream_id.toString();
if (
current_filter &&
(current_filter.is_conversation_view() ||
current_filter.is_conversation_view_with_near()) &&
current_filter.has_topic(channel_name, topic_name)
current_filter.has_topic(stream_id_string, topic_name)
) {
message_view.show(
[
{operator: "channel", operand: channel_name},
{operator: "channel", operand: stream_id_string},
{operator: "topic", operand: new_topic},
],
{},

View File

@ -9,6 +9,7 @@ import * as compose_ui from "./compose_ui";
import * as hash_util from "./hash_util";
import * as message_lists from "./message_lists";
import * as rows from "./rows";
import * as stream_data from "./stream_data";
import * as topic_link_util from "./topic_link_util";
import * as util from "./util";
@ -662,7 +663,10 @@ export function try_stream_topic_syntax_text(text: string): string | null {
return null;
}
if (topic_link_util.will_produce_broken_stream_topic_link(stream_topic.stream_name)) {
const stream = stream_data.get_sub_by_id(stream_topic.stream_id);
assert(stream !== undefined);
const stream_name = stream.name;
if (topic_link_util.will_produce_broken_stream_topic_link(stream_name)) {
return null;
}
@ -673,7 +677,7 @@ export function try_stream_topic_syntax_text(text: string): string | null {
return null;
}
let syntax_text = "#**" + stream_topic.stream_name;
let syntax_text = "#**" + stream_name;
if (stream_topic.topic_name) {
syntax_text += ">" + stream_topic.topic_name;
}

View File

@ -12,7 +12,6 @@ import * as messages_overlay_ui from "./messages_overlay_ui";
import * as overlays from "./overlays";
import * as people from "./people";
import * as rendered_markdown from "./rendered_markdown";
import * as stream_data from "./stream_data";
import * as user_card_popover from "./user_card_popover";
import * as user_group_popover from "./user_group_popover";
@ -30,7 +29,7 @@ function restore_draft(draft_id) {
[
{
operator: "channel",
operand: stream_data.get_stream_name_from_id(compose_args.stream_id),
operand: compose_args.stream_id.toString(),
},
{operator: "topic", operand: compose_args.topic},
],

View File

@ -18,7 +18,6 @@ import type {UserPillItem} from "./search_suggestion";
import {current_user, realm} from "./state_data";
import type {NarrowTerm} from "./state_data";
import * as stream_data from "./stream_data";
import type {StreamSubscription} from "./sub_store";
import * as unread from "./unread";
import * as user_topics from "./user_topics";
import * as util from "./util";
@ -75,12 +74,15 @@ type ValidOrInvalidUser =
const CHANNEL_SYNONYM = "stream";
const CHANNELS_SYNONYM = "streams";
function zephyr_stream_name_match(message: Message & {type: "stream"}, operand: string): boolean {
function zephyr_stream_name_match(
message: Message & {type: "stream"},
stream_name: string,
): boolean {
// Zephyr users expect narrowing to "social" to also show messages to /^(un)*social(.d)*$/
// (unsocial, ununsocial, social.d, etc)
// TODO: hoist the regex compiling out of the closure
const m = /^(?:un)*(.+?)(?:\.d)*$/i.exec(operand);
let base_stream_name = operand;
const m = /^(?:un)*(.+?)(?:\.d)*$/i.exec(stream_name);
let base_stream_name = stream_name;
if (m?.[1] !== undefined) {
base_stream_name = m[1];
}
@ -88,8 +90,8 @@ function zephyr_stream_name_match(message: Message & {type: "stream"}, operand:
/^(un)*/.source + _.escapeRegExp(base_stream_name) + /(\.d)*$/.source,
"i",
);
const stream_name = stream_data.get_stream_name_from_id(message.stream_id);
return related_regexp.test(stream_name);
const message_stream_name = stream_data.get_stream_name_from_id(message.stream_id);
return related_regexp.test(message_stream_name);
}
function zephyr_topic_name_match(message: Message & {type: "stream"}, operand: string): boolean {
@ -209,15 +211,12 @@ function message_matches_search_term(message: Message, operator: string, operand
return false;
}
operand = operand.toLowerCase();
if (realm.realm_is_zephyr_mirror_realm) {
return zephyr_stream_name_match(message, operand);
const stream = stream_data.get_sub_by_id_string(operand);
return zephyr_stream_name_match(message, stream?.name ?? "");
}
// Try to match by stream_id if have a valid sub for
// the operand. If we can't find the id, we return false.
const stream_id = stream_data.get_stream_id(operand);
return stream_id !== undefined && message.stream_id === stream_id;
return message.stream_id.toString() === operand;
}
case "topic":
@ -291,7 +290,6 @@ const USER_OPERATORS = new Set([
export class Filter {
_terms: NarrowTerm[];
_sub?: StreamSubscription | undefined;
_sorted_term_types?: string[] = undefined;
_predicate?: (message: Message) => boolean;
_can_mark_messages_read?: boolean;
@ -355,7 +353,6 @@ export class Filter {
break;
case "channel":
operand = stream_data.get_name(operand);
break;
case "topic":
break;
@ -495,6 +492,18 @@ export class Filter {
}
operand = Filter.decodeOperand(parts.join(":"), operator);
// Check for user-entered channel name. If the name is valid,
// convert it to id.
if (
(operator === "stream" || operator === "channel") &&
Number.isNaN(Number.parseInt(operand, 10))
) {
const sub = stream_data.get_sub(operand);
if (sub) {
operand = sub.stream_id.toString();
}
}
// We use Filter.operator_to_prefix() to check if the
// operator is known. If it is not known, then we treat
// it as a search for the given string (which may contain
@ -545,7 +554,7 @@ export class Filter {
return Number.isInteger(Number(term.operand));
case "channel":
case "stream":
return stream_data.get_sub(term.operand) !== undefined;
return stream_data.get_sub_by_id_string(term.operand) !== undefined;
case "channels":
case "streams":
return term.operand === "public";
@ -715,7 +724,7 @@ export class Filter {
Filter.canonicalize_operator(term.operator) === expected && !term.negated;
if (is(terms[0], "channel") && is(terms[1], "topic")) {
const channel = terms[0].operand;
const channel = stream_data.get_valid_sub_by_id_string(terms[0].operand).name;
const topic = terms[1].operand;
parts.push({
type: "channel_topic",
@ -783,6 +792,18 @@ export class Filter {
};
}
if (prefix_for_operator !== "") {
if (canonicalized_operator === "channel") {
const stream = stream_data.get_sub_by_id_string(operand);
if (stream) {
return {
type: "prefix_for_operator",
prefix_for_operator,
operand: stream.name,
};
}
// Assume the operand is a partially formed name and return
// the operator as the stream name in the next block.
}
return {
type: "prefix_for_operator",
prefix_for_operator,
@ -863,12 +884,11 @@ export class Filter {
const adjusted_term = {...term};
if (
Filter.canonicalize_operator(term.operator) === "channel" &&
!util.lower_same(term.operand, message.display_recipient)
term.operand !== message.stream_id.toString()
) {
adjusted_term.operand = message.display_recipient;
adjusted_term.operand = message.stream_id.toString();
terms_changed = true;
}
if (
Filter.canonicalize_operator(term.operator) === "topic" &&
!util.lower_same(term.operand, message.topic)
@ -890,11 +910,6 @@ export class Filter {
setup_filter(terms: NarrowTerm[]): void {
this._terms = this.fix_terms(terms);
this.cached_sorted_terms_for_comparison = undefined;
if (this.has_operator("channel")) {
this._sub = stream_data.get_sub_by_name(this.operands("channel")[0]!);
} else {
this._sub = undefined;
}
}
equals(filter: Filter, excluded_operators?: string[]): boolean {
@ -948,12 +963,15 @@ export class Filter {
public_terms(): NarrowTerm[] {
const safe_to_return = this._terms.filter(
// Filter out the embedded narrow (if any).
(term) =>
!(
page_params.narrow_stream !== undefined &&
term.operator === "channel" &&
term.operand.toLowerCase() === page_params.narrow_stream.toLowerCase()
),
(term) => {
// TODO(stream_id): Ideally we have `page_params.narrow_stream_id`
if (page_params.narrow_stream === undefined || term.operator !== "channel") {
return true;
}
const narrow_stream = stream_data.get_sub_by_name(page_params.narrow_stream);
assert(narrow_stream !== undefined);
return Number.parseInt(term.operand, 10) === narrow_stream.stream_id;
},
);
return safe_to_return;
}
@ -1170,15 +1188,16 @@ export class Filter {
return "/#narrow/has/reaction/sender/me";
}
if (_.isEqual(term_types, ["channel", "topic", "search"])) {
const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!);
// if channel does not exist, redirect to home view
if (!this._sub) {
if (!sub) {
return "#";
}
return (
"/#narrow/" +
CHANNEL_SYNONYM +
"/" +
stream_data.name_to_slug(this.operands("channel")[0]!) +
stream_data.id_to_slug(sub.stream_id) +
"/topic/" +
this.operands("topic")[0]
);
@ -1191,17 +1210,16 @@ export class Filter {
if (term_types[1] === "search") {
switch (term_types[0]) {
case "channel":
case "channel": {
const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!);
// if channel does not exist, redirect to home view
if (!this._sub) {
if (!sub) {
return "#";
}
return (
"/#narrow/" +
CHANNEL_SYNONYM +
"/" +
stream_data.name_to_slug(this.operands("channel")[0]!)
"/#narrow/" + CHANNEL_SYNONYM + "/" + stream_data.id_to_slug(sub.stream_id)
);
}
case "is-dm":
return "/#narrow/is/dm";
case "is-starred":
@ -1250,21 +1268,23 @@ export class Filter {
case "in-all":
icon = "home";
break;
case "channel":
if (!this._sub) {
case "channel": {
const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!);
if (!sub) {
icon = "question-circle-o";
break;
}
if (this._sub.invite_only) {
if (sub.invite_only) {
zulip_icon = "lock";
break;
}
if (this._sub.is_web_public) {
if (sub.is_web_public) {
zulip_icon = "globe";
break;
}
zulip_icon = "hashtag";
break;
}
case "is-dm":
zulip_icon = "user";
break;
@ -1302,11 +1322,11 @@ export class Filter {
(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("channel")[0];
return $t({defaultMessage: "Unknown channel #{search_text}"}, {search_text});
const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!);
if (!sub) {
return $t({defaultMessage: "Unknown channel"});
}
return this._sub.name;
return sub.name;
}
if (
(term_types.length === 2 && _.isEqual(term_types, ["dm", "near"])) ||
@ -1512,8 +1532,10 @@ export class Filter {
return new Filter(terms);
}
has_topic(stream_name: string, topic: string): boolean {
return this.has_operand("channel", stream_name) && this.has_operand("topic", topic);
has_topic(stream_id: number, topic: string): boolean {
return (
this.has_operand("channel", stream_id.toString()) && this.has_operand("topic", topic)
);
}
sorted_term_types(): string[] {
@ -1668,12 +1690,12 @@ export class Filter {
this.requires_adjustment_for_moved_with_target = false;
}
can_newly_match_moved_messages(new_channel: string, new_topic: string): boolean {
can_newly_match_moved_messages(new_channel_id: string, new_topic: string): boolean {
// Checks if any of the operators on this Filter object have
// the property that it's possible for their true value to
// change as a result of messages being moved into the
// channel/topic pair provided in the parameters.
if (this.has_operand_case_insensitive("channel", new_channel)) {
if (this.has_operand_case_insensitive("channel", new_channel_id)) {
return true;
}

View File

@ -36,18 +36,19 @@ export function encode_operand(operator: string, operand: string): string {
}
if (operator === "stream") {
return encode_stream_name(operand);
const stream_id = Number.parseInt(operand, 10);
return encode_stream_id(stream_id);
}
return internal_url.encodeHashComponent(operand);
}
export function encode_stream_name(operand: string): string {
// stream_data prefixes the stream id, but it does not do the
export function encode_stream_id(stream_id: number): string {
// stream_data postfixes the stream name, but it does not do the
// URI encoding piece
operand = stream_data.name_to_slug(operand);
const slug = stream_data.id_to_slug(stream_id);
return internal_url.encodeHashComponent(operand);
return internal_url.encodeHashComponent(slug);
}
export function decode_operand(operator: string, operand: string): string {
@ -67,13 +68,7 @@ export function decode_operand(operator: string, operand: string): string {
operand = internal_url.decodeHashComponent(operand);
if (util.canonicalize_stream_synonyms(operator) === "stream") {
const stream_id = stream_data.slug_to_stream_id(operand);
if (stream_id) {
const stream = stream_data.get_sub_by_id(stream_id);
if (stream) {
return stream.name;
}
}
return stream_data.slug_to_stream_id(operand)?.toString() ?? "";
}
return operand;
@ -294,7 +289,7 @@ export function validate_group_settings_hash(hash: string): string {
export function decode_stream_topic_from_url(
url_str: string,
): {stream_name: string; topic_name?: string} | null {
): {stream_id: number; topic_name?: string} | null {
try {
const url = new URL(url_str);
if (url.origin !== window.location.origin || !url.hash.startsWith("#narrow")) {
@ -314,13 +309,18 @@ export function decode_stream_topic_from_url(
if (terms[0]?.operator !== "stream" && terms[0]?.operator !== "channel") {
return null;
}
const stream_id = Number.parseInt(terms[0].operand, 10);
// This can happen if we don't recognize the stream operand as a valid id.
if (Number.isNaN(stream_id)) {
return null;
}
if (terms.length === 1) {
return {stream_name: terms[0].operand};
return {stream_id};
}
if (terms[1]?.operator !== "topic") {
return null;
}
return {stream_name: terms[0].operand, topic_name: terms[1].operand};
return {stream_id, topic_name: terms[1].operand};
} catch {
return null;
}

View File

@ -54,7 +54,6 @@ import * as sidebar_ui from "./sidebar_ui";
import * as spectators from "./spectators";
import * as starred_messages_ui from "./starred_messages_ui";
import {realm} from "./state_data";
import * as stream_data from "./stream_data";
import * as stream_list from "./stream_list";
import * as stream_popover from "./stream_popover";
import * as stream_settings_ui from "./stream_settings_ui";
@ -1234,7 +1233,7 @@ export function process_hotkey(e, hotkey) {
[
{
operator: "channel",
operand: stream_data.get_stream_name_from_id(msg.stream_id),
operand: msg.stream_id.toString(),
},
{operator: "topic", operand: msg.topic},
{operator: "near", operand: msg.id},

View File

@ -472,7 +472,6 @@ export function update_messages(events) {
});
}
const old_stream_name = stream_archived ? undefined : old_stream.name;
if (
going_forward_change &&
// This logic is a bit awkward. What we're trying to
@ -490,7 +489,8 @@ export function update_messages(events) {
// messages within a narrow.
selection_changed_topic &&
current_filter &&
current_filter.has_topic(old_stream_name, orig_topic)
old_stream_id &&
current_filter.has_topic(old_stream_id, orig_topic)
) {
let new_filter = current_filter;
if (new_filter && stream_changed) {
@ -501,10 +501,9 @@ export function update_messages(events) {
// stream_data lookup here to fail.
//
// The fix is likely somewhat involved, so punting for now.
const new_stream_name = sub_store.get(new_stream_id).name;
new_filter = new_filter.filter_with_new_params({
operator: "channel",
operand: new_stream_name,
operand: new_stream_id.toString(),
});
changed_narrow = true;
}
@ -533,10 +532,10 @@ export function update_messages(events) {
// If a message was moved to the current narrow and we don't have
// the message cached, we need to refresh the narrow to display the message.
if (!changed_narrow && local_cache_missing_messages && current_filter) {
let moved_message_stream = old_stream_name;
let moved_message_stream_id = old_stream_id;
let moved_message_topic = orig_topic;
if (stream_changed) {
moved_message_stream = sub_store.get(new_stream_id).name;
moved_message_stream_id = sub_store.get(new_stream_id).stream_id.toString();
}
if (topic_edited) {
@ -545,7 +544,7 @@ export function update_messages(events) {
if (
current_filter.can_newly_match_moved_messages(
moved_message_stream,
moved_message_stream_id,
moved_message_topic,
)
) {

View File

@ -340,7 +340,25 @@ export function load_messages(opts: MessageFetchOptions, attempt = 1): void {
if (page_params.narrow !== undefined) {
terms = [...terms, ...page_params.narrow];
}
data.narrow = JSON.stringify(terms);
// TODO(stream_id): The server would ideally work with stream ids instead
// of stream names.
const server_terms = [];
for (const term of terms) {
if (term.operator === "channel") {
const sub = stream_data.get_sub_by_id_string(term.operand);
server_terms.push({
...term,
// If the sub is undefined, we'll get an error which is handled
// later by showing a "this channel does not exist or is private"
// notice.
operand: sub?.name,
});
} else {
server_terms.push({...term});
}
}
data.narrow = JSON.stringify(server_terms);
}
let update_loading_indicator =

View File

@ -450,10 +450,11 @@ export function show(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("channel")[0];
const narrow_stream_data = stream_data.get_sub(narrow_stream_name);
const narrow_stream_data = stream_data.get_sub_by_id_string(
filter.operands("channel")[0],
);
if (!narrow_stream_data) {
// The stream name is invalid or incorrect in the URL.
// The stream id is invalid or incorrect in the URL.
// We reconstruct the narrow with the data from the
// target message ID that we have.
const adjusted_terms = Filter.adjusted_terms_if_moved(
@ -1085,10 +1086,9 @@ export function render_message_list_with_selected_message(opts) {
narrow_history.save_narrow_state_and_flush();
}
export function activate_stream_for_cycle_hotkey(stream_id) {
const stream_name = stream_data.get_stream_name_from_id(stream_id);
function activate_stream_for_cycle_hotkey(stream_id) {
// This is the common code for A/D hotkeys.
const filter_expr = [{operator: "channel", operand: stream_name}];
const filter_expr = [{operator: "channel", operand: stream_id.toString()}];
show(filter_expr, {});
}
@ -1158,9 +1158,8 @@ export function narrow_to_next_topic(opts = {}) {
return;
}
const stream_name = stream_data.get_stream_name_from_id(next_narrow.stream_id);
const filter_expr = [
{operator: "channel", operand: stream_name},
{operator: "channel", operand: next_narrow.stream_id.toString()},
{operator: "topic", operand: next_narrow.topic},
];
@ -1218,9 +1217,8 @@ export function narrow_by_topic(target_id, opts) {
unread_ops.notify_server_message_read(original);
}
const stream_name = stream_data.get_stream_name_from_id(original.stream_id);
const search_terms = [
{operator: "channel", operand: stream_name},
{operator: "channel", operand: original.stream_id.toString()},
{operator: "topic", operand: original.topic},
];
opts = {then_select_id: target_id, ...opts};
@ -1264,7 +1262,7 @@ export function narrow_by_recipient(target_id, opts) {
[
{
operator: "stream",
operand: stream_data.get_stream_name_from_id(message.stream_id),
operand: message.stream_id.toString(),
},
],
opts,
@ -1289,10 +1287,9 @@ export function to_compose_target() {
if (!stream_id) {
return;
}
const stream_name = stream_data.get_sub_by_id(stream_id).name;
// If we are composing to a new topic, we narrow to the stream but
// grey-out the message view instead of narrowing to an empty view.
const terms = [{operator: "channel", operand: stream_name}];
const terms = [{operator: "channel", operand: stream_id.toString()}];
const topic = compose_state.topic();
if (topic !== "") {
terms.push({operator: "topic", operand: topic});

View File

@ -14,6 +14,7 @@ import * as recent_view_util from "./recent_view_util";
import * as rendered_markdown from "./rendered_markdown";
import * as search from "./search";
import {current_user} from "./state_data";
import * as stream_data from "./stream_data";
import type {SettingsSubscription} from "./stream_settings_data";
import type {StreamSubscription} from "./sub_store";
@ -99,22 +100,21 @@ function get_message_view_header_context(filter: Filter | undefined): MessageVie
is_spectator: page_params.is_spectator,
});
if (filter.has_operator("channel") && !filter._sub) {
return {
...context,
sub_count: "0",
formatted_sub_count: "0",
rendered_narrow_description: $t({
defaultMessage: "This channel does not exist or is private.",
}),
};
}
if (filter._sub) {
if (filter.has_operator("channel")) {
const current_stream = stream_data.get_sub_by_id_string(filter.operands("channel")[0]!);
if (!current_stream) {
return {
...context,
sub_count: "0",
formatted_sub_count: "0",
rendered_narrow_description: $t({
defaultMessage: "This channel does not exist or is private.",
}),
};
}
// We can now be certain that the narrow
// involves a stream which exists and
// the current user can access.
const current_stream = filter._sub;
const sub_count = peer_data.get_subscriber_count(current_stream.stream_id);
return {
...context,
@ -131,11 +131,15 @@ function get_message_view_header_context(filter: Filter | undefined): MessageVie
export function colorize_message_view_header(): void {
const filter = narrow_state.filter();
if (filter === undefined || !filter._sub) {
let current_stream;
if (filter?.has_operator("channel")) {
current_stream = stream_data.get_valid_sub_by_id_string(filter.operands("channel")[0]!);
}
if (!current_stream) {
return;
}
// selecting i instead of .fa because web public streams have custom icon.
$("#message_view_header a.stream i").css("color", filter._sub.color);
$("#message_view_header a.stream i").css("color", current_stream.color);
}
function append_and_display_title_area(context: MessageViewHeaderContext): void {
@ -175,10 +179,10 @@ export function render_title_area(): void {
// This function checks if "modified_sub" which is the stream whose values
// have been updated is the same as the stream which is currently
// narrowed (filter._sub) and rerenders if necessary
// narrowed and rerenders if necessary
export function maybe_rerender_title_area_for_stream(modified_sub: SettingsSubscription): void {
const filter = narrow_state.filter();
if (filter && filter._sub && filter._sub.stream_id === modified_sub.stream_id) {
const current_stream_id = narrow_state.stream_id();
if (current_stream_id === modified_sub.stream_id) {
render_title_area();
}
}

View File

@ -76,10 +76,11 @@ function retrieve_search_query_data(): SearchData {
// Add in stream:foo and topic:bar if present
if (current_filter.has_operator("channel") || current_filter.has_operator("topic")) {
const stream = current_filter.operands("channel")[0];
const stream_id = current_filter.operands("channel")[0];
const topic = current_filter.operands("topic")[0];
if (stream) {
search_string_result.stream_query = stream;
if (stream_id) {
const stream_name = stream_data.get_valid_sub_by_id_string(stream_id).name;
search_string_result.stream_query = stream_name;
}
if (topic) {
search_string_result.topic_query = topic;
@ -184,7 +185,7 @@ export function pick_empty_narrow_banner(): NarrowBannerData {
if (
page_params.is_spectator &&
first_operator === "channel" &&
!stream_data.is_web_public_by_stream_name(first_operand)
!stream_data.is_web_public_by_stream_id(Number.parseInt(first_operand, 10))
) {
// For non web-public streams, show `login_to_access` modal.
spectators.login_to_access(true);
@ -264,7 +265,7 @@ export function pick_empty_narrow_banner(): NarrowBannerData {
// fallthrough to default case if no match is found
break;
case "channel":
if (!stream_data.is_subscribed_by_name(first_operand)) {
if (!stream_data.is_subscribed(Number.parseInt(first_operand, 10))) {
// You are narrowed to a stream which does not exist or is a private stream
// in which you were never subscribed.
@ -280,7 +281,7 @@ export function pick_empty_narrow_banner(): NarrowBannerData {
return false;
}
const stream_sub = stream_data.get_sub(first_operand);
const stream_sub = stream_data.get_sub_by_id_string(first_operand);
return stream_sub && stream_data.can_toggle_subscription(stream_sub);
}

View File

@ -114,21 +114,26 @@ export function set_compose_defaults(): {
return opts;
}
export function stream_name(current_filter: Filter | undefined = filter()): string | undefined {
export function stream_id(current_filter: Filter | undefined = filter()): number | undefined {
if (current_filter === undefined) {
return undefined;
}
const stream_operands = current_filter.operands("channel");
if (stream_operands.length === 1 && stream_operands[0] !== undefined) {
const name = stream_operands[0];
// Use get_name() to get the most current stream
// name (considering renames and capitalization).
return stream_data.get_name(name);
return Number.parseInt(stream_operands[0], 10);
}
return undefined;
}
export function stream_name(current_filter: Filter | undefined = filter()): string | undefined {
const id = stream_id(current_filter);
if (id === undefined) {
return undefined;
}
const sub = stream_data.get_sub_by_id(id);
return sub?.name;
}
export function stream_sub(
current_filter: Filter | undefined = filter(),
): StreamSubscription | undefined {
@ -140,19 +145,7 @@ export function stream_sub(
if (stream_operands.length !== 1 || stream_operands[0] === undefined) {
return undefined;
}
const name = stream_operands[0];
const sub = stream_data.get_sub_by_name(name);
return sub;
}
export function stream_id(filter?: Filter): number | undefined {
const sub = stream_sub(filter);
if (sub === undefined) {
return undefined;
}
return sub.stream_id;
return stream_data.get_sub_by_id_string(stream_operands[0]);
}
export function topic(current_filter: Filter | undefined = filter()): string | undefined {

View File

@ -8,6 +8,7 @@ import * as inbox_util from "./inbox_util";
import * as people from "./people";
import * as recent_view_util from "./recent_view_util";
import {realm} from "./state_data";
import * as stream_data from "./stream_data";
import * as unread from "./unread";
import type {FullUnreadCountsData} from "./unread";
@ -34,10 +35,11 @@ export function compute_narrow_title(filter?: Filter): string {
}
if (filter.has_operator("channel")) {
if (!filter._sub) {
const sub = stream_data.get_sub_by_id_string(filter.operands("channel")[0]!);
if (!sub) {
// The stream is not set because it does not currently
// exist (possibly due to a stream name change), or it
// is a private stream and the user is not subscribed.
// exist, or it is a private stream and the user is not
// subscribed.
return filter_title;
}
if (filter.has_operator("topic")) {

View File

@ -8,7 +8,6 @@ import {$t} from "./i18n";
import * as message_view from "./message_view";
import * as people from "./people";
import * as scheduled_messages from "./scheduled_messages";
import * as stream_data from "./stream_data";
import * as timerender from "./timerender";
export function hide_scheduled_message_success_compose_banner(scheduled_message_id) {
@ -23,7 +22,7 @@ function narrow_via_edit_scheduled_message(compose_args) {
[
{
operator: "channel",
operand: stream_data.get_stream_name_from_id(compose_args.stream_id),
operand: compose_args.stream_id,
},
{operator: "topic", operand: compose_args.topic},
],

View File

@ -10,6 +10,7 @@ import type {InputPill, InputPillContainer} from "./input_pill";
import * as people from "./people";
import type {User} from "./people";
import type {NarrowTerm} from "./state_data";
import * as stream_data from "./stream_data";
import * as user_status from "./user_status";
import type {UserStatusEmojiInfo} from "./user_status";
import * as util from "./util";
@ -56,7 +57,7 @@ export function create_item_from_search_string(search_string: string): SearchPil
export function get_search_string_from_item(item: SearchPill): string {
const sign = item.negated ? "-" : "";
return `${sign}${item.operator}: ${get_search_operand(item)}`;
return `${sign}${item.operator}: ${get_search_operand(item, true)}`;
}
// This is called when the a pill is closed. We have custom logic here
@ -215,16 +216,19 @@ export function set_search_bar_contents(
}
}
function get_search_operand(item: SearchPill): string {
function get_search_operand(item: SearchPill, for_display: boolean): string {
if (item.type === "search_user") {
return item.users.map((user) => user.email).join(",");
}
if (for_display && item.operator === "channel") {
return stream_data.get_valid_sub_by_id_string(item.operand).name;
}
return item.operand;
}
export function get_current_search_pill_terms(pill_widget: SearchPillWidget): NarrowTerm[] {
return pill_widget.items().map((item) => ({
...item,
operand: get_search_operand(item),
operand: get_search_operand(item, false),
}));
}

View File

@ -159,21 +159,23 @@ function get_channel_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggest
const query = last.operand;
let channels = stream_data.subscribed_streams();
channels = channels.filter((channel) => channel_matches_query(channel, query));
channels = channels.filter((channel_name) => channel_matches_query(channel_name, query));
channels = typeahead_helper.sorter(query, channels, (x) => x);
const regex = typeahead_helper.build_highlight_regex(query);
const highlight_query = typeahead_helper.highlight_with_escaping_and_regex;
return channels.map((channel) => {
return channels.map((channel_name) => {
const prefix = "channel";
const highlighted_channel = highlight_query(regex, channel);
const highlighted_channel = highlight_query(regex, channel_name);
const verb = last.negated ? "exclude " : "";
const description_html = verb + prefix + " " + highlighted_channel;
const channel = stream_data.get_sub_by_name(channel_name);
assert(channel !== undefined);
const term = {
operator: "channel",
operand: channel,
operand: channel.stream_id.toString(),
negated: last.negated,
};
const search_string = Filter.unparse([term]);
@ -473,7 +475,7 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio
const operator = Filter.canonicalize_operator(last.operator);
const operand = last.operand;
const negated = operator === "topic" && last.negated;
let channel: string | undefined;
let channel_id: string | undefined;
let guess: string | undefined;
const filter = new Filter(terms);
const suggest_terms: NarrowTerm[] = [];
@ -496,28 +498,28 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio
switch (operator) {
case "channel":
guess = "";
channel = operand;
channel_id = operand;
suggest_terms.push(last);
break;
case "topic":
case "search":
guess = operand;
if (filter.has_operator("channel")) {
channel = filter.operands("channel")[0];
channel_id = filter.operands("channel")[0];
} else {
channel = narrow_state.stream_name();
if (channel) {
suggest_terms.push({operator: "channel", operand: channel});
channel_id = narrow_state.stream_id()?.toString();
if (channel_id) {
suggest_terms.push({operator: "channel", operand: channel_id});
}
}
break;
}
if (!channel) {
if (!channel_id) {
return [];
}
const subscription = stream_data.get_sub(channel);
const subscription = stream_data.get_sub_by_id_string(channel_id);
if (!subscription) {
return [];
}
@ -849,12 +851,12 @@ function suggestion_search_string(suggestion_line: SuggestionLine): string {
}
function suggestions_for_current_filter(): SuggestionLine[] {
if (narrow_state.stream_name() && narrow_state.topic() !== "") {
if (narrow_state.stream_id() && narrow_state.topic() !== "") {
return [
get_default_suggestion_line([
{
operator: "channel",
operand: narrow_state.stream_name()!,
operand: narrow_state.stream_id()!.toString(),
},
]),
get_default_suggestion_line(narrow_state.search_terms()),

View File

@ -1,3 +1,5 @@
import assert from "minimalistic-assert";
import * as blueslip from "./blueslip";
import * as color_data from "./color_data";
import {FoldDict} from "./fold_dict";
@ -177,6 +179,18 @@ export function get_sub(stream_name: string): StreamSubscription | undefined {
return undefined;
}
export function get_sub_by_id_string(stream_id_string: string): StreamSubscription | undefined {
const stream_id = Number.parseInt(stream_id_string, 10);
const stream = stream_info.get(stream_id);
return stream;
}
export function get_valid_sub_by_id_string(stream_id_string: string): StreamSubscription {
const stream = get_sub_by_id_string(stream_id_string);
assert(stream !== undefined);
return stream;
}
export function get_sub_by_id(stream_id: number): StreamSubscription | undefined {
return stream_info.get(stream_id);
}
@ -222,13 +236,8 @@ export function get_sub_by_name(name: string): StreamSubscription | undefined {
return sub_store.get(stream_id);
}
export function name_to_slug(name: string): string {
const stream_id = get_stream_id(name);
if (!stream_id) {
return name;
}
export function id_to_slug(stream_id: number): string {
let name = get_stream_name_from_id(stream_id);
// The name part of the URL doesn't really matter, so we try to
// make it pretty.
name = name.replaceAll(" ", "-");
@ -605,11 +614,6 @@ export function can_post_messages_in_stream(stream: StreamSubscription): boolean
return true;
}
export function is_subscribed_by_name(stream_name: string): boolean {
const sub = get_sub(stream_name);
return sub ? sub.subscribed : false;
}
export function is_subscribed(stream_id: number): boolean {
const sub = sub_store.get(stream_id);
return sub ? sub.subscribed : false;
@ -643,8 +647,8 @@ export function is_invite_only_by_stream_id(stream_id: number): boolean {
return sub.invite_only;
}
export function is_web_public_by_stream_name(stream_name: string): boolean {
const sub = get_sub(stream_name);
export function is_web_public_by_stream_id(stream_id: number): boolean {
const sub = get_sub_by_id(stream_id);
if (sub === undefined) {
return false;
}
@ -667,22 +671,6 @@ export function is_default_stream_id(stream_id: number): boolean {
return default_stream_ids.has(stream_id);
}
export function get_name(stream_name: string): string {
// This returns the actual name of a stream if we are subscribed to
// it (e.g. "Denmark" vs. "denmark"), while falling thru to
// stream_name if we don't have a subscription. (Stream names
// are case-insensitive, but we try to display the actual name
// when we know it.)
//
// This function will also do the right thing if we have
// an old stream name in memory for a recently renamed stream.
const sub = get_sub_by_name(stream_name);
if (sub === undefined) {
return stream_name;
}
return sub.name;
}
export function is_user_subscribed(stream_id: number, user_id: number): boolean {
const sub = sub_store.get(stream_id);
if (sub === undefined || !can_view_subscribers(sub)) {

View File

@ -682,8 +682,7 @@ export function get_sidebar_stream_topic_info(filter: Filter): {
return result;
}
const stream_name = op_stream[0];
const stream_id = stream_data.get_stream_id(stream_name);
const stream_id = Number.parseInt(op_stream[0], 10);
if (!stream_id) {
return result;

View File

@ -138,7 +138,7 @@ function build_stream_popover(opts) {
[
{
operator: "stream",
operand: sub.name,
operand: sub.stream_id.toString(),
},
],
{trigger: "stream-popover"},

View File

@ -903,7 +903,7 @@ export function view_stream() {
const row_data = get_row_data(active_data.$row);
if (row_data) {
const stream_narrow_hash =
"#narrow/stream/" + hash_util.encode_stream_name(row_data.object.name);
"#narrow/stream/" + hash_util.encode_stream_id(row_data.object.stream_id);
browser_history.go_to_location(stream_narrow_hash);
}
}

View File

@ -1,5 +1,4 @@
import _ from "lodash";
import assert from "minimalistic-assert";
import * as narrow_state from "./narrow_state";
import * as pm_conversations from "./pm_conversations";
@ -87,7 +86,6 @@ export function get_next_topic(
function get_unmuted_topics(stream_id: number): string[] {
const narrowed_steam_id = narrow_state.stream_id();
assert(stream_id !== undefined);
const topics = stream_topic_history.get_recent_topic_names(stream_id);
const narrowed_topic = narrow_state.topic();
if (
@ -119,16 +117,11 @@ export function get_next_topic(
return topics;
}
function has_unread_messages(stream_id: number, topic: string): boolean {
assert(stream_id !== undefined);
return unread.topic_has_any_unread(stream_id, topic);
}
if (only_followed_topics) {
return next_topic(
my_streams,
get_followed_topics,
has_unread_messages,
unread.topic_has_any_unread,
curr_stream_id,
curr_topic,
);
@ -137,7 +130,7 @@ export function get_next_topic(
return next_topic(
my_streams,
get_unmuted_topics,
has_unread_messages,
unread.topic_has_any_unread,
curr_stream_id,
curr_topic,
);

View File

@ -549,7 +549,7 @@ export function initialize_everything(state_data) {
[
{
operator: "stream",
operand: sub.name,
operand: sub.stream_id.toString(),
},
],
{trigger},
@ -658,7 +658,7 @@ export function initialize_everything(state_data) {
const sub = sub_store.get(stream_id);
message_view.show(
[
{operator: "channel", operand: sub.name},
{operator: "channel", operand: sub.stream_id.toString()},
{operator: "topic", operand: topic},
],
{trigger: "sidebar"},

View File

@ -22,7 +22,6 @@ import * as overlays from "./overlays";
import * as people from "./people";
import * as recent_view_ui from "./recent_view_ui";
import type {NarrowTerm} from "./state_data";
import * as stream_data from "./stream_data";
import * as ui_report from "./ui_report";
import * as unread from "./unread";
import * as unread_ui from "./unread_ui";
@ -610,22 +609,20 @@ export function process_visible(): void {
}
export function mark_stream_as_read(stream_id: number): void {
const stream_name = stream_data.get_stream_name_from_id(stream_id);
bulk_update_read_flags_for_narrow(
[
{operator: "is", operand: "unread", negated: false},
{operator: "channel", operand: stream_name},
{operator: "channel", operand: stream_id.toString()},
],
"add",
);
}
export function mark_topic_as_read(stream_id: number, topic: string): void {
const stream_name = stream_data.get_stream_name_from_id(stream_id);
bulk_update_read_flags_for_narrow(
[
{operator: "is", operand: "unread", negated: false},
{operator: "channel", operand: stream_name},
{operator: "channel", operand: stream_id.toString()},
{operator: "topic", operand: topic},
],
"add",
@ -633,10 +630,9 @@ export function mark_topic_as_read(stream_id: number, topic: string): void {
}
export function mark_topic_as_unread(stream_id: number, topic: string): void {
const stream_name = stream_data.get_stream_name_from_id(stream_id);
bulk_update_read_flags_for_narrow(
[
{operator: "channel", operand: stream_name},
{operator: "channel", operand: stream_id.toString()},
{operator: "topic", operand: topic},
],
"remove",

View File

@ -105,7 +105,7 @@ const $fred_stub = $.create("fred stub");
const rome_sub = {name: "Rome", subscribed: true, stream_id: 1001};
function add_sub_and_set_as_current_narrow(sub) {
stream_data.add_sub(sub);
const filter = new Filter([{operator: "stream", operand: sub.name}]);
const filter = new Filter([{operator: "stream", operand: sub.stream_id}]);
message_lists.set_current({
data: {
filter,

View File

@ -34,7 +34,7 @@ run_test("filter", () => {
stream_data.add_sub(denmark_stream);
const filter_terms = [
{operator: "stream", operand: "Denmark"},
{operator: "stream", operand: denmark_stream.stream_id.toString()},
{operator: "topic", operand: "copenhagen"},
];
@ -85,7 +85,7 @@ run_test("narrow_state", () => {
// Now set up a Filter object.
const filter_terms = [
{operator: "stream", operand: "Denmark"},
{operator: "stream", operand: denmark_stream.stream_id.toString()},
{operator: "topic", operand: "copenhagen"},
];

File diff suppressed because it is too large Load Diff

View File

@ -21,8 +21,9 @@ const hamlet = {
people.add_active_user(hamlet);
const frontend_id = 99;
const frontend = {
stream_id: 99,
stream_id: frontend_id,
name: "frontend",
};
@ -44,7 +45,7 @@ run_test("hash_util", () => {
encode_decode_operand(operator, operand, "15-Hamlet");
operator = "stream";
operand = "frontend";
operand = frontend_id.toString();
encode_decode_operand(operator, operand, "99-frontend");
@ -175,19 +176,19 @@ run_test("test_is_in_specified_hash_category", () => {
run_test("test_parse_narrow", () => {
assert.deepEqual(hash_util.parse_narrow(["narrow", "stream", "99-frontend"]), [
{negated: false, operator: "stream", operand: "frontend"},
{negated: false, operator: "stream", operand: frontend_id.toString()},
]);
assert.deepEqual(hash_util.parse_narrow(["narrow", "-stream", "99-frontend"]), [
{negated: true, operator: "stream", operand: "frontend"},
{negated: true, operator: "stream", operand: frontend_id.toString()},
]);
assert.equal(hash_util.parse_narrow(["narrow", "BOGUS"]), undefined);
// For nonexistent streams, we get the full slug.
// We possibly should remove the prefix and fix this test.
// For nonexistent streams, we get an empty string. We don't use this
// anywhere, and just show "Invalid stream" in the navbar.
assert.deepEqual(hash_util.parse_narrow(["narrow", "stream", "42-bogus"]), [
{negated: false, operator: "stream", operand: "42-bogus"},
{negated: false, operator: "stream", operand: ""},
]);
});

View File

@ -38,48 +38,60 @@ const message_view = zrequire("../src/message_view");
const stream_data = zrequire("stream_data");
const {Filter} = zrequire("../src/filter");
const devel_id = 100;
const devel = {
name: "devel",
stream_id: devel_id,
color: "blue",
subscribed: true,
};
stream_data.add_sub(devel);
run_test("terms_round_trip", () => {
let terms;
let hash;
let narrow;
terms = [
{operator: "stream", operand: "devel"},
{operator: "stream", operand: devel_id.toString()},
{operator: "topic", operand: "algol"},
];
hash = hash_util.search_terms_to_hash(terms);
assert.equal(hash, "#narrow/stream/devel/topic/algol");
assert.equal(hash, "#narrow/stream/100-devel/topic/algol");
narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [
{operator: "stream", operand: "devel", negated: false},
{operator: "stream", operand: devel_id.toString(), negated: false},
{operator: "topic", operand: "algol", negated: false},
]);
terms = [
{operator: "stream", operand: "devel"},
{operator: "stream", operand: devel_id.toString()},
{operator: "topic", operand: "visual c++", negated: true},
];
hash = hash_util.search_terms_to_hash(terms);
assert.equal(hash, "#narrow/stream/devel/-topic/visual.20c.2B.2B");
assert.equal(hash, "#narrow/stream/100-devel/-topic/visual.20c.2B.2B");
narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [
{operator: "stream", operand: "devel", negated: false},
{operator: "stream", operand: devel_id.toString(), negated: false},
{operator: "topic", operand: "visual c++", negated: true},
]);
// test new encodings, where we have a stream id
const florida_id = 987;
const florida_stream = {
name: "Florida, USA",
stream_id: 987,
stream_id: florida_id,
};
stream_data.add_sub(florida_stream);
terms = [{operator: "stream", operand: "Florida, USA"}];
terms = [{operator: "stream", operand: florida_id.toString()}];
hash = hash_util.search_terms_to_hash(terms);
assert.equal(hash, "#narrow/stream/987-Florida.2C-USA");
narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [{operator: "stream", operand: "Florida, USA", negated: false}]);
assert.deepEqual(narrow, [
{operator: "stream", operand: florida_id.toString(), negated: false},
]);
});
run_test("stream_to_channel_rename", () => {
@ -90,13 +102,15 @@ run_test("stream_to_channel_rename", () => {
// Confirm the URLs generated from search terms use "stream" and "streams"
// and that the new Filter has the new "channel" and "channels" operators.
terms = [{operator: "channel", operand: "devel"}];
terms = [{operator: "channel", operand: devel_id.toString()}];
hash = hash_util.search_terms_to_hash(terms);
assert.equal(hash, "#narrow/stream/devel");
assert.equal(hash, "#narrow/stream/100-devel");
narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [{operator: "stream", operand: "devel", negated: false}]);
assert.deepEqual(narrow, [{operator: "stream", operand: devel_id.toString(), negated: false}]);
filter = new Filter(narrow);
assert.deepEqual(filter.terms(), [{operator: "channel", operand: "devel", negated: false}]);
assert.deepEqual(filter.terms(), [
{operator: "channel", operand: devel_id.toString(), negated: false},
]);
terms = [{operator: "channels", operand: "public"}];
hash = hash_util.search_terms_to_hash(terms);
@ -108,23 +122,28 @@ run_test("stream_to_channel_rename", () => {
// Confirm that a narrow URL with "channel" and an enocoded stream/channel ID,
// will be decoded correctly.
const test_stream_id = 34;
const test_channel = {
name: "decode",
stream_id: 34,
stream_id: test_stream_id,
};
stream_data.add_sub(test_channel);
hash = "#narrow/channel/34-decode";
narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [{operator: "channel", operand: "decode", negated: false}]);
assert.deepEqual(narrow, [
{operator: "channel", operand: test_stream_id.toString(), negated: false},
]);
filter = new Filter(narrow);
assert.deepEqual(filter.terms(), [{operator: "channel", operand: "decode", negated: false}]);
assert.deepEqual(filter.terms(), [
{operator: "channel", operand: test_stream_id.toString(), negated: false},
]);
});
run_test("terms_trailing_slash", () => {
const hash = "#narrow/stream/devel/topic/algol/";
const hash = "#narrow/stream/100-devel/topic/algol/";
const narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [
{operator: "stream", operand: "devel", negated: false},
{operator: "stream", operand: devel_id.toString(), negated: false},
{operator: "topic", operand: "algol", negated: false},
]);
});
@ -270,6 +289,12 @@ run_test("hash_interactions", ({override, override_rewire}) => {
]);
assert.equal(window.location.hash, "#recent");
const denmark_id = 1;
stream_data.add_sub({
subscribed: true,
name: "Denmark",
stream_id: denmark_id,
});
window.location.hash = "#narrow/stream/Denmark";
helper.clear_events();
@ -280,7 +305,7 @@ run_test("hash_interactions", ({override, override_rewire}) => {
"message_view.show",
]);
let terms = helper.get_narrow_terms();
assert.equal(terms[0].operand, "Denmark");
assert.equal(terms[0].operand, denmark_id.toString());
window.location.hash = "#narrow";

View File

@ -242,7 +242,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
);
// for non-existent or private stream
set_filter([["stream", "Foo"]]);
set_filter([["stream", "999"]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
$(".empty_feed_notice_main").html(),
@ -250,8 +250,9 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
);
// for non-subbed public stream
stream_data.add_sub({name: "ROME", stream_id: 99});
set_filter([["stream", "Rome"]]);
const rome_id = 99;
stream_data.add_sub({name: "ROME", stream_id: rome_id});
set_filter([["stream", rome_id.toString()]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
$(".empty_feed_notice_main").html(),
@ -263,7 +264,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
// for non-web-public stream for spectator
page_params.is_spectator = true;
set_filter([["stream", "Rome"]]);
set_filter([["stream", rome_id.toString()]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
$(".empty_feed_notice_main").html(),
@ -274,7 +275,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
);
set_filter([
["stream", "Rome"],
["stream", rome_id.toString()],
["topic", "foo"],
]);
narrow_banner.show_empty_narrow_message();
@ -287,9 +288,10 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
);
// for web-public stream for spectator
stream_data.add_sub({name: "web-public-stream", stream_id: 1231, is_web_public: true});
const web_public_id = 1231;
stream_data.add_sub({name: "web-public-stream", stream_id: web_public_id, is_web_public: true});
set_filter([
["stream", "web-public-stream"],
["stream", web_public_id.toString()],
["topic", "foo"],
]);
narrow_banner.show_empty_narrow_message();
@ -524,7 +526,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
set_filter([
["sender", "alice@example.com"],
["stream", "Rome"],
["stream", rome_id.toString()],
]);
narrow_banner.show_empty_narrow_message();
assert.equal(
@ -542,14 +544,15 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
),
);
const my_stream_id = 103;
const my_stream = {
name: "my stream",
stream_id: 103,
stream_id: my_stream_id,
};
stream_data.add_sub(my_stream);
stream_data.subscribe_myself(my_stream);
set_filter([["stream", "my stream"]]);
set_filter([["stream", my_stream_id.toString()]]);
narrow_banner.show_empty_narrow_message();
assert.equal(
$(".empty_feed_notice_main").html(),
@ -617,6 +620,8 @@ run_test("show_search_stopwords", ({mock_template}) => {
empty_narrow_html("translated: No search results.", undefined, expected_search_data),
);
const streamA_id = 88;
stream_data.add_sub({name: "streamA", stream_id: streamA_id});
const expected_stream_search_data = {
has_stop_word: true,
stream_query: "streamA",
@ -627,7 +632,7 @@ run_test("show_search_stopwords", ({mock_template}) => {
],
};
set_filter([
["stream", "streamA"],
["stream", streamA_id.toString()],
["search", "what about grail"],
]);
narrow_banner.show_empty_narrow_message();
@ -647,7 +652,7 @@ run_test("show_search_stopwords", ({mock_template}) => {
],
};
set_filter([
["stream", "streamA"],
["stream", streamA_id.toString()],
["topic", "topicA"],
["search", "what about grail"],
]);
@ -666,12 +671,14 @@ run_test("show_invalid_narrow_message", ({mock_template}) => {
message_lists.set_current(undefined);
mock_template("empty_feed_notice.hbs", true, (_data, html) => html);
stream_data.add_sub({name: "streamA", stream_id: 88});
stream_data.add_sub({name: "streamB", stream_id: 77});
const streamA_id = 88;
const streamB_id = 77;
stream_data.add_sub({name: "streamA", stream_id: streamA_id});
stream_data.add_sub({name: "streamB", stream_id: streamB_id});
set_filter([
["stream", "streamA"],
["stream", "streamB"],
["stream", streamA_id.toString()],
["stream", streamB_id.toString()],
]);
narrow_banner.show_empty_narrow_message();
assert.equal(
@ -734,7 +741,8 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => {
});
compose_state.set_message_type("stream");
stream_data.add_sub({name: "ROME", stream_id: 99});
const rome_id = 99;
stream_data.add_sub({name: "ROME", stream_id: rome_id});
compose_state.set_stream_id(99);
// Test with existing topic
@ -744,7 +752,7 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => {
assert.equal(args.called, true);
assert.equal(args.opts.trigger, "narrow_to_compose_target");
assert.deepEqual(args.terms, [
{operator: "channel", operand: "ROME"},
{operator: "channel", operand: rome_id.toString()},
{operator: "topic", operand: "one"},
]);
@ -754,7 +762,7 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => {
message_view.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.terms, [
{operator: "channel", operand: "ROME"},
{operator: "channel", operand: rome_id.toString()},
{operator: "topic", operand: "four"},
]);
@ -763,14 +771,14 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => {
args.called = false;
message_view.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.terms, [{operator: "channel", operand: "ROME"}]);
assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]);
// Test with no topic
compose_state.topic(undefined);
args.called = false;
message_view.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.terms, [{operator: "channel", operand: "ROME"}]);
assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]);
});
run_test("narrow_to_compose_target direct messages", ({override, override_rewire}) => {
@ -851,26 +859,24 @@ run_test("narrow_compute_title", () => {
assert.equal(narrow_title.compute_narrow_title(filter), "translated: Messages sent by you");
// Stream narrows
const foo_stream_id = 43;
const sub = {
name: "Foo",
stream_id: 43,
stream_id: foo_stream_id,
};
stream_data.add_sub(sub);
filter = new Filter([
{operator: "stream", operand: "foo"},
{operator: "stream", operand: foo_stream_id.toString()},
{operator: "topic", operand: "bar"},
]);
assert.equal(narrow_title.compute_narrow_title(filter), "#Foo > bar");
filter = new Filter([{operator: "stream", operand: "foo"}]);
filter = new Filter([{operator: "stream", operand: foo_stream_id.toString()}]);
assert.equal(narrow_title.compute_narrow_title(filter), "#Foo");
filter = new Filter([{operator: "stream", operand: "Elephant"}]);
assert.equal(
narrow_title.compute_narrow_title(filter),
"translated: Unknown channel #Elephant",
);
assert.equal(narrow_title.compute_narrow_title(filter), "translated: Unknown channel");
// Direct messages with narrows
const joe = {

View File

@ -171,7 +171,7 @@ run_test("basics", ({override, override_rewire}) => {
override_rewire(message_view, "try_rendering_locally_for_same_narrow", noop);
const helper = test_helper({override});
const terms = [{operator: "stream", operand: "Denmark"}];
const terms = [{operator: "stream", operand: denmark.stream_id.toString()}];
const selected_id = 1000;

View File

@ -40,26 +40,27 @@ test("stream", () => {
assert.ok(!narrow_state.filter());
assert.equal(narrow_state.stream_id(), undefined);
const test_stream = {name: "Test", stream_id: 15};
const test_stream_id = 15;
const test_stream = {name: "Test", stream_id: test_stream_id};
stream_data.add_sub(test_stream);
assert.ok(!narrow_state.is_for_stream_id(test_stream.stream_id));
set_filter([
["stream", "Test"],
["stream", test_stream_id.toString()],
["topic", "Bar"],
["search", "yo"],
]);
assert.ok(narrow_state.filter());
assert.equal(narrow_state.stream_name(), "Test");
assert.equal(narrow_state.stream_id(), 15);
assert.equal(narrow_state.stream_id(), test_stream_id);
assert.equal(narrow_state.stream_sub().stream_id, test_stream.stream_id);
assert.equal(narrow_state.topic(), "Bar");
assert.ok(narrow_state.is_for_stream_id(test_stream.stream_id));
const expected_terms = [
{negated: false, operator: "channel", operand: "Test"},
{negated: false, operator: "channel", operand: test_stream_id.toString()},
{negated: false, operator: "topic", operand: "Bar"},
{negated: false, operator: "search", operand: "yo"},
];
@ -68,6 +69,8 @@ test("stream", () => {
assert.deepEqual(public_terms, expected_terms);
});
const foo_stream_id = 72;
const foo_stream = {name: "Foo", stream_id: foo_stream_id};
test("narrowed", () => {
assert.ok(!narrow_state.narrowed_to_pms());
assert.ok(!narrow_state.narrowed_by_reply());
@ -76,6 +79,8 @@ test("narrowed", () => {
assert.ok(!narrow_state.narrowed_by_stream_reply());
assert.equal(narrow_state.stream_sub(), undefined);
stream_data.add_sub(foo_stream);
set_filter([["stream", "Foo"]]);
assert.ok(!narrow_state.narrowed_to_pms());
assert.ok(!narrow_state.narrowed_by_reply());
@ -91,7 +96,7 @@ test("narrowed", () => {
assert.ok(!narrow_state.narrowed_by_stream_reply());
set_filter([
["stream", "Foo"],
["stream", foo_stream_id.toString()],
["topic", "bar"],
]);
assert.ok(!narrow_state.narrowed_to_pms());
@ -117,14 +122,14 @@ test("narrowed", () => {
test("terms", () => {
set_filter([
["stream", "Foo"],
["stream", foo_stream_id.toString()],
["topic", "Bar"],
["search", "Yo"],
]);
let result = narrow_state.search_terms();
assert.equal(result.length, 3);
assert.equal(result[0].operator, "channel");
assert.equal(result[0].operand, "Foo");
assert.equal(result[0].operand, foo_stream_id.toString());
assert.equal(result[1].operator, "topic");
assert.equal(result[1].operand, "Bar");
@ -136,11 +141,11 @@ test("terms", () => {
result = narrow_state.search_terms();
assert.equal(result.length, 0);
page_params.narrow = [{operator: "stream", operand: "Foo"}];
page_params.narrow = [{operator: "stream", operand: foo_stream_id.toString()}];
result = narrow_state.search_terms();
assert.equal(result.length, 1);
assert.equal(result[0].operator, "channel");
assert.equal(result[0].operand, "Foo");
assert.equal(result[0].operand, foo_stream_id.toString());
});
test("excludes_muted_topics", () => {
@ -169,7 +174,7 @@ test("excludes_muted_topics", () => {
test("set_compose_defaults", () => {
set_filter([
["stream", "Foo"],
["stream", foo_stream_id.toString()],
["topic", "Bar"],
]);
@ -178,10 +183,9 @@ test("set_compose_defaults", () => {
assert.equal(stream_and_topic.stream_id, undefined);
assert.equal(stream_and_topic.topic, "Bar");
const test_stream = {name: "Foo", stream_id: 72};
stream_data.add_sub(test_stream);
stream_data.add_sub(foo_stream);
stream_and_topic = narrow_state.set_compose_defaults();
assert.equal(stream_and_topic.stream_id, 72);
assert.equal(stream_and_topic.stream_id, foo_stream_id);
assert.equal(stream_and_topic.topic, "Bar");
set_filter([["dm", "foo@bar.com"]]);
@ -212,11 +216,12 @@ test("set_compose_defaults", () => {
]);
assert.deepEqual(narrow_state.set_compose_defaults(), {});
stream_data.add_sub({name: "ROME", stream_id: 99});
set_filter([["stream", "rome"]]);
const rome_id = 99;
stream_data.add_sub({name: "ROME", stream_id: rome_id});
set_filter([["stream", rome_id.toString()]]);
const stream_test = narrow_state.set_compose_defaults();
assert.equal(stream_test.stream_id, 99);
assert.equal(stream_test.stream_id, rome_id);
});
test("update_email", () => {
@ -241,7 +246,7 @@ test("update_email", () => {
test("topic", () => {
set_filter([
["stream", "Foo"],
["stream", foo_stream.stream_id.toString()],
["topic", "Bar"],
]);
assert.equal(narrow_state.topic(), "Bar");
@ -271,14 +276,15 @@ test("stream_sub", () => {
assert.equal(narrow_state.stream_sub(), undefined);
set_filter([
["stream", "Foo"],
["stream", "55"],
["topic", "Bar"],
]);
assert.equal(narrow_state.stream_name(), "Foo");
assert.equal(narrow_state.stream_name(), undefined);
assert.equal(narrow_state.stream_sub(), undefined);
const sub = {name: "Foo", stream_id: 55};
stream_data.add_sub(sub);
assert.equal(narrow_state.stream_name(), "Foo");
assert.deepEqual(narrow_state.stream_sub(), sub);
set_filter([
@ -297,7 +303,7 @@ test("pm_ids_string", () => {
assert.deepStrictEqual(narrow_state.pm_ids_set(), new Set());
set_filter([
["stream", "Foo"],
["stream", foo_stream.stream_id.toString()],
["topic", "Bar"],
]);
assert.equal(narrow_state.pm_ids_string(), undefined);

View File

@ -25,6 +25,8 @@ const alice = {
full_name: "Alice",
};
const bogus_stream_id = "999999";
people.init();
people.add_active_user(alice);
@ -111,12 +113,12 @@ run_test("get_unread_ids", () => {
assert.deepEqual(unread_ids, []);
assert_unread_info({flavor: "not_found"});
terms = [{operator: "stream", operand: "bogus"}];
terms = [{operator: "stream", operand: bogus_stream_id}];
set_filter(terms);
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, []);
terms = [{operator: "stream", operand: sub.name}];
terms = [{operator: "stream", operand: sub.stream_id.toString()}];
set_filter(terms);
unread_ids = candidate_ids();
assert.deepEqual(unread_ids, []);
@ -131,7 +133,7 @@ run_test("get_unread_ids", () => {
});
terms = [
{operator: "stream", operand: "bogus"},
{operator: "stream", operand: bogus_stream_id},
{operator: "topic", operand: "my topic"},
];
set_filter(terms);
@ -139,7 +141,7 @@ run_test("get_unread_ids", () => {
assert.deepEqual(unread_ids, []);
terms = [
{operator: "stream", operand: sub.name},
{operator: "stream", operand: sub.stream_id.toString()},
{operator: "topic", operand: "my topic"},
];
set_filter(terms);
@ -226,7 +228,7 @@ run_test("get_unread_ids", () => {
// destination topic.
unread.process_loaded_messages([other_topic_message]);
terms = [
{operator: "channel", operand: sub.name},
{operator: "channel", operand: sub.stream_id.toString()},
{operator: "topic", operand: "another topic"},
];
set_filter(terms);
@ -234,7 +236,7 @@ run_test("get_unread_ids", () => {
assert.deepEqual(unread_ids, [other_topic_message.id]);
terms = [
{operator: "channel", operand: sub.name},
{operator: "channel", operand: sub.stream_id.toString()},
{operator: "topic", operand: "another topic"},
{operator: "with", operand: stream_msg.id},
];
@ -243,7 +245,7 @@ run_test("get_unread_ids", () => {
assert.deepEqual(unread_ids, [stream_msg.id]);
terms = [
{operator: "channel", operand: sub.name},
{operator: "channel", operand: sub.stream_id.toString()},
{operator: "topic", operand: "another topic"},
{operator: "with", operand: private_msg.id},
];

View File

@ -66,19 +66,19 @@ test("unsubscribe", () => {
stream_data.add_sub(devel);
// verify clean slate
assert.ok(!stream_data.is_subscribed_by_name("devel"));
assert.ok(!stream_data.is_subscribed(devel.stream_id));
// set up our subscription
devel.subscribed = true;
peer_data.set_subscribers(devel.stream_id, [me.user_id]);
// ensure our setup is accurate
assert.ok(stream_data.is_subscribed_by_name("devel"));
assert.ok(stream_data.is_subscribed(devel.stream_id));
// DO THE UNSUBSCRIBE HERE
stream_data.unsubscribe_myself(devel);
assert.ok(!devel.subscribed);
assert.ok(!stream_data.is_subscribed_by_name("devel"));
assert.ok(!stream_data.is_subscribed(devel.stream_id));
assert.ok(!contains_sub(stream_data.subscribed_subs(), devel));
assert.ok(contains_sub(stream_data.unsubscribed_subs(), devel));
@ -96,7 +96,7 @@ test("subscribers", () => {
people.add_active_user(george);
// verify setup
assert.ok(stream_data.is_subscribed_by_name(sub.name));
assert.ok(stream_data.is_subscribed(sub.stream_id));
const stream_id = sub.stream_id;

View File

@ -237,25 +237,26 @@ run_test("initialize", ({override, override_rewire, mock_template}) => {
assert.equal(opts.updater("ver"), "ver");
assert.ok(!input_pill_displayed);
const verona_stream_id = verona.stream_id.toString();
terms = [
{
negated: false,
operator: "channel",
operand: "Verona",
operand: verona_stream_id,
},
];
expected_pill_display_value = "channel: Verona";
_setup(terms);
input_pill_displayed = false;
mock_pill_removes(search.search_pill_widget);
assert.equal(opts.updater("channel:Verona"), "");
assert.equal(opts.updater(`channel:${verona_stream_id}`), "");
assert.ok(input_pill_displayed);
search.__Rewire__("is_using_input_method", true);
_setup(terms);
input_pill_displayed = false;
mock_pill_removes(search.search_pill_widget);
assert.equal(opts.updater("channel:Verona"), "");
assert.equal(opts.updater(`channel:${verona_stream_id}`), "");
assert.ok(input_pill_displayed);
}
return {

View File

@ -50,6 +50,12 @@ const jeff = {
const example_avatar_url = "http://example.com/example.png";
let _stream_id = 0;
function new_stream_id() {
_stream_id += 1;
return _stream_id;
}
function init() {
current_user.is_admin = true;
@ -81,7 +87,7 @@ function test(label, f) {
test("basic_get_suggestions", ({override}) => {
const query = "fred";
override(narrow_state, "stream_name", () => "office");
override(narrow_state, "stream_id", noop);
const suggestions = get_suggestions(query);
@ -116,14 +122,17 @@ test("get_is_suggestions_for_spectator", () => {
test("subset_suggestions", ({mock_template}) => {
mock_template("search_description.hbs", true, (_data, html) => html);
const query = "channel:Denmark topic:Hamlet shakespeare";
const denmark_id = new_stream_id();
const sub = {name: "Denmark", stream_id: denmark_id};
stream_data.add_sub(sub);
const query = `channel:${denmark_id} topic:Hamlet shakespeare`;
const suggestions = get_suggestions(query);
const expected = [
"channel:Denmark topic:Hamlet shakespeare",
"channel:Denmark topic:Hamlet",
"channel:Denmark",
`channel:${denmark_id} topic:Hamlet shakespeare`,
`channel:${denmark_id} topic:Hamlet`,
`channel:${denmark_id}`,
];
assert.deepEqual(suggestions.strings, expected);
@ -249,7 +258,7 @@ test("dm_suggestions", ({override, mock_template}) => {
// "pm-with" operator returns search result
// and "dm" operator as a suggestions
override(narrow_state, "stream_name", () => undefined);
override(narrow_state, "stream_id", () => undefined);
query = "pm-with";
suggestions = get_suggestions(query);
expected = ["pm-with", "dm:"];
@ -330,10 +339,10 @@ test("group_suggestions", ({mock_template}) => {
assert.deepEqual(suggestions.strings, expected);
// Doesn't show dms because it's invalid in combination
// with a channel.
query = "channel:Denmark has:link dm:bob@zulip.com,Smit";
// with a channel. (Random channel id.)
query = "channel:66 has:link dm:bob@zulip.com,Smit";
suggestions = get_suggestions(query);
expected = ["channel:Denmark has:link", "channel:Denmark"];
expected = ["channel:66 has:link", "channel:66"];
assert.deepEqual(suggestions.strings, expected);
// Invalid emails don't give suggestions
@ -346,8 +355,10 @@ test("group_suggestions", ({mock_template}) => {
test("empty_query_suggestions", () => {
const query = "";
stream_data.add_sub({stream_id: 44, name: "devel", subscribed: true});
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
const devel_id = new_stream_id();
const office_id = new_stream_id();
stream_data.add_sub({stream_id: devel_id, name: "devel", subscribed: true});
stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true});
const suggestions = get_suggestions(query);
@ -361,8 +372,8 @@ test("empty_query_suggestions", () => {
"is:unread",
"is:resolved",
"sender:myself@zulip.com",
"channel:devel",
"channel:office",
`channel:${devel_id}`,
`channel:${office_id}`,
"has:link",
"has:image",
"has:attachment",
@ -395,7 +406,7 @@ test("has_suggestions", ({override, mock_template}) => {
let query = "h";
stream_data.add_sub({stream_id: 44, name: "devel", subscribed: true});
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
override(narrow_state, "stream_name", noop);
override(narrow_state, "stream_id", noop);
let suggestions = get_suggestions(query);
let expected = ["h", "has:link", "has:image", "has:attachment", "has:reaction"];
@ -439,13 +450,10 @@ test("has_suggestions", ({override, mock_template}) => {
expected = ["att", "has:attachment"];
assert.deepEqual(suggestions.strings, expected);
query = "channel:Denmark is:alerted has:lin";
// 66 is misc channel id.
query = "channel:66 is:alerted has:lin";
suggestions = get_suggestions(query);
expected = [
"channel:Denmark is:alerted has:link",
"channel:Denmark is:alerted",
"channel:Denmark",
];
expected = ["channel:66 is:alerted has:link", "channel:66 is:alerted", "channel:66"];
assert.deepEqual(suggestions.strings, expected);
});
@ -453,9 +461,7 @@ test("check_is_suggestions", ({override, mock_template}) => {
mock_template("search_description.hbs", true, (_data, html) => html);
mock_template("user_pill.hbs", true, (_data, html) => html);
stream_data.add_sub({stream_id: 44, name: "devel", subscribed: true});
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
override(narrow_state, "stream_name", noop);
override(narrow_state, "stream_id", noop);
let query = "i";
let suggestions = get_suggestions(query);
@ -541,20 +547,16 @@ test("check_is_suggestions", ({override, mock_template}) => {
expected = ["st", "streams:public", "is:starred", "channel:"];
assert.deepEqual(suggestions.strings, expected);
query = "channel:Denmark has:link is:sta";
query = "channel:66 has:link is:sta";
suggestions = get_suggestions(query);
expected = [
"channel:Denmark has:link is:starred",
"channel:Denmark has:link",
"channel:Denmark",
];
expected = ["channel:66 has:link is:starred", "channel:66 has:link", "channel:66"];
assert.deepEqual(suggestions.strings, expected);
});
test("sent_by_me_suggestions", ({override, mock_template}) => {
mock_template("search_description.hbs", true, (_data, html) => html);
override(narrow_state, "stream_name", noop);
override(narrow_state, "stream_id", noop);
let query = "";
let suggestions = get_suggestions(query);
@ -604,13 +606,16 @@ test("sent_by_me_suggestions", ({override, mock_template}) => {
expected = ["-sent", "-sender:myself@zulip.com"];
assert.deepEqual(suggestions.strings, expected);
query = "channel:Denmark topic:Denmark1 sent";
const denmark_id = new_stream_id();
const sub = {name: "Denmark", stream_id: denmark_id};
stream_data.add_sub(sub);
query = `channel:${denmark_id} topic:Denmark1 sent`;
suggestions = get_suggestions(query);
expected = [
"channel:Denmark topic:Denmark1 sent",
"channel:Denmark topic:Denmark1 sender:myself@zulip.com",
"channel:Denmark topic:Denmark1",
"channel:Denmark",
`channel:${denmark_id} topic:Denmark1 sent`,
`channel:${denmark_id} topic:Denmark1 sender:myself@zulip.com`,
`channel:${denmark_id} topic:Denmark1`,
`channel:${denmark_id}`,
];
assert.deepEqual(suggestions.strings, expected);
@ -627,11 +632,11 @@ test("topic_suggestions", ({override, mock_template}) => {
let expected;
override(stream_topic_history_util, "get_server_history", noop);
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
override(narrow_state, "stream_name", () => "office");
const office_id = new_stream_id();
stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true});
override(narrow_state, "stream_id", () => office_id);
const devel_id = 44;
const office_id = 77;
const devel_id = new_stream_id();
stream_data.add_sub({stream_id: devel_id, name: "devel", subscribed: true});
stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true});
@ -657,8 +662,8 @@ test("topic_suggestions", ({override, mock_template}) => {
"dm:ted@zulip.com",
"sender:ted@zulip.com",
"dm-including:ted@zulip.com",
"channel:office topic:team",
"channel:office topic:test",
`channel:${office_id} topic:team`,
`channel:${office_id} topic:test`,
];
assert.deepEqual(suggestions.strings, expected);
@ -666,40 +671,56 @@ test("topic_suggestions", ({override, mock_template}) => {
return suggestions.lookup_table.get(q).description_html;
}
assert.equal(describe("te"), "Search for <strong>te</strong>");
assert.equal(describe("channel:office topic:team"), "Channel office > team");
assert.equal(describe(`channel:${office_id} topic:team`), "Channel office > team");
suggestions = get_suggestions("topic:staplers channel:office");
expected = ["topic:staplers channel:office", "topic:staplers"];
suggestions = get_suggestions(`topic:staplers channel:${office_id}`);
expected = [`topic:staplers channel:${office_id}`, "topic:staplers"];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("channel:devel topic:");
expected = ["channel:devel topic:", "channel:devel topic:REXX", "channel:devel"];
suggestions = get_suggestions(`channel:${devel_id} topic:`);
expected = [
`channel:${devel_id} topic:`,
`channel:${devel_id} topic:REXX`,
`channel:${devel_id}`,
];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("channel:devel -topic:");
expected = ["channel:devel -topic:", "channel:devel -topic:REXX", "channel:devel"];
suggestions = get_suggestions(`channel:${devel_id} -topic:`);
expected = [
`channel:${devel_id} -topic:`,
`channel:${devel_id} -topic:REXX`,
`channel:${devel_id}`,
];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("-topic:te");
expected = ["-topic:te", "channel:office -topic:team", "channel:office -topic:test"];
expected = [
"-topic:te",
`channel:${office_id} -topic:team`,
`channel:${office_id} -topic:test`,
];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("is:alerted channel:devel is:starred topic:");
suggestions = get_suggestions(`is:alerted channel:${devel_id} is:starred topic:`);
expected = [
"is:alerted channel:devel is:starred topic:",
"is:alerted channel:devel is:starred topic:REXX",
"is:alerted channel:devel is:starred",
"is:alerted channel:devel",
`is:alerted channel:${devel_id} is:starred topic:`,
`is:alerted channel:${devel_id} is:starred topic:REXX`,
`is:alerted channel:${devel_id} is:starred`,
`is:alerted channel:${devel_id}`,
"is:alerted",
];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("is:dm channel:devel topic:");
expected = ["is:dm channel:devel topic:", "is:dm channel:devel", "is:dm"];
suggestions = get_suggestions(`is:dm channel:${devel_id} topic:`);
expected = [`is:dm channel:${devel_id} topic:`, `is:dm channel:${devel_id}`, `is:dm`];
assert.deepEqual(suggestions.strings, expected);
suggestions = get_suggestions("topic:REXX channel:devel topic:");
expected = ["topic:REXX channel:devel topic:", "topic:REXX channel:devel", "topic:REXX"];
suggestions = get_suggestions(`topic:REXX channel:${devel_id} topic:`);
expected = [
`topic:REXX channel:${devel_id} topic:`,
`topic:REXX channel:${devel_id}`,
"topic:REXX",
];
assert.deepEqual(suggestions.strings, expected);
});
@ -748,38 +769,41 @@ test("topic_suggestions (limits)", () => {
test("whitespace_glitch", ({override, mock_template}) => {
mock_template("search_description.hbs", true, (_data, html) => html);
const office_stream_id = new_stream_id();
const query = "channel:office "; // note trailing space
override(stream_topic_history_util, "get_server_history", noop);
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
stream_data.add_sub({stream_id: office_stream_id, name: "office", subscribed: true});
const suggestions = get_suggestions(query);
const expected = ["channel:office"];
const expected = [`channel:${office_stream_id}`];
assert.deepEqual(suggestions.strings, expected);
});
test("channel_completion", ({override}) => {
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
stream_data.add_sub({stream_id: 88, name: "dev help", subscribed: true});
const office_stream_id = new_stream_id();
stream_data.add_sub({stream_id: office_stream_id, name: "office", subscribed: true});
const dev_help_stream_id = new_stream_id();
stream_data.add_sub({stream_id: dev_help_stream_id, name: "dev help", subscribed: true});
override(narrow_state, "stream_name", noop);
override(narrow_state, "stream_id", noop);
let query = "channel:of";
let suggestions = get_suggestions(query);
let expected = ["channel:office"];
let expected = [`channel:${office_stream_id}`];
assert.deepEqual(suggestions.strings, expected);
query = "-channel:of";
suggestions = get_suggestions(query);
expected = ["-channel:office"];
expected = [`-channel:${office_stream_id}`];
assert.deepEqual(suggestions.strings, expected);
query = "hel";
suggestions = get_suggestions(query);
expected = ["hel", "channel:dev+help"];
expected = ["hel", `channel:${dev_help_stream_id}`];
assert.deepEqual(suggestions.strings, expected);
});
@ -789,7 +813,7 @@ test("people_suggestions", ({override, mock_template}) => {
let query = "te";
override(narrow_state, "stream_name", noop);
override(narrow_state, "stream_id", noop);
const ted = {
email: "ted@zulip.com",
@ -959,7 +983,7 @@ test("people_suggestions", ({override, mock_template}) => {
test("operator_suggestions", ({override, mock_template}) => {
mock_template("search_description.hbs", true, (_data, html) => html);
override(narrow_state, "stream_name", () => undefined);
override(narrow_state, "stream_id", () => undefined);
// Completed operator should return nothing
let query = "channel:";
@ -977,37 +1001,40 @@ test("operator_suggestions", ({override, mock_template}) => {
expected = ["-s", "-sender:myself@zulip.com", "-sender:", "-channel:"];
assert.deepEqual(suggestions.strings, expected);
query = "channel:Denmark is:alerted -f";
// 66 is a misc channel id.
query = "channel:66 is:alerted -f";
suggestions = get_suggestions(query);
expected = [
"channel:Denmark is:alerted -f",
"channel:Denmark is:alerted -sender:myself@zulip.com",
"channel:Denmark is:alerted -sender:",
"channel:Denmark is:alerted",
"channel:Denmark",
"channel:66 is:alerted -f",
"channel:66 is:alerted -sender:myself@zulip.com",
"channel:66 is:alerted -sender:",
"channel:66 is:alerted",
"channel:66",
];
assert.deepEqual(suggestions.strings, expected);
});
test("queries_with_spaces", () => {
stream_data.add_sub({stream_id: 77, name: "office", subscribed: true});
stream_data.add_sub({stream_id: 88, name: "dev help", subscribed: true});
const office_id = new_stream_id();
stream_data.add_sub({stream_id: office_id, name: "office", subscribed: true});
const dev_help_id = new_stream_id();
stream_data.add_sub({stream_id: dev_help_id, name: "dev help", subscribed: true});
// test allowing spaces with quotes surrounding operand
let query = 'channel:"dev he"';
let suggestions = get_suggestions(query);
let expected = ["channel:dev+help"];
let expected = [`channel:${dev_help_id}`];
assert.deepEqual(suggestions.strings, expected);
// test mismatched quote
query = 'channel:"dev h';
suggestions = get_suggestions(query);
expected = ["channel:dev+help"];
expected = [`channel:${dev_help_id}`];
assert.deepEqual(suggestions.strings, expected);
// test extra space after operator still works
query = "channel: offi";
suggestions = get_suggestions(query);
expected = ["channel:office"];
expected = [`channel:${office_id}`];
assert.deepEqual(suggestions.strings, expected);
});

View File

@ -17,6 +17,7 @@ const people = zrequire("people");
const settings_config = zrequire("settings_config");
const sub_store = zrequire("sub_store");
const stream_data = zrequire("stream_data");
const hash_util = zrequire("hash_util");
const stream_settings_data = zrequire("stream_settings_data");
const user_groups = zrequire("user_groups");
@ -135,10 +136,8 @@ test("basics", () => {
assert.deepEqual(stream_data.get_colors(), ["red", "yellow"]);
assert.deepEqual(stream_data.subscribed_stream_ids(), [social.stream_id, test.stream_id]);
assert.ok(stream_data.is_subscribed_by_name("social"));
assert.ok(stream_data.is_subscribed_by_name("Social"));
assert.ok(!stream_data.is_subscribed_by_name("Denmark"));
assert.ok(!stream_data.is_subscribed_by_name("Rome"));
assert.ok(stream_data.is_subscribed(social.stream_id));
assert.ok(!stream_data.is_subscribed(denmark.stream_id));
assert.equal(stream_data.get_stream_privacy_policy(test.stream_id), "public");
assert.equal(stream_data.get_stream_privacy_policy(social.stream_id), "invite-only");
@ -147,9 +146,10 @@ test("basics", () => {
"invite-only-public-history",
);
assert.equal(stream_data.get_stream_privacy_policy(web_public_stream.stream_id), "web-public");
assert.ok(stream_data.is_web_public_by_stream_name(web_public_stream.name));
assert.ok(!stream_data.is_web_public_by_stream_name(social.name));
assert.ok(!stream_data.is_web_public_by_stream_name("unknown"));
assert.ok(stream_data.is_web_public_by_stream_id(web_public_stream.stream_id));
assert.ok(!stream_data.is_web_public_by_stream_id(social.stream_id));
const unknown_stream_id = 9999;
assert.ok(!stream_data.is_web_public_by_stream_id(unknown_stream_id));
assert.ok(stream_data.is_invite_only_by_stream_id(social.stream_id));
// Unknown stream id
@ -159,9 +159,6 @@ test("basics", () => {
assert.equal(stream_data.get_color(undefined), "#c2c2c2");
assert.equal(stream_data.get_color(1234567), "#c2c2c2");
assert.equal(stream_data.get_name("denMARK"), "Denmark");
assert.equal(stream_data.get_name("unknown Stream"), "unknown Stream");
assert.ok(!stream_data.is_muted(social.stream_id));
assert.ok(stream_data.is_muted(denmark.stream_id));
@ -176,19 +173,37 @@ test("basics", () => {
// "new" correct url formats
assert.equal(stream_data.slug_to_stream_id("2-social"), 2);
assert.equal(hash_util.decode_operand("channel", "2-social"), "2");
assert.equal(stream_data.slug_to_stream_id("2"), 2);
assert.equal(hash_util.decode_operand("channel", "2"), "2");
// we still get 2 because it's a valid stream id
assert.equal(stream_data.slug_to_stream_id("2-whatever"), 2);
// invalid stream id
assert.equal(stream_data.slug_to_stream_id("999-social"), undefined);
// legacy
assert.equal(stream_data.slug_to_stream_id("social"), 2);
assert.equal(stream_data.slug_to_stream_id("2-"), 2);
// legacy, we recognize "social" as a valid channel name
assert.equal(stream_data.slug_to_stream_id("social"), 2);
assert.equal(hash_util.decode_operand("channel", "social"), "2");
// These aren't prepended with valid ids nor valid channel names. We
// don't get any stream id from the slug, and the decoded operand (the
// only caller of `slug_to_stream_id`) returns an empty string (which we
// don't display anywhere, since the channel is invalid).
assert.equal(stream_data.slug_to_stream_id("999-social"), undefined);
assert.equal(hash_util.decode_operand("channel", "999-social"), "");
// invalid formats
assert.equal(stream_data.slug_to_stream_id("25-or-6-to-4"), undefined);
assert.equal(stream_data.slug_to_stream_id("2something"), undefined);
assert.equal(stream_data.slug_to_stream_id("99-whatever"), undefined);
assert.equal(hash_util.decode_operand("channel", "99-whatever"), "");
assert.equal(stream_data.slug_to_stream_id("25-or-6-to-4"), undefined);
assert.equal(hash_util.decode_operand("channel", "25-or-6-to-4"), "");
assert.equal(stream_data.slug_to_stream_id("2something"), undefined);
assert.equal(hash_util.decode_operand("channel", "2something"), "");
assert.equal(stream_data.slug_to_stream_id("99whatever"), undefined);
assert.equal(hash_util.decode_operand("channel", "99whatever"), "");
// sub_store
assert.equal(sub_store.get(-3), undefined);
@ -528,12 +543,12 @@ test("delete_sub", () => {
stream_data.add_sub(canada);
assert.ok(stream_data.is_subscribed_by_name("Canada"));
assert.ok(stream_data.is_subscribed(canada.stream_id));
assert.equal(stream_data.get_sub("Canada").stream_id, canada.stream_id);
assert.equal(sub_store.get(canada.stream_id).name, "Canada");
stream_data.delete_sub(canada.stream_id);
assert.ok(!stream_data.is_subscribed_by_name("Canada"));
assert.ok(!stream_data.is_subscribed(canada.stream_id));
assert.ok(!stream_data.get_sub("Canada"));
assert.ok(!sub_store.get(canada.stream_id));

View File

@ -83,7 +83,7 @@ const frontend = {
};
function narrow_to_frontend() {
const filter = new Filter([{operator: "stream", operand: "frontend"}]);
const filter = new Filter([{operator: "stream", operand: frontend.stream_id.toString()}]);
message_lists.current = {
data: {
filter,
@ -382,12 +382,12 @@ test("marked_subscribed (emails)", ({override}) => {
$("#channels_overlay_container .stream-row:not(.notdisplayed)").length = 0;
assert.ok(!stream_data.is_subscribed_by_name(sub.name));
assert.ok(!stream_data.is_subscribed(sub.stream_id));
const user_ids = [15, 20, 25, me.user_id];
stream_events.mark_subscribed(sub, user_ids, "");
assert.deepEqual(new Set(peer_data.get_subscribers(sub.stream_id)), new Set(user_ids));
assert.ok(stream_data.is_subscribed_by_name(sub.name));
assert.ok(stream_data.is_subscribed(sub.stream_id));
const args = subs_stub.get_args("sub");
assert.deepEqual(sub, args.sub);

View File

@ -429,19 +429,19 @@ test_ui("narrowing", ({mock_template}) => {
let filter;
filter = new Filter([{operator: "stream", operand: "devel"}]);
filter = new Filter([{operator: "stream", operand: develSub.stream_id.toString()}]);
stream_list.handle_narrow_activated(filter);
assert.ok($("<devel-sidebar-row-stub>").hasClass("active-filter"));
filter = new Filter([
{operator: "stream", operand: "cars"},
{operator: "stream", operand: carSub.stream_id.toString()},
{operator: "topic", operand: "sedans"},
]);
stream_list.handle_narrow_activated(filter);
assert.ok(!$("ul.filters li").hasClass("active-filter"));
assert.ok(!$("<cars-sidebar-row-stub>").hasClass("active-filter")); // false because of topic
filter = new Filter([{operator: "stream", operand: "cars"}]);
filter = new Filter([{operator: "stream", operand: carSub.stream_id.toString()}]);
stream_list.handle_narrow_activated(filter);
assert.ok(!$("ul.filters li").hasClass("active-filter"));
assert.ok($("<cars-sidebar-row-stub>").hasClass("active-filter"));