diff --git a/tools/lib/capitalization.py b/tools/lib/capitalization.py index 7cde496006a7c..b6142338e5185 100644 --- a/tools/lib/capitalization.py +++ b/tools/lib/capitalization.py @@ -114,6 +114,7 @@ r"^moving messages$", r"^start a conversation$", r"^welcome to Zulip!$", + r"^general chat$", # These are used as example short names (e.g. an uncapitalized context): r"^marketing$", r"^cookie$", diff --git a/web/src/topic_list_data.ts b/web/src/topic_list_data.ts index 9d399ce87423d..c48ccf5d877fd 100644 --- a/web/src/topic_list_data.ts +++ b/web/src/topic_list_data.ts @@ -18,6 +18,7 @@ export type TopicInfo = { topic_name: string; topic_resolved_prefix: string; topic_display_name: string; + is_empty_string_topic: boolean; unread: number; is_zero: boolean; is_muted: boolean; @@ -127,7 +128,8 @@ function choose_topics( stream_id, topic_name, topic_resolved_prefix, - topic_display_name, + topic_display_name: util.get_final_topic_display_name(topic_display_name), + is_empty_string_topic: topic_display_name === "", unread: num_unread, is_zero: num_unread === 0, is_muted: is_topic_muted, diff --git a/web/src/util.ts b/web/src/util.ts index 5cc632f403837..d251337b1308c 100644 --- a/web/src/util.ts +++ b/web/src/util.ts @@ -2,8 +2,10 @@ import Handlebars from "handlebars/runtime.js"; import _ from "lodash"; import * as blueslip from "./blueslip.ts"; +import {$t} from "./i18n.ts"; import type {MatchedMessage, Message, RawMessage} from "./message_store.ts"; import type {UpdateMessageEvent} from "./server_event_types.ts"; +import {realm} from "./state_data.ts"; import {user_settings} from "./user_settings.ts"; // From MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random @@ -533,3 +535,13 @@ export function compare_a_b(a: T, b: T): number { } return -1; } + +export function get_final_topic_display_name(topic_display_name: string): string { + if (topic_display_name === "") { + if (realm.realm_empty_topic_display_name === "general chat") { + return $t({defaultMessage: "general chat"}); + } + return realm.realm_empty_topic_display_name; + } + return topic_display_name; +} diff --git a/web/styles/zulip.css b/web/styles/zulip.css index 5ddaa11029a2f..7c59ac93e75b2 100644 --- a/web/styles/zulip.css +++ b/web/styles/zulip.css @@ -2115,3 +2115,7 @@ body:not(.hide-left-sidebar) { border: 1px solid var(--color-border-personal-menu-avatar); } } + +.empty-topic-display { + font-style: italic; +} diff --git a/web/templates/topic_list_item.hbs b/web/templates/topic_list_item.hbs index ccfa714fb190f..687e8c0700786 100644 --- a/web/templates/topic_list_item.hbs +++ b/web/templates/topic_list_item.hbs @@ -4,7 +4,7 @@ {{topic_resolved_prefix}} - {{topic_display_name}} + {{topic_display_name}}
{{#if contains_unread_mention}} diff --git a/web/tests/topic_list_data.test.cjs b/web/tests/topic_list_data.test.cjs index c1afc0fd8fdb1..e76f9eb433b04 100644 --- a/web/tests/topic_list_data.test.cjs +++ b/web/tests/topic_list_data.test.cjs @@ -37,7 +37,9 @@ const stream_topic_history = zrequire("stream_topic_history"); const topic_list_data = zrequire("topic_list_data"); const unread = zrequire("unread"); -set_realm({}); +const REALM_EMPTY_TOPIC_DISPLAY_NAME = "test general chat"; + +set_realm({realm_empty_topic_display_name: REALM_EMPTY_TOPIC_DISPLAY_NAME}); const general = { stream_id: 556, @@ -102,6 +104,7 @@ test("get_list_info w/real stream_topic_history", ({override}) => { topic_name: "topic 11", topic_resolved_prefix: "", topic_display_name: "topic 11", + is_empty_string_topic: false, unread: 0, is_zero: true, stream_id: 556, @@ -132,6 +135,7 @@ test("get_list_info w/real stream_topic_history", ({override}) => { topic_display_name: "topic 9", topic_name: "✔ topic 9", topic_resolved_prefix: "✔ ", + is_empty_string_topic: false, unread: 0, url: "#narrow/channel/556-general/topic/.E2.9C.94.20topic.209", }); @@ -147,18 +151,44 @@ test("get_list_info w/real stream_topic_history", ({override}) => { topic_display_name: "topic 8", topic_name: "topic 8", topic_resolved_prefix: "", + is_empty_string_topic: false, unread: 0, url: "#narrow/channel/556-general/topic/topic.208", }); + // Empty string as topic name. + add_topic_message("", 2025); + + list_info = get_list_info(); + assert.equal(list_info.items.length, 8); + assert.equal(list_info.more_topics_unreads, 0); + assert.equal(list_info.more_topics_have_unread_mention_messages, false); + assert.equal(list_info.num_possible_topics, 11); + + assert.deepEqual(list_info.items[0], { + contains_unread_mention: false, + is_active_topic: false, + is_muted: false, + is_followed: false, + is_unmuted_or_followed: false, + is_zero: true, + stream_id: 556, + topic_display_name: REALM_EMPTY_TOPIC_DISPLAY_NAME, + topic_name: "", + topic_resolved_prefix: "", + is_empty_string_topic: true, + unread: 0, + url: "#narrow/channel/556-general/topic/", + }); + // If we zoom in, our results are based on topic filter. // If topic search input is empty, we show all 10 topics. const zoomed = true; list_info = get_list_info(zoomed); - assert.equal(list_info.items.length, 10); + assert.equal(list_info.items.length, 11); assert.equal(list_info.more_topics_unreads, 0); assert.equal(list_info.more_topics_have_unread_mention_messages, false); - assert.equal(list_info.num_possible_topics, 10); + assert.equal(list_info.num_possible_topics, 11); add_topic_message("After Brooklyn", 1008); add_topic_message("Catering", 1009); diff --git a/web/tests/util.test.cjs b/web/tests/util.test.cjs index 5bcb006957dae..5abd85a0feba6 100644 --- a/web/tests/util.test.cjs +++ b/web/tests/util.test.cjs @@ -9,8 +9,12 @@ const {set_global, with_overrides, zrequire} = require("./lib/namespace.cjs"); const {run_test} = require("./lib/test.cjs"); const blueslip = zrequire("blueslip"); +const {set_realm} = zrequire("state_data"); const {initialize_user_settings} = zrequire("user_settings"); +const realm = {}; +set_realm(realm); + set_global("document", {}); const util = zrequire("util"); @@ -488,3 +492,21 @@ run_test("compare_a_b", () => { const sorted_by_name = [...unsorted].sort((a, b) => util.compare_a_b(a.name, b.name)); assert.deepEqual(sorted_by_name, [user2, user4, user3, user1]); }); + +run_test("get_final_topic_display_name", ({override}) => { + // When the topic name is not an empty string, + // the displayed topic name matches the actual topic name. + assert.deepEqual(util.get_final_topic_display_name("not empty string"), "not empty string"); + + // When the topic name is an empty string, there are two possible scenarios: + // 1. The `realm_empty_topic_display_name` setting has its default value + // "general chat". In this case, the topic is displayed as the translated + // value of "general chat" based on the user's language settings. + // 2. The `realm_empty_topic_display_name` setting has been customized by + // an admin. In this case, the topic is displayed using the value of + // `realm_empty_topic_display_name` without any translation. + override(realm, "realm_empty_topic_display_name", "general chat"); + assert.deepEqual(util.get_final_topic_display_name(""), "translated: general chat"); + override(realm, "realm_empty_topic_display_name", "random topic name"); + assert.deepEqual(util.get_final_topic_display_name(""), "random topic name"); +});