You are searching for messages that belong to more than one stream, which is not possible.
",
+ ),
);
set_filter([
@@ -316,10 +522,12 @@ run_test("show_invalid_narrow_message", () => {
]);
hide_all_empty_narrow_messages();
narrow_banner.show_empty_narrow_message();
- assert.ok($("#empty_search_narrow_message").visible());
assert.equal(
- display.text(),
- "translated: You are searching for messages that belong to more than one topic, which is not possible.",
+ $(".empty_feed_notice_main").html(),
+ empty_narrow_html(
+ "translated: No search results",
+ "translated HTML:
You are searching for messages that belong to more than one topic, which is not possible.
",
+ ),
);
people.add_active_user(ray);
@@ -331,10 +539,12 @@ run_test("show_invalid_narrow_message", () => {
]);
hide_all_empty_narrow_messages();
narrow_banner.show_empty_narrow_message();
- assert.ok($("#empty_search_narrow_message").visible());
assert.equal(
- display.text(),
- "translated: You are searching for messages that are sent by more than one person, which is not possible.",
+ $(".empty_feed_notice_main").html(),
+ empty_narrow_html(
+ "translated: No search results",
+ "translated HTML:
You are searching for messages that are sent by more than one person, which is not possible.
",
+ ),
);
});
diff --git a/frontend_tests/puppeteer_tests/message-basics.ts b/frontend_tests/puppeteer_tests/message-basics.ts
index db5d8cf176..875fa7e447 100644
--- a/frontend_tests/puppeteer_tests/message-basics.ts
+++ b/frontend_tests/puppeteer_tests/message-basics.ts
@@ -154,26 +154,29 @@ async function search_silent_user(page: Page, str: string, item: string): Promis
await page.click(".search_icon");
await page.waitForSelector("#search_query", {visible: true});
await common.select_item_via_typeahead(page, "#search_query", str, item);
- await page.waitForSelector("#silent_user", {visible: true});
+ await page.waitForSelector(".empty_feed_notice", {visible: true});
const expect_message = "You haven't received any messages sent by this user yet!";
- assert.strictEqual(await common.get_text_from_selector(page, "#silent_user"), expect_message);
+ assert.strictEqual(
+ await common.get_text_from_selector(page, ".empty_feed_notice"),
+ expect_message,
+ );
await un_narrow(page);
}
async function expect_non_existing_user(page: Page): Promise {
- await page.waitForSelector("#non_existing_user", {visible: true});
+ await page.waitForSelector(".empty_feed_notice", {visible: true});
const expected_message = "This user does not exist!";
assert.strictEqual(
- await common.get_text_from_selector(page, "#non_existing_user"),
+ await common.get_text_from_selector(page, ".empty_feed_notice"),
expected_message,
);
}
async function expect_non_existing_users(page: Page): Promise {
- await page.waitForSelector("#non_existing_users", {visible: true});
+ await page.waitForSelector(".empty_feed_notice", {visible: true});
const expected_message = "One or more of these users do not exist!";
assert.strictEqual(
- await common.get_text_from_selector(page, "#non_existing_users"),
+ await common.get_text_from_selector(page, ".empty_feed_notice"),
expected_message,
);
}
diff --git a/static/js/narrow_banner.js b/static/js/narrow_banner.js
index a41978118b..5742506a33 100644
--- a/static/js/narrow_banner.js
+++ b/static/js/narrow_banner.js
@@ -1,67 +1,68 @@
import $ from "jquery";
-import _ from "lodash";
-import {$t} from "./i18n";
+import {$t, $t_html} from "./i18n";
+import {narrow_error} from "./narrow_error";
import * as narrow_state from "./narrow_state";
import {page_params} from "./page_params";
import * as people from "./people";
import * as stream_data from "./stream_data";
-function set_invalid_narrow_message(invalid_narrow_message) {
- const search_string_display = $("#empty_search_stop_words_string");
- search_string_display.text(invalid_narrow_message);
-}
-
-function show_search_query() {
- // when search bar contains multiple filters, only show search queries
+function retrieve_search_query_data() {
+ // when search bar contains multiple filters, only retrieve search queries
const current_filter = narrow_state.filter();
const search_query = current_filter.operands("search")[0];
const query_words = search_query.split(" ");
- const search_string_display = $("#empty_search_stop_words_string");
- let query_contains_stop_words = false;
-
- // Also removes previous search_string if any
- search_string_display.text($t({defaultMessage: "You searched for:"}));
+ const search_string_result = {
+ query_words: [],
+ has_stop_word: false,
+ };
// Add in stream:foo and topic:bar if present
if (current_filter.has_operator("stream") || current_filter.has_operator("topic")) {
- let stream_topic_string = "";
const stream = current_filter.operands("stream")[0];
const topic = current_filter.operands("topic")[0];
if (stream) {
- stream_topic_string = "stream: " + stream;
+ search_string_result.stream_query = stream;
}
if (topic) {
- stream_topic_string = stream_topic_string + " topic: " + topic;
+ search_string_result.topic_query = topic;
}
-
- search_string_display.append(" ");
- search_string_display.append($("").text(stream_topic_string));
}
+ // Gather information about each query word
for (const query_word of query_words) {
- search_string_display.append(" ");
-
- // if query contains stop words, it is enclosed by a tag
if (page_params.stop_words.includes(query_word)) {
- // stop_words do not need sanitization so this is unnecessary but it is fail-safe.
- search_string_display.append($("").text(query_word));
- query_contains_stop_words = true;
+ search_string_result.has_stop_word = true;
+ search_string_result.query_words.push({
+ query_word,
+ is_stop_word: true,
+ });
} else {
- // We use .text("...") to sanitize the user-given query_string.
- search_string_display.append($("").text(query_word));
+ search_string_result.query_words.push({
+ query_word,
+ is_stop_word: false,
+ });
}
}
- if (query_contains_stop_words) {
- const preamble = $t({defaultMessage: "Some common words were excluded from your search."});
- search_string_display.html(_.escape(preamble) + " " + search_string_display.html());
- }
+ return search_string_result;
}
function pick_empty_narrow_banner() {
- const default_banner = $("#empty_narrow_message");
+ const default_banner = {
+ title: $t({defaultMessage: "Nothing's been sent here yet!"}),
+ html: $t_html(
+ {
+ defaultMessage: "Why not start the conversation?",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
+ const empty_search_narrow_title = $t({defaultMessage: "No search results"});
const current_filter = narrow_state.filter();
@@ -78,57 +79,105 @@ function pick_empty_narrow_banner() {
// For invalid-multi-operator narrows, we display an invalid narrow message
const streams = current_filter.operands("stream");
- let invalid_narrow_message = "";
// No message can have multiple streams
if (streams.length > 1) {
- invalid_narrow_message = $t({
- defaultMessage:
- "You are searching for messages that belong to more than one stream, which is not possible.",
- });
+ return {
+ title: empty_search_narrow_title,
+ html: $t_html({
+ defaultMessage:
+ "
You are searching for messages that belong to more than one stream, which is not possible.
",
+ }),
+ };
}
// No message can have multiple topics
if (current_filter.operands("topic").length > 1) {
- invalid_narrow_message = $t({
- defaultMessage:
- "You are searching for messages that belong to more than one topic, which is not possible.",
- });
+ return {
+ title: empty_search_narrow_title,
+ html: $t_html({
+ defaultMessage:
+ "
You are searching for messages that belong to more than one topic, which is not possible.
",
+ }),
+ };
}
// No message can have multiple senders
if (current_filter.operands("sender").length > 1) {
- invalid_narrow_message = $t({
- defaultMessage:
- "You are searching for messages that are sent by more than one person, which is not possible.",
- });
- }
- if (invalid_narrow_message !== "") {
- set_invalid_narrow_message(invalid_narrow_message);
- return $("#empty_search_narrow_message");
+ return {
+ title: empty_search_narrow_title,
+ html: $t_html({
+ defaultMessage:
+ "
You are searching for messages that are sent by more than one person, which is not possible.
",
+ }),
+ };
}
// For empty stream searches within other narrows, we display the stop words
if (current_filter.operands("search").length > 0) {
- show_search_query();
- return $("#empty_search_narrow_message");
+ return {
+ title: empty_search_narrow_title,
+ search_data: retrieve_search_query_data(),
+ };
}
+
// For other multi-operator narrows, we just use the default banner
return default_banner;
}
+
switch (first_operator) {
case "is":
switch (first_operand) {
case "starred":
// You have no starred messages.
- return $("#empty_star_narrow_message");
+ return {
+ title: $t({defaultMessage: "You haven't starred anything yet!"}),
+ html: $t_html(
+ {
+ defaultMessage:
+ "Learn more about starring messages here.",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
case "mentioned":
- return $("#empty_narrow_all_mentioned");
+ return {
+ title: $t({defaultMessage: "You haven't been mentioned yet!"}),
+ html: $t_html(
+ {
+ defaultMessage: "Learn more about mentions here.",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
case "private":
// You have no private messages.
- return $("#empty_narrow_all_private_message");
+ return {
+ title: $t({defaultMessage: "You have no private messages yet!"}),
+ html: $t_html(
+ {
+ defaultMessage: "Why not start the conversation?",
+ },
+ {
+ // TODO: The href here is a bit weird; we probably want to migrate
+ // this to a button element down the line.
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
case "unread":
// You have no unread messages.
- return $("#no_unread_narrow_message");
+ return {
+ title: $t({defaultMessage: "You have no unread messages!"}),
+ };
case "resolved":
- return $("#empty_narrow_resolved_topics");
+ return {
+ title: $t({defaultMessage: "No topics are marked as resolved."}),
+ };
}
// fallthrough to default case if no match is found
break;
@@ -149,48 +198,131 @@ function pick_empty_narrow_banner() {
}
if (can_toggle_narrowed_stream()) {
- return $("#nonsubbed_stream_narrow_message");
+ return {
+ title: $t({
+ defaultMessage:
+ "You aren't subscribed to this stream and nobody has talked about that yet!",
+ }),
+ // TODO: Consider moving the button to be its own option in the template.
+ html: $t_html(
+ {
+ defaultMessage: "Subscribe",
+ },
+ {
+ "z-button": (content_html) =>
+ ``,
+ },
+ ),
+ };
}
- return $("#nonsubbed_private_nonexistent_stream_narrow_message");
+ return {
+ title: $t({defaultMessage: "This stream does not exist or is private."}),
+ };
}
// else fallthrough to default case
break;
- case "search":
+ case "search": {
// You are narrowed to empty search results.
- show_search_query();
- return $("#empty_search_narrow_message");
+ return {
+ title: empty_search_narrow_title,
+ search_data: retrieve_search_query_data(),
+ };
+ }
case "pm-with":
if (!people.is_valid_bulk_emails_for_compose(first_operand.split(","))) {
if (!first_operand.includes(",")) {
- return $("#non_existing_user");
+ return {
+ title: $t({defaultMessage: "This user does not exist!"}),
+ };
}
- return $("#non_existing_users");
+ return {
+ title: $t({defaultMessage: "One or more of these users do not exist!"}),
+ };
}
if (!first_operand.includes(",")) {
// You have no private messages with this person
if (people.is_current_user(first_operand)) {
- return $("#empty_narrow_self_private_message");
+ return {
+ title: $t({
+ defaultMessage:
+ "You have not sent any private messages to yourself yet!",
+ }),
+ html: $t_html(
+ {
+ defaultMessage:
+ "Why not start a conversation with yourself?",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
}
- return $("#empty_narrow_private_message");
+ return {
+ title: $t({
+ defaultMessage: "You have no private messages with this person yet!",
+ }),
+ html: $t_html(
+ {
+ defaultMessage: "Why not start the conversation?",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
}
- return $("#empty_narrow_multi_private_message");
+ return {
+ title: $t({defaultMessage: "You have no private messages with these people yet!"}),
+ html: $t_html(
+ {
+ defaultMessage: "Why not start the conversation?",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
case "sender":
if (people.get_by_email(first_operand)) {
- return $("#silent_user");
+ return {
+ title: $t({
+ defaultMessage: "You haven't received any messages sent by this user yet!",
+ }),
+ };
}
- return $("#non_existing_user");
+ return {
+ title: $t({defaultMessage: "This user does not exist!"}),
+ };
case "group-pm-with":
- return $("#empty_narrow_group_private_message");
+ return {
+ title: $t({
+ defaultMessage: "You have no group private messages with this person yet!",
+ }),
+ html: $t_html(
+ {
+ defaultMessage: "Why not start the conversation?",
+ },
+ {
+ "z-link": (content_html) =>
+ `${content_html}`,
+ },
+ ),
+ };
}
return default_banner;
}
export function show_empty_narrow_message() {
- $(".empty_feed_notice").hide();
- pick_empty_narrow_banner().show();
+ $(".empty_feed_notice_main").empty();
+ const rendered_narrow_banner = narrow_error(pick_empty_narrow_banner());
+ $(".empty_feed_notice_main").html(rendered_narrow_banner);
}
export function hide_empty_narrow_message() {
- $(".empty_feed_notice").hide();
+ $(".empty_feed_notice_main").empty();
}
diff --git a/static/js/narrow_error.js b/static/js/narrow_error.js
new file mode 100644
index 0000000000..45d715827a
--- /dev/null
+++ b/static/js/narrow_error.js
@@ -0,0 +1,10 @@
+import render_empty_feed_notice from "../templates/empty_feed_notice.hbs";
+
+export function narrow_error(narrow_banner_data) {
+ const title = narrow_banner_data.title;
+ const html = narrow_banner_data.html;
+ const search_data = narrow_banner_data.search_data;
+
+ const $empty_feed_notice = render_empty_feed_notice({title, html, search_data});
+ return $empty_feed_notice;
+}
diff --git a/static/styles/zulip.css b/static/styles/zulip.css
index a65331b564..817b9580d2 100644
--- a/static/styles/zulip.css
+++ b/static/styles/zulip.css
@@ -2439,7 +2439,6 @@ div.topic_edit_spinner .loading_indicator_spinner {
.empty_feed_notice {
padding: 3em 1em;
- display: none;
text-align: center;
}
diff --git a/static/templates/empty_feed_notice.hbs b/static/templates/empty_feed_notice.hbs
new file mode 100644
index 0000000000..ab32dcd087
--- /dev/null
+++ b/static/templates/empty_feed_notice.hbs
@@ -0,0 +1,23 @@
+
{{ _("You haven't received any messages sent by this user yet!") }}
-
-
-
{{ _("This user does not exist!") }}
-
-
-
{{ _("One or more of these users do not exist!") }}
-
-
-
{{ _("You aren't subscribed to this stream and nobody has talked about that yet!") }}
-
-
-
-
-
-
{{ _("This stream does not exist or is private.") }}
-
-
-
{{ _("You haven't starred anything yet!") }}
-
-
- {% trans %}
- Learn more about starring messages
- here.
- {% endtrans %}
-
-
-
-
{{ _("You have no unread messages!") }}
-
-
-
{{ _("You haven't been mentioned yet!") }}
-
-
- {% trans %}
- Learn more about mentions
- here.
- {% endtrans %}
-
-
-
-
{{ _('No search results') }}
-
-
-
-
{{ _("No topics are marked as resolved.") }}
-
+
diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py
index c0e783424f..c6bac88235 100644
--- a/zerver/tests/test_home.py
+++ b/zerver/tests/test_home.py
@@ -219,7 +219,7 @@ class HomeTest(ZulipTestCase):
def test_home(self) -> None:
# Keep this list sorted!!!
html_bits = [
- "start the conversation",
+ "empty_feed_notice_main",
"Loading...",
# Verify that the app styles get included
"app-stubentry.js",
@@ -446,7 +446,7 @@ class HomeTest(ZulipTestCase):
which still want the home page to load properly.
"""
html = result.content.decode()
- if "start a conversation" not in html:
+ if "empty_feed_notice_main" not in html:
raise AssertionError("Home page probably did not load.")
def test_terms_of_service(self) -> None: