stream_list_sort: Don't include unsubscribed streams in sort results.

Fixes bug reported here:
https://chat.zulip.org/#narrow/channel/9-issues/topic/exception.20when.20viewing.20world-public.20channel.20as.20a.20guest/near/2240871
This commit is contained in:
Evy Kassirer
2025-08-12 21:12:23 -07:00
committed by Tim Abbott
parent 3cbf0e70a2
commit 14ce2c5a81
3 changed files with 37 additions and 12 deletions

View File

@@ -140,6 +140,7 @@ export function get_stream_ids_in_folder(folder_id: number): number[] {
export function get_channels_in_folders_matching_search_term_in_folder_name(
search_term: string,
all_subscribed_stream_ids: Set<number>,
): number[] {
const channel_folders = get_channel_folders();
const matching_channel_folders = util.filter_by_word_prefix_match(
@@ -150,7 +151,11 @@ export function get_channels_in_folders_matching_search_term_in_folder_name(
const channel_ids: number[] = [];
for (const channel_folder of matching_channel_folders) {
channel_ids.push(...get_stream_ids_in_folder(channel_folder.id));
for (const stream_id of get_stream_ids_in_folder(channel_folder.id)) {
if (all_subscribed_stream_ids.has(stream_id)) {
channel_ids.push(stream_id);
}
}
}
return channel_ids;
}

View File

@@ -122,12 +122,15 @@ type StreamListSortResult = {
sections: StreamListSection[];
};
export function sort_groups(stream_ids: number[], search_term: string): StreamListSortResult {
export function sort_groups(
all_subscribed_stream_ids: number[],
search_term: string,
): StreamListSortResult {
const stream_id_to_name = (stream_id: number): string => sub_store.get(stream_id)!.name;
// Use -, _, : and / as word separators apart from the default space character
const word_separator_regex = /[\s/:_-]/;
stream_ids = util.filter_by_word_prefix_match(
stream_ids,
let matching_stream_ids = util.filter_by_word_prefix_match(
all_subscribed_stream_ids,
search_term,
stream_id_to_name,
word_separator_regex,
@@ -137,24 +140,25 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
if (
current_channel_id !== undefined &&
stream_data.is_subscribed(current_channel_id) &&
!stream_ids.includes(current_channel_id) &&
!matching_stream_ids.includes(current_channel_id) &&
// If any of the topics of the channel match the search term, we need to
// include the channel in the list of streams.
topic_list_data.get_list_info(current_channel_id, false, (topic_names) =>
topic_list_data.filter_topics_by_search_term(topic_names, search_term),
).items.length > 0
) {
stream_ids.push(current_channel_id);
matching_stream_ids.push(current_channel_id);
}
// If the channel folder matches the search term, include all channels
// of that folder.
if (user_settings.web_left_sidebar_show_channel_folders && search_term) {
stream_ids = [
matching_stream_ids = [
...new Set([
...stream_ids,
...matching_stream_ids,
...channel_folders.get_channels_in_folders_matching_search_term_in_folder_name(
search_term,
new Set(all_subscribed_stream_ids),
),
]),
];
@@ -179,7 +183,7 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
const folder_sections = new Map<number, StreamListSection>();
for (const stream_id of stream_ids) {
for (const stream_id of matching_stream_ids) {
const sub = sub_store.get(stream_id);
assert(sub);
if (sub.is_archived) {

View File

@@ -114,22 +114,38 @@ run_test("basics", () => {
]);
assert.deepEqual(channel_folders.get_stream_ids_in_folder(backend_folder.id), []);
const subscribed_streams = new Set([
stream_1.stream_id,
stream_2.stream_id,
stream_3.stream_id,
stream_4.stream_id,
]);
// Tests for get_channels_in_folders_matching_search_term_in_folder_name
// Should match 'Frontend' folder and return its streams
assert.deepEqual(
channel_folders.get_channels_in_folders_matching_search_term_in_folder_name("Front"),
channel_folders.get_channels_in_folders_matching_search_term_in_folder_name(
"Front",
subscribed_streams,
),
[stream_2.stream_id, stream_4.stream_id],
);
// Should match 'Backend' folder and return no streams
assert.deepEqual(
channel_folders.get_channels_in_folders_matching_search_term_in_folder_name("Back"),
channel_folders.get_channels_in_folders_matching_search_term_in_folder_name(
"Back",
subscribed_streams,
),
[],
);
// Should match no folder and return empty array
assert.deepEqual(
channel_folders.get_channels_in_folders_matching_search_term_in_folder_name("Nonexistent"),
channel_folders.get_channels_in_folders_matching_search_term_in_folder_name(
"Nonexistent",
subscribed_streams,
),
[],
);