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. // Renarrow to the unresolved topic if currently viewing the resolved topic.
const current_filter = narrow_state.filter(); 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 ( if (
current_filter && current_filter &&
(current_filter.is_conversation_view() || (current_filter.is_conversation_view() ||
current_filter.is_conversation_view_with_near()) && 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( message_view.show(
[ [
{operator: "channel", operand: channel_name}, {operator: "channel", operand: stream_id_string},
{operator: "topic", operand: new_topic}, {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 hash_util from "./hash_util";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import * as rows from "./rows"; import * as rows from "./rows";
import * as stream_data from "./stream_data";
import * as topic_link_util from "./topic_link_util"; import * as topic_link_util from "./topic_link_util";
import * as util from "./util"; import * as util from "./util";
@@ -662,7 +663,10 @@ export function try_stream_topic_syntax_text(text: string): string | null {
return 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; return null;
} }
@@ -673,7 +677,7 @@ export function try_stream_topic_syntax_text(text: string): string | null {
return null; return null;
} }
let syntax_text = "#**" + stream_topic.stream_name; let syntax_text = "#**" + stream_name;
if (stream_topic.topic_name) { if (stream_topic.topic_name) {
syntax_text += ">" + 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 overlays from "./overlays";
import * as people from "./people"; import * as people from "./people";
import * as rendered_markdown from "./rendered_markdown"; 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_card_popover from "./user_card_popover";
import * as user_group_popover from "./user_group_popover"; import * as user_group_popover from "./user_group_popover";
@@ -30,7 +29,7 @@ function restore_draft(draft_id) {
[ [
{ {
operator: "channel", 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}, {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 {current_user, realm} from "./state_data";
import type {NarrowTerm} from "./state_data"; import type {NarrowTerm} from "./state_data";
import * as stream_data from "./stream_data"; import * as stream_data from "./stream_data";
import type {StreamSubscription} from "./sub_store";
import * as unread from "./unread"; import * as unread from "./unread";
import * as user_topics from "./user_topics"; import * as user_topics from "./user_topics";
import * as util from "./util"; import * as util from "./util";
@@ -75,12 +74,15 @@ type ValidOrInvalidUser =
const CHANNEL_SYNONYM = "stream"; const CHANNEL_SYNONYM = "stream";
const CHANNELS_SYNONYM = "streams"; 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)*$/ // Zephyr users expect narrowing to "social" to also show messages to /^(un)*social(.d)*$/
// (unsocial, ununsocial, social.d, etc) // (unsocial, ununsocial, social.d, etc)
// TODO: hoist the regex compiling out of the closure // TODO: hoist the regex compiling out of the closure
const m = /^(?:un)*(.+?)(?:\.d)*$/i.exec(operand); const m = /^(?:un)*(.+?)(?:\.d)*$/i.exec(stream_name);
let base_stream_name = operand; let base_stream_name = stream_name;
if (m?.[1] !== undefined) { if (m?.[1] !== undefined) {
base_stream_name = m[1]; 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, /^(un)*/.source + _.escapeRegExp(base_stream_name) + /(\.d)*$/.source,
"i", "i",
); );
const stream_name = stream_data.get_stream_name_from_id(message.stream_id); const message_stream_name = stream_data.get_stream_name_from_id(message.stream_id);
return related_regexp.test(stream_name); return related_regexp.test(message_stream_name);
} }
function zephyr_topic_name_match(message: Message & {type: "stream"}, operand: string): boolean { 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; return false;
} }
operand = operand.toLowerCase();
if (realm.realm_is_zephyr_mirror_realm) { 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 return message.stream_id.toString() === operand;
// 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;
} }
case "topic": case "topic":
@@ -291,7 +290,6 @@ const USER_OPERATORS = new Set([
export class Filter { export class Filter {
_terms: NarrowTerm[]; _terms: NarrowTerm[];
_sub?: StreamSubscription | undefined;
_sorted_term_types?: string[] = undefined; _sorted_term_types?: string[] = undefined;
_predicate?: (message: Message) => boolean; _predicate?: (message: Message) => boolean;
_can_mark_messages_read?: boolean; _can_mark_messages_read?: boolean;
@@ -355,7 +353,6 @@ export class Filter {
break; break;
case "channel": case "channel":
operand = stream_data.get_name(operand);
break; break;
case "topic": case "topic":
break; break;
@@ -495,6 +492,18 @@ export class Filter {
} }
operand = Filter.decodeOperand(parts.join(":"), operator); 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 // We use Filter.operator_to_prefix() to check if the
// operator is known. If it is not known, then we treat // operator is known. If it is not known, then we treat
// it as a search for the given string (which may contain // it as a search for the given string (which may contain
@@ -545,7 +554,7 @@ export class Filter {
return Number.isInteger(Number(term.operand)); return Number.isInteger(Number(term.operand));
case "channel": case "channel":
case "stream": case "stream":
return stream_data.get_sub(term.operand) !== undefined; return stream_data.get_sub_by_id_string(term.operand) !== undefined;
case "channels": case "channels":
case "streams": case "streams":
return term.operand === "public"; return term.operand === "public";
@@ -715,7 +724,7 @@ export class Filter {
Filter.canonicalize_operator(term.operator) === expected && !term.negated; Filter.canonicalize_operator(term.operator) === expected && !term.negated;
if (is(terms[0], "channel") && is(terms[1], "topic")) { 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; const topic = terms[1].operand;
parts.push({ parts.push({
type: "channel_topic", type: "channel_topic",
@@ -783,6 +792,18 @@ export class Filter {
}; };
} }
if (prefix_for_operator !== "") { 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 { return {
type: "prefix_for_operator", type: "prefix_for_operator",
prefix_for_operator, prefix_for_operator,
@@ -863,12 +884,11 @@ export class Filter {
const adjusted_term = {...term}; const adjusted_term = {...term};
if ( if (
Filter.canonicalize_operator(term.operator) === "channel" && 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; terms_changed = true;
} }
if ( if (
Filter.canonicalize_operator(term.operator) === "topic" && Filter.canonicalize_operator(term.operator) === "topic" &&
!util.lower_same(term.operand, message.topic) !util.lower_same(term.operand, message.topic)
@@ -890,11 +910,6 @@ export class Filter {
setup_filter(terms: NarrowTerm[]): void { setup_filter(terms: NarrowTerm[]): void {
this._terms = this.fix_terms(terms); this._terms = this.fix_terms(terms);
this.cached_sorted_terms_for_comparison = undefined; 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 { equals(filter: Filter, excluded_operators?: string[]): boolean {
@@ -948,12 +963,15 @@ export class Filter {
public_terms(): NarrowTerm[] { public_terms(): NarrowTerm[] {
const safe_to_return = this._terms.filter( const safe_to_return = this._terms.filter(
// Filter out the embedded narrow (if any). // Filter out the embedded narrow (if any).
(term) => (term) => {
!( // TODO(stream_id): Ideally we have `page_params.narrow_stream_id`
page_params.narrow_stream !== undefined && if (page_params.narrow_stream === undefined || term.operator !== "channel") {
term.operator === "channel" && return true;
term.operand.toLowerCase() === page_params.narrow_stream.toLowerCase() }
), 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; return safe_to_return;
} }
@@ -1170,15 +1188,16 @@ export class Filter {
return "/#narrow/has/reaction/sender/me"; return "/#narrow/has/reaction/sender/me";
} }
if (_.isEqual(term_types, ["channel", "topic", "search"])) { 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 channel does not exist, redirect to home view
if (!this._sub) { if (!sub) {
return "#"; return "#";
} }
return ( return (
"/#narrow/" + "/#narrow/" +
CHANNEL_SYNONYM + CHANNEL_SYNONYM +
"/" + "/" +
stream_data.name_to_slug(this.operands("channel")[0]!) + stream_data.id_to_slug(sub.stream_id) +
"/topic/" + "/topic/" +
this.operands("topic")[0] this.operands("topic")[0]
); );
@@ -1191,17 +1210,16 @@ export class Filter {
if (term_types[1] === "search") { if (term_types[1] === "search") {
switch (term_types[0]) { 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 channel does not exist, redirect to home view
if (!this._sub) { if (!sub) {
return "#"; return "#";
} }
return ( return (
"/#narrow/" + "/#narrow/" + CHANNEL_SYNONYM + "/" + stream_data.id_to_slug(sub.stream_id)
CHANNEL_SYNONYM +
"/" +
stream_data.name_to_slug(this.operands("channel")[0]!)
); );
}
case "is-dm": case "is-dm":
return "/#narrow/is/dm"; return "/#narrow/is/dm";
case "is-starred": case "is-starred":
@@ -1250,21 +1268,23 @@ export class Filter {
case "in-all": case "in-all":
icon = "home"; icon = "home";
break; break;
case "channel": case "channel": {
if (!this._sub) { const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!);
if (!sub) {
icon = "question-circle-o"; icon = "question-circle-o";
break; break;
} }
if (this._sub.invite_only) { if (sub.invite_only) {
zulip_icon = "lock"; zulip_icon = "lock";
break; break;
} }
if (this._sub.is_web_public) { if (sub.is_web_public) {
zulip_icon = "globe"; zulip_icon = "globe";
break; break;
} }
zulip_icon = "hashtag"; zulip_icon = "hashtag";
break; break;
}
case "is-dm": case "is-dm":
zulip_icon = "user"; zulip_icon = "user";
break; break;
@@ -1302,11 +1322,11 @@ export class Filter {
(term_types.length === 2 && _.isEqual(term_types, ["channel", "topic"])) || (term_types.length === 2 && _.isEqual(term_types, ["channel", "topic"])) ||
(term_types.length === 1 && _.isEqual(term_types, ["channel"])) (term_types.length === 1 && _.isEqual(term_types, ["channel"]))
) { ) {
if (!this._sub) { const sub = stream_data.get_sub_by_id_string(this.operands("channel")[0]!);
const search_text = this.operands("channel")[0]; if (!sub) {
return $t({defaultMessage: "Unknown channel #{search_text}"}, {search_text}); return $t({defaultMessage: "Unknown channel"});
} }
return this._sub.name; return sub.name;
} }
if ( if (
(term_types.length === 2 && _.isEqual(term_types, ["dm", "near"])) || (term_types.length === 2 && _.isEqual(term_types, ["dm", "near"])) ||
@@ -1512,8 +1532,10 @@ export class Filter {
return new Filter(terms); return new Filter(terms);
} }
has_topic(stream_name: string, topic: string): boolean { has_topic(stream_id: number, topic: string): boolean {
return this.has_operand("channel", stream_name) && this.has_operand("topic", topic); return (
this.has_operand("channel", stream_id.toString()) && this.has_operand("topic", topic)
);
} }
sorted_term_types(): string[] { sorted_term_types(): string[] {
@@ -1668,12 +1690,12 @@ export class Filter {
this.requires_adjustment_for_moved_with_target = false; 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 // Checks if any of the operators on this Filter object have
// the property that it's possible for their true value to // the property that it's possible for their true value to
// change as a result of messages being moved into the // change as a result of messages being moved into the
// channel/topic pair provided in the parameters. // 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; return true;
} }

View File

@@ -36,18 +36,19 @@ export function encode_operand(operator: string, operand: string): string {
} }
if (operator === "stream") { 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); return internal_url.encodeHashComponent(operand);
} }
export function encode_stream_name(operand: string): string { export function encode_stream_id(stream_id: number): string {
// stream_data prefixes the stream id, but it does not do the // stream_data postfixes the stream name, but it does not do the
// URI encoding piece // 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 { 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); operand = internal_url.decodeHashComponent(operand);
if (util.canonicalize_stream_synonyms(operator) === "stream") { if (util.canonicalize_stream_synonyms(operator) === "stream") {
const stream_id = stream_data.slug_to_stream_id(operand); return stream_data.slug_to_stream_id(operand)?.toString() ?? "";
if (stream_id) {
const stream = stream_data.get_sub_by_id(stream_id);
if (stream) {
return stream.name;
}
}
} }
return operand; return operand;
@@ -294,7 +289,7 @@ export function validate_group_settings_hash(hash: string): string {
export function decode_stream_topic_from_url( export function decode_stream_topic_from_url(
url_str: string, url_str: string,
): {stream_name: string; topic_name?: string} | null { ): {stream_id: number; topic_name?: string} | null {
try { try {
const url = new URL(url_str); const url = new URL(url_str);
if (url.origin !== window.location.origin || !url.hash.startsWith("#narrow")) { 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") { if (terms[0]?.operator !== "stream" && terms[0]?.operator !== "channel") {
return null; 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) { if (terms.length === 1) {
return {stream_name: terms[0].operand}; return {stream_id};
} }
if (terms[1]?.operator !== "topic") { if (terms[1]?.operator !== "topic") {
return null; return null;
} }
return {stream_name: terms[0].operand, topic_name: terms[1].operand}; return {stream_id, topic_name: terms[1].operand};
} catch { } catch {
return null; return null;
} }

View File

@@ -54,7 +54,6 @@ import * as sidebar_ui from "./sidebar_ui";
import * as spectators from "./spectators"; import * as spectators from "./spectators";
import * as starred_messages_ui from "./starred_messages_ui"; import * as starred_messages_ui from "./starred_messages_ui";
import {realm} from "./state_data"; import {realm} from "./state_data";
import * as stream_data from "./stream_data";
import * as stream_list from "./stream_list"; import * as stream_list from "./stream_list";
import * as stream_popover from "./stream_popover"; import * as stream_popover from "./stream_popover";
import * as stream_settings_ui from "./stream_settings_ui"; import * as stream_settings_ui from "./stream_settings_ui";
@@ -1234,7 +1233,7 @@ export function process_hotkey(e, hotkey) {
[ [
{ {
operator: "channel", operator: "channel",
operand: stream_data.get_stream_name_from_id(msg.stream_id), operand: msg.stream_id.toString(),
}, },
{operator: "topic", operand: msg.topic}, {operator: "topic", operand: msg.topic},
{operator: "near", operand: msg.id}, {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 ( if (
going_forward_change && going_forward_change &&
// This logic is a bit awkward. What we're trying to // This logic is a bit awkward. What we're trying to
@@ -490,7 +489,8 @@ export function update_messages(events) {
// messages within a narrow. // messages within a narrow.
selection_changed_topic && selection_changed_topic &&
current_filter && 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; let new_filter = current_filter;
if (new_filter && stream_changed) { if (new_filter && stream_changed) {
@@ -501,10 +501,9 @@ export function update_messages(events) {
// stream_data lookup here to fail. // stream_data lookup here to fail.
// //
// The fix is likely somewhat involved, so punting for now. // 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({ new_filter = new_filter.filter_with_new_params({
operator: "channel", operator: "channel",
operand: new_stream_name, operand: new_stream_id.toString(),
}); });
changed_narrow = true; 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 // 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. // the message cached, we need to refresh the narrow to display the message.
if (!changed_narrow && local_cache_missing_messages && current_filter) { 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; let moved_message_topic = orig_topic;
if (stream_changed) { 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) { if (topic_edited) {
@@ -545,7 +544,7 @@ export function update_messages(events) {
if ( if (
current_filter.can_newly_match_moved_messages( current_filter.can_newly_match_moved_messages(
moved_message_stream, moved_message_stream_id,
moved_message_topic, moved_message_topic,
) )
) { ) {

View File

@@ -340,7 +340,25 @@ export function load_messages(opts: MessageFetchOptions, attempt = 1): void {
if (page_params.narrow !== undefined) { if (page_params.narrow !== undefined) {
terms = [...terms, ...page_params.narrow]; 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 = let update_loading_indicator =

View File

@@ -450,10 +450,11 @@ export function show(raw_terms, opts) {
// location, then we should retarget this narrow operation // location, then we should retarget this narrow operation
// to where the message is located now. // to where the message is located now.
const narrow_topic = filter.operands("topic")[0]; const narrow_topic = filter.operands("topic")[0];
const narrow_stream_name = filter.operands("channel")[0]; const narrow_stream_data = stream_data.get_sub_by_id_string(
const narrow_stream_data = stream_data.get_sub(narrow_stream_name); filter.operands("channel")[0],
);
if (!narrow_stream_data) { 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 // We reconstruct the narrow with the data from the
// target message ID that we have. // target message ID that we have.
const adjusted_terms = Filter.adjusted_terms_if_moved( 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(); narrow_history.save_narrow_state_and_flush();
} }
export function activate_stream_for_cycle_hotkey(stream_id) { function activate_stream_for_cycle_hotkey(stream_id) {
const stream_name = stream_data.get_stream_name_from_id(stream_id);
// This is the common code for A/D hotkeys. // 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, {}); show(filter_expr, {});
} }
@@ -1158,9 +1158,8 @@ export function narrow_to_next_topic(opts = {}) {
return; return;
} }
const stream_name = stream_data.get_stream_name_from_id(next_narrow.stream_id);
const filter_expr = [ const filter_expr = [
{operator: "channel", operand: stream_name}, {operator: "channel", operand: next_narrow.stream_id.toString()},
{operator: "topic", operand: next_narrow.topic}, {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); unread_ops.notify_server_message_read(original);
} }
const stream_name = stream_data.get_stream_name_from_id(original.stream_id);
const search_terms = [ const search_terms = [
{operator: "channel", operand: stream_name}, {operator: "channel", operand: original.stream_id.toString()},
{operator: "topic", operand: original.topic}, {operator: "topic", operand: original.topic},
]; ];
opts = {then_select_id: target_id, ...opts}; opts = {then_select_id: target_id, ...opts};
@@ -1264,7 +1262,7 @@ export function narrow_by_recipient(target_id, opts) {
[ [
{ {
operator: "stream", operator: "stream",
operand: stream_data.get_stream_name_from_id(message.stream_id), operand: message.stream_id.toString(),
}, },
], ],
opts, opts,
@@ -1289,10 +1287,9 @@ export function to_compose_target() {
if (!stream_id) { if (!stream_id) {
return; 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 // 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. // 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(); const topic = compose_state.topic();
if (topic !== "") { if (topic !== "") {
terms.push({operator: "topic", operand: 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 rendered_markdown from "./rendered_markdown";
import * as search from "./search"; import * as search from "./search";
import {current_user} from "./state_data"; import {current_user} from "./state_data";
import * as stream_data from "./stream_data";
import type {SettingsSubscription} from "./stream_settings_data"; import type {SettingsSubscription} from "./stream_settings_data";
import type {StreamSubscription} from "./sub_store"; 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, is_spectator: page_params.is_spectator,
}); });
if (filter.has_operator("channel") && !filter._sub) { if (filter.has_operator("channel")) {
return { const current_stream = stream_data.get_sub_by_id_string(filter.operands("channel")[0]!);
...context, if (!current_stream) {
sub_count: "0", return {
formatted_sub_count: "0", ...context,
rendered_narrow_description: $t({ sub_count: "0",
defaultMessage: "This channel does not exist or is private.", formatted_sub_count: "0",
}), rendered_narrow_description: $t({
}; defaultMessage: "This channel does not exist or is private.",
} }),
};
if (filter._sub) { }
// We can now be certain that the narrow // We can now be certain that the narrow
// involves a stream which exists and // involves a stream which exists and
// the current user can access. // the current user can access.
const current_stream = filter._sub;
const sub_count = peer_data.get_subscriber_count(current_stream.stream_id); const sub_count = peer_data.get_subscriber_count(current_stream.stream_id);
return { return {
...context, ...context,
@@ -131,11 +131,15 @@ function get_message_view_header_context(filter: Filter | undefined): MessageVie
export function colorize_message_view_header(): void { export function colorize_message_view_header(): void {
const filter = narrow_state.filter(); 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; return;
} }
// selecting i instead of .fa because web public streams have custom icon. // 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 { 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 // This function checks if "modified_sub" which is the stream whose values
// have been updated is the same as the stream which is currently // 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 { export function maybe_rerender_title_area_for_stream(modified_sub: SettingsSubscription): void {
const filter = narrow_state.filter(); const current_stream_id = narrow_state.stream_id();
if (filter && filter._sub && filter._sub.stream_id === modified_sub.stream_id) { if (current_stream_id === modified_sub.stream_id) {
render_title_area(); render_title_area();
} }
} }

View File

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

View File

@@ -114,21 +114,26 @@ export function set_compose_defaults(): {
return opts; 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) { if (current_filter === undefined) {
return undefined; return undefined;
} }
const stream_operands = current_filter.operands("channel"); const stream_operands = current_filter.operands("channel");
if (stream_operands.length === 1 && stream_operands[0] !== undefined) { if (stream_operands.length === 1 && stream_operands[0] !== undefined) {
const name = stream_operands[0]; return Number.parseInt(stream_operands[0], 10);
// Use get_name() to get the most current stream
// name (considering renames and capitalization).
return stream_data.get_name(name);
} }
return undefined; 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( export function stream_sub(
current_filter: Filter | undefined = filter(), current_filter: Filter | undefined = filter(),
): StreamSubscription | undefined { ): StreamSubscription | undefined {
@@ -140,19 +145,7 @@ export function stream_sub(
if (stream_operands.length !== 1 || stream_operands[0] === undefined) { if (stream_operands.length !== 1 || stream_operands[0] === undefined) {
return undefined; return undefined;
} }
return stream_data.get_sub_by_id_string(stream_operands[0]);
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;
} }
export function topic(current_filter: Filter | undefined = filter()): string | undefined { 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 people from "./people";
import * as recent_view_util from "./recent_view_util"; import * as recent_view_util from "./recent_view_util";
import {realm} from "./state_data"; import {realm} from "./state_data";
import * as stream_data from "./stream_data";
import * as unread from "./unread"; import * as unread from "./unread";
import type {FullUnreadCountsData} 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.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 // The stream is not set because it does not currently
// exist (possibly due to a stream name change), or it // exist, or it is a private stream and the user is not
// is a private stream and the user is not subscribed. // subscribed.
return filter_title; return filter_title;
} }
if (filter.has_operator("topic")) { if (filter.has_operator("topic")) {

View File

@@ -8,7 +8,6 @@ import {$t} from "./i18n";
import * as message_view from "./message_view"; import * as message_view from "./message_view";
import * as people from "./people"; import * as people from "./people";
import * as scheduled_messages from "./scheduled_messages"; import * as scheduled_messages from "./scheduled_messages";
import * as stream_data from "./stream_data";
import * as timerender from "./timerender"; import * as timerender from "./timerender";
export function hide_scheduled_message_success_compose_banner(scheduled_message_id) { 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", 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}, {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 * as people from "./people";
import type {User} from "./people"; import type {User} from "./people";
import type {NarrowTerm} from "./state_data"; import type {NarrowTerm} from "./state_data";
import * as stream_data from "./stream_data";
import * as user_status from "./user_status"; import * as user_status from "./user_status";
import type {UserStatusEmojiInfo} from "./user_status"; import type {UserStatusEmojiInfo} from "./user_status";
import * as util from "./util"; 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 { export function get_search_string_from_item(item: SearchPill): string {
const sign = item.negated ? "-" : ""; 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 // 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") { if (item.type === "search_user") {
return item.users.map((user) => user.email).join(","); 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; return item.operand;
} }
export function get_current_search_pill_terms(pill_widget: SearchPillWidget): NarrowTerm[] { export function get_current_search_pill_terms(pill_widget: SearchPillWidget): NarrowTerm[] {
return pill_widget.items().map((item) => ({ return pill_widget.items().map((item) => ({
...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; const query = last.operand;
let channels = stream_data.subscribed_streams(); 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); channels = typeahead_helper.sorter(query, channels, (x) => x);
const regex = typeahead_helper.build_highlight_regex(query); const regex = typeahead_helper.build_highlight_regex(query);
const highlight_query = typeahead_helper.highlight_with_escaping_and_regex; const highlight_query = typeahead_helper.highlight_with_escaping_and_regex;
return channels.map((channel) => { return channels.map((channel_name) => {
const prefix = "channel"; const prefix = "channel";
const highlighted_channel = highlight_query(regex, channel); const highlighted_channel = highlight_query(regex, channel_name);
const verb = last.negated ? "exclude " : ""; const verb = last.negated ? "exclude " : "";
const description_html = verb + prefix + " " + highlighted_channel; const description_html = verb + prefix + " " + highlighted_channel;
const channel = stream_data.get_sub_by_name(channel_name);
assert(channel !== undefined);
const term = { const term = {
operator: "channel", operator: "channel",
operand: channel, operand: channel.stream_id.toString(),
negated: last.negated, negated: last.negated,
}; };
const search_string = Filter.unparse([term]); 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 operator = Filter.canonicalize_operator(last.operator);
const operand = last.operand; const operand = last.operand;
const negated = operator === "topic" && last.negated; const negated = operator === "topic" && last.negated;
let channel: string | undefined; let channel_id: string | undefined;
let guess: string | undefined; let guess: string | undefined;
const filter = new Filter(terms); const filter = new Filter(terms);
const suggest_terms: NarrowTerm[] = []; const suggest_terms: NarrowTerm[] = [];
@@ -496,28 +498,28 @@ function get_topic_suggestions(last: NarrowTerm, terms: NarrowTerm[]): Suggestio
switch (operator) { switch (operator) {
case "channel": case "channel":
guess = ""; guess = "";
channel = operand; channel_id = operand;
suggest_terms.push(last); suggest_terms.push(last);
break; break;
case "topic": case "topic":
case "search": case "search":
guess = operand; guess = operand;
if (filter.has_operator("channel")) { if (filter.has_operator("channel")) {
channel = filter.operands("channel")[0]; channel_id = filter.operands("channel")[0];
} else { } else {
channel = narrow_state.stream_name(); channel_id = narrow_state.stream_id()?.toString();
if (channel) { if (channel_id) {
suggest_terms.push({operator: "channel", operand: channel}); suggest_terms.push({operator: "channel", operand: channel_id});
} }
} }
break; break;
} }
if (!channel) { if (!channel_id) {
return []; return [];
} }
const subscription = stream_data.get_sub(channel); const subscription = stream_data.get_sub_by_id_string(channel_id);
if (!subscription) { if (!subscription) {
return []; return [];
} }
@@ -849,12 +851,12 @@ function suggestion_search_string(suggestion_line: SuggestionLine): string {
} }
function suggestions_for_current_filter(): SuggestionLine[] { function suggestions_for_current_filter(): SuggestionLine[] {
if (narrow_state.stream_name() && narrow_state.topic() !== "") { if (narrow_state.stream_id() && narrow_state.topic() !== "") {
return [ return [
get_default_suggestion_line([ get_default_suggestion_line([
{ {
operator: "channel", operator: "channel",
operand: narrow_state.stream_name()!, operand: narrow_state.stream_id()!.toString(),
}, },
]), ]),
get_default_suggestion_line(narrow_state.search_terms()), 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 blueslip from "./blueslip";
import * as color_data from "./color_data"; import * as color_data from "./color_data";
import {FoldDict} from "./fold_dict"; import {FoldDict} from "./fold_dict";
@@ -177,6 +179,18 @@ export function get_sub(stream_name: string): StreamSubscription | undefined {
return 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 { export function get_sub_by_id(stream_id: number): StreamSubscription | undefined {
return stream_info.get(stream_id); 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); return sub_store.get(stream_id);
} }
export function name_to_slug(name: string): string { export function id_to_slug(stream_id: number): string {
const stream_id = get_stream_id(name); let name = get_stream_name_from_id(stream_id);
if (!stream_id) {
return name;
}
// The name part of the URL doesn't really matter, so we try to // The name part of the URL doesn't really matter, so we try to
// make it pretty. // make it pretty.
name = name.replaceAll(" ", "-"); name = name.replaceAll(" ", "-");
@@ -605,11 +614,6 @@ export function can_post_messages_in_stream(stream: StreamSubscription): boolean
return true; 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 { export function is_subscribed(stream_id: number): boolean {
const sub = sub_store.get(stream_id); const sub = sub_store.get(stream_id);
return sub ? sub.subscribed : false; 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; return sub.invite_only;
} }
export function is_web_public_by_stream_name(stream_name: string): boolean { export function is_web_public_by_stream_id(stream_id: number): boolean {
const sub = get_sub(stream_name); const sub = get_sub_by_id(stream_id);
if (sub === undefined) { if (sub === undefined) {
return false; return false;
} }
@@ -667,22 +671,6 @@ export function is_default_stream_id(stream_id: number): boolean {
return default_stream_ids.has(stream_id); 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 { export function is_user_subscribed(stream_id: number, user_id: number): boolean {
const sub = sub_store.get(stream_id); const sub = sub_store.get(stream_id);
if (sub === undefined || !can_view_subscribers(sub)) { if (sub === undefined || !can_view_subscribers(sub)) {

View File

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

View File

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

View File

@@ -903,7 +903,7 @@ export function view_stream() {
const row_data = get_row_data(active_data.$row); const row_data = get_row_data(active_data.$row);
if (row_data) { if (row_data) {
const stream_narrow_hash = 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); browser_history.go_to_location(stream_narrow_hash);
} }
} }

View File

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

View File

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

View File

@@ -22,7 +22,6 @@ import * as overlays from "./overlays";
import * as people from "./people"; import * as people from "./people";
import * as recent_view_ui from "./recent_view_ui"; import * as recent_view_ui from "./recent_view_ui";
import type {NarrowTerm} from "./state_data"; import type {NarrowTerm} from "./state_data";
import * as stream_data from "./stream_data";
import * as ui_report from "./ui_report"; import * as ui_report from "./ui_report";
import * as unread from "./unread"; import * as unread from "./unread";
import * as unread_ui from "./unread_ui"; 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 { 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( bulk_update_read_flags_for_narrow(
[ [
{operator: "is", operand: "unread", negated: false}, {operator: "is", operand: "unread", negated: false},
{operator: "channel", operand: stream_name}, {operator: "channel", operand: stream_id.toString()},
], ],
"add", "add",
); );
} }
export function mark_topic_as_read(stream_id: number, topic: string): void { 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( bulk_update_read_flags_for_narrow(
[ [
{operator: "is", operand: "unread", negated: false}, {operator: "is", operand: "unread", negated: false},
{operator: "channel", operand: stream_name}, {operator: "channel", operand: stream_id.toString()},
{operator: "topic", operand: topic}, {operator: "topic", operand: topic},
], ],
"add", "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 { 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( bulk_update_read_flags_for_narrow(
[ [
{operator: "channel", operand: stream_name}, {operator: "channel", operand: stream_id.toString()},
{operator: "topic", operand: topic}, {operator: "topic", operand: topic},
], ],
"remove", "remove",

View File

@@ -105,7 +105,7 @@ const $fred_stub = $.create("fred stub");
const rome_sub = {name: "Rome", subscribed: true, stream_id: 1001}; const rome_sub = {name: "Rome", subscribed: true, stream_id: 1001};
function add_sub_and_set_as_current_narrow(sub) { function add_sub_and_set_as_current_narrow(sub) {
stream_data.add_sub(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({ message_lists.set_current({
data: { data: {
filter, filter,

View File

@@ -34,7 +34,7 @@ run_test("filter", () => {
stream_data.add_sub(denmark_stream); stream_data.add_sub(denmark_stream);
const filter_terms = [ const filter_terms = [
{operator: "stream", operand: "Denmark"}, {operator: "stream", operand: denmark_stream.stream_id.toString()},
{operator: "topic", operand: "copenhagen"}, {operator: "topic", operand: "copenhagen"},
]; ];
@@ -85,7 +85,7 @@ run_test("narrow_state", () => {
// Now set up a Filter object. // Now set up a Filter object.
const filter_terms = [ const filter_terms = [
{operator: "stream", operand: "Denmark"}, {operator: "stream", operand: denmark_stream.stream_id.toString()},
{operator: "topic", operand: "copenhagen"}, {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); people.add_active_user(hamlet);
const frontend_id = 99;
const frontend = { const frontend = {
stream_id: 99, stream_id: frontend_id,
name: "frontend", name: "frontend",
}; };
@@ -44,7 +45,7 @@ run_test("hash_util", () => {
encode_decode_operand(operator, operand, "15-Hamlet"); encode_decode_operand(operator, operand, "15-Hamlet");
operator = "stream"; operator = "stream";
operand = "frontend"; operand = frontend_id.toString();
encode_decode_operand(operator, operand, "99-frontend"); encode_decode_operand(operator, operand, "99-frontend");
@@ -175,19 +176,19 @@ run_test("test_is_in_specified_hash_category", () => {
run_test("test_parse_narrow", () => { run_test("test_parse_narrow", () => {
assert.deepEqual(hash_util.parse_narrow(["narrow", "stream", "99-frontend"]), [ 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"]), [ 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); assert.equal(hash_util.parse_narrow(["narrow", "BOGUS"]), undefined);
// For nonexistent streams, we get the full slug. // For nonexistent streams, we get an empty string. We don't use this
// We possibly should remove the prefix and fix this test. // anywhere, and just show "Invalid stream" in the navbar.
assert.deepEqual(hash_util.parse_narrow(["narrow", "stream", "42-bogus"]), [ 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 stream_data = zrequire("stream_data");
const {Filter} = zrequire("../src/filter"); 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", () => { run_test("terms_round_trip", () => {
let terms; let terms;
let hash; let hash;
let narrow; let narrow;
terms = [ terms = [
{operator: "stream", operand: "devel"}, {operator: "stream", operand: devel_id.toString()},
{operator: "topic", operand: "algol"}, {operator: "topic", operand: "algol"},
]; ];
hash = hash_util.search_terms_to_hash(terms); 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("/")); narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [ assert.deepEqual(narrow, [
{operator: "stream", operand: "devel", negated: false}, {operator: "stream", operand: devel_id.toString(), negated: false},
{operator: "topic", operand: "algol", negated: false}, {operator: "topic", operand: "algol", negated: false},
]); ]);
terms = [ terms = [
{operator: "stream", operand: "devel"}, {operator: "stream", operand: devel_id.toString()},
{operator: "topic", operand: "visual c++", negated: true}, {operator: "topic", operand: "visual c++", negated: true},
]; ];
hash = hash_util.search_terms_to_hash(terms); 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("/")); narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [ assert.deepEqual(narrow, [
{operator: "stream", operand: "devel", negated: false}, {operator: "stream", operand: devel_id.toString(), negated: false},
{operator: "topic", operand: "visual c++", negated: true}, {operator: "topic", operand: "visual c++", negated: true},
]); ]);
// test new encodings, where we have a stream id // test new encodings, where we have a stream id
const florida_id = 987;
const florida_stream = { const florida_stream = {
name: "Florida, USA", name: "Florida, USA",
stream_id: 987, stream_id: florida_id,
}; };
stream_data.add_sub(florida_stream); 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); hash = hash_util.search_terms_to_hash(terms);
assert.equal(hash, "#narrow/stream/987-Florida.2C-USA"); assert.equal(hash, "#narrow/stream/987-Florida.2C-USA");
narrow = hash_util.parse_narrow(hash.split("/")); 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", () => { 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" // Confirm the URLs generated from search terms use "stream" and "streams"
// and that the new Filter has the new "channel" and "channels" operators. // 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); 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("/")); 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); 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"}]; terms = [{operator: "channels", operand: "public"}];
hash = hash_util.search_terms_to_hash(terms); 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, // Confirm that a narrow URL with "channel" and an enocoded stream/channel ID,
// will be decoded correctly. // will be decoded correctly.
const test_stream_id = 34;
const test_channel = { const test_channel = {
name: "decode", name: "decode",
stream_id: 34, stream_id: test_stream_id,
}; };
stream_data.add_sub(test_channel); stream_data.add_sub(test_channel);
hash = "#narrow/channel/34-decode"; hash = "#narrow/channel/34-decode";
narrow = hash_util.parse_narrow(hash.split("/")); 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); 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", () => { 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("/")); const narrow = hash_util.parse_narrow(hash.split("/"));
assert.deepEqual(narrow, [ assert.deepEqual(narrow, [
{operator: "stream", operand: "devel", negated: false}, {operator: "stream", operand: devel_id.toString(), negated: false},
{operator: "topic", operand: "algol", 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"); 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"; window.location.hash = "#narrow/stream/Denmark";
helper.clear_events(); helper.clear_events();
@@ -280,7 +305,7 @@ run_test("hash_interactions", ({override, override_rewire}) => {
"message_view.show", "message_view.show",
]); ]);
let terms = helper.get_narrow_terms(); let terms = helper.get_narrow_terms();
assert.equal(terms[0].operand, "Denmark"); assert.equal(terms[0].operand, denmark_id.toString());
window.location.hash = "#narrow"; window.location.hash = "#narrow";

View File

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

View File

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

View File

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

View File

@@ -66,19 +66,19 @@ test("unsubscribe", () => {
stream_data.add_sub(devel); stream_data.add_sub(devel);
// verify clean slate // 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 // set up our subscription
devel.subscribed = true; devel.subscribed = true;
peer_data.set_subscribers(devel.stream_id, [me.user_id]); peer_data.set_subscribers(devel.stream_id, [me.user_id]);
// ensure our setup is accurate // 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 // DO THE UNSUBSCRIBE HERE
stream_data.unsubscribe_myself(devel); stream_data.unsubscribe_myself(devel);
assert.ok(!devel.subscribed); 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.subscribed_subs(), devel));
assert.ok(contains_sub(stream_data.unsubscribed_subs(), devel)); assert.ok(contains_sub(stream_data.unsubscribed_subs(), devel));
@@ -96,7 +96,7 @@ test("subscribers", () => {
people.add_active_user(george); people.add_active_user(george);
// verify setup // 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; 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.equal(opts.updater("ver"), "ver");
assert.ok(!input_pill_displayed); assert.ok(!input_pill_displayed);
const verona_stream_id = verona.stream_id.toString();
terms = [ terms = [
{ {
negated: false, negated: false,
operator: "channel", operator: "channel",
operand: "Verona", operand: verona_stream_id,
}, },
]; ];
expected_pill_display_value = "channel: Verona"; expected_pill_display_value = "channel: Verona";
_setup(terms); _setup(terms);
input_pill_displayed = false; input_pill_displayed = false;
mock_pill_removes(search.search_pill_widget); 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); assert.ok(input_pill_displayed);
search.__Rewire__("is_using_input_method", true); search.__Rewire__("is_using_input_method", true);
_setup(terms); _setup(terms);
input_pill_displayed = false; input_pill_displayed = false;
mock_pill_removes(search.search_pill_widget); 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); assert.ok(input_pill_displayed);
} }
return { return {

View File

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

View File

@@ -17,6 +17,7 @@ const people = zrequire("people");
const settings_config = zrequire("settings_config"); const settings_config = zrequire("settings_config");
const sub_store = zrequire("sub_store"); const sub_store = zrequire("sub_store");
const stream_data = zrequire("stream_data"); const stream_data = zrequire("stream_data");
const hash_util = zrequire("hash_util");
const stream_settings_data = zrequire("stream_settings_data"); const stream_settings_data = zrequire("stream_settings_data");
const user_groups = zrequire("user_groups"); const user_groups = zrequire("user_groups");
@@ -135,10 +136,8 @@ test("basics", () => {
assert.deepEqual(stream_data.get_colors(), ["red", "yellow"]); assert.deepEqual(stream_data.get_colors(), ["red", "yellow"]);
assert.deepEqual(stream_data.subscribed_stream_ids(), [social.stream_id, test.stream_id]); 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(social.stream_id));
assert.ok(stream_data.is_subscribed_by_name("Social")); assert.ok(!stream_data.is_subscribed(denmark.stream_id));
assert.ok(!stream_data.is_subscribed_by_name("Denmark"));
assert.ok(!stream_data.is_subscribed_by_name("Rome"));
assert.equal(stream_data.get_stream_privacy_policy(test.stream_id), "public"); 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"); assert.equal(stream_data.get_stream_privacy_policy(social.stream_id), "invite-only");
@@ -147,9 +146,10 @@ test("basics", () => {
"invite-only-public-history", "invite-only-public-history",
); );
assert.equal(stream_data.get_stream_privacy_policy(web_public_stream.stream_id), "web-public"); 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_id(web_public_stream.stream_id));
assert.ok(!stream_data.is_web_public_by_stream_name(social.name)); assert.ok(!stream_data.is_web_public_by_stream_id(social.stream_id));
assert.ok(!stream_data.is_web_public_by_stream_name("unknown")); 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)); assert.ok(stream_data.is_invite_only_by_stream_id(social.stream_id));
// Unknown 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(undefined), "#c2c2c2");
assert.equal(stream_data.get_color(1234567), "#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(social.stream_id));
assert.ok(stream_data.is_muted(denmark.stream_id)); assert.ok(stream_data.is_muted(denmark.stream_id));
@@ -176,19 +173,37 @@ test("basics", () => {
// "new" correct url formats // "new" correct url formats
assert.equal(stream_data.slug_to_stream_id("2-social"), 2); 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(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 // we still get 2 because it's a valid stream id
assert.equal(stream_data.slug_to_stream_id("2-whatever"), 2); assert.equal(stream_data.slug_to_stream_id("2-whatever"), 2);
// invalid stream id assert.equal(stream_data.slug_to_stream_id("2-"), 2);
assert.equal(stream_data.slug_to_stream_id("999-social"), undefined);
// legacy // legacy, we recognize "social" as a valid channel name
assert.equal(stream_data.slug_to_stream_id("social"), 2); 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(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(stream_data.slug_to_stream_id("99whatever"), undefined);
assert.equal(hash_util.decode_operand("channel", "99whatever"), "");
// sub_store // sub_store
assert.equal(sub_store.get(-3), undefined); assert.equal(sub_store.get(-3), undefined);
@@ -528,12 +543,12 @@ test("delete_sub", () => {
stream_data.add_sub(canada); 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(stream_data.get_sub("Canada").stream_id, canada.stream_id);
assert.equal(sub_store.get(canada.stream_id).name, "Canada"); assert.equal(sub_store.get(canada.stream_id).name, "Canada");
stream_data.delete_sub(canada.stream_id); 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(!stream_data.get_sub("Canada"));
assert.ok(!sub_store.get(canada.stream_id)); assert.ok(!sub_store.get(canada.stream_id));

View File

@@ -83,7 +83,7 @@ const frontend = {
}; };
function narrow_to_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 = { message_lists.current = {
data: { data: {
filter, filter,
@@ -382,12 +382,12 @@ test("marked_subscribed (emails)", ({override}) => {
$("#channels_overlay_container .stream-row:not(.notdisplayed)").length = 0; $("#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]; const user_ids = [15, 20, 25, me.user_id];
stream_events.mark_subscribed(sub, user_ids, ""); stream_events.mark_subscribed(sub, user_ids, "");
assert.deepEqual(new Set(peer_data.get_subscribers(sub.stream_id)), new Set(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"); const args = subs_stub.get_args("sub");
assert.deepEqual(sub, args.sub); assert.deepEqual(sub, args.sub);

View File

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