FEATURE: improve "blank page syndrome" on the user bookmarks page

FEATURE: improve “blank page syndrome” on the user bookmarks page

diff --git a/app/assets/javascripts/discourse/app/controllers/user-activity-bookmarks.js b/app/assets/javascripts/discourse/app/controllers/user-activity-bookmarks.js
index 6733610..8df706e 100644
--- a/app/assets/javascripts/discourse/app/controllers/user-activity-bookmarks.js
+++ b/app/assets/javascripts/discourse/app/controllers/user-activity-bookmarks.js
@@ -1,27 +1,30 @@
 import Controller, { inject as controller } from "@ember/controller";
+import { iconHTML } from "discourse-common/lib/icon-library";
 import Bookmark from "discourse/models/bookmark";
 import I18n from "I18n";
 import { Promise } from "rsvp";
 import EmberObject, { action } from "@ember/object";
 import discourseComputed from "discourse-common/utils/decorators";
+import { notEmpty } from "@ember/object/computed";
 
 export default Controller.extend({
+  queryParams: ["q"],
+
   application: controller(),
   user: controller(),
 
   content: null,
   loading: false,
-  noResultsHelp: null,
+  permissionDenied: false,
   searchTerm: null,
   q: null,
-
-  queryParams: ["q"],
+  inSearchMode: notEmpty("q"),
 
   loadItems() {
     this.setProperties({
       content: [],
       loading: true,
-      noResultsHelp: null,
+      permissionDenied: false,
     });
 
     if (this.q && !this.searchTerm) {
@@ -40,22 +43,28 @@ export default Controller.extend({
       });
   },
 
+  @discourseComputed()
+  emptyStateBody() {
+    return I18n.t("user.no_bookmarks_body", {
+      icon: iconHTML("bookmark"),
+    }).htmlSafe();
+  },
+
+  @discourseComputed("inSearchMode", "noContent")
+  userDoesNotHaveBookmarks(inSearchMode, noContent) {
+    return !inSearchMode && noContent;
+  },
+
+  @discourseComputed("inSearchMode", "noContent")
+  nothingFound(inSearchMode, noContent) {
+    return inSearchMode && noContent;
+  },
+
   @discourseComputed("loaded", "content.length")
   noContent(loaded, contentLength) {
     return loaded && contentLength === 0;
   },
 
-  @discourseComputed("noResultsHelp", "noContent")
-  noResultsHelpMessage(noResultsHelp, noContent) {
-    if (noResultsHelp) {
-      return noResultsHelp;
-    }
-    if (noContent) {
-      return I18n.t("bookmarks.no_user_bookmarks");
-    }
-    return "";
-  },
-
   @action
   search() {
     this.set("q", this.searchTerm);
@@ -83,16 +92,11 @@ export default Controller.extend({
   },
 
   _bookmarksListDenied() {
-    this.set("noResultsHelp", I18n.t("bookmarks.list_permission_denied"));
+    this.set("permissionDenied", true);
   },
 
   _processLoadResponse(response) {
-    if (!response) {
-      return;
-    }
-
-    if (response.no_results_help) {
-      this.set("noResultsHelp", response.no_results_help);
+    if (!response || !response.user_bookmark_list) {
       return;
     }
 
diff --git a/app/assets/javascripts/discourse/app/templates/user/bookmarks.hbs b/app/assets/javascripts/discourse/app/templates/user/bookmarks.hbs
index 30dad54..fc689b8 100644
--- a/app/assets/javascripts/discourse/app/templates/user/bookmarks.hbs
+++ b/app/assets/javascripts/discourse/app/templates/user/bookmarks.hbs
@@ -1,22 +1,34 @@
-<div class="form-horizontal bookmark-search-form">
-  {{input type="text"
-          value=searchTerm
-          placeholder=(i18n "bookmarks.search_placeholder")
-          enter=(action "search")
-          id="bookmark-search" autocomplete="discourse"}}
-  {{d-button
-      class="btn-primary"
-      action=(action "search")
-      type="button"
-      icon="search"}}
-</div>
-{{#if noContent}}
-  <div class="alert alert-info">{{noResultsHelpMessage}}</div>
-{{else}}
-  {{bookmark-list
-    loadMore=(action "loadMore")
-    reload=(action "reload")
-    loading=loading
-    loadingMore=loadingMore
-    content=content}}
-{{/if}}
+{{#conditional-loading-spinner condition=loading}}
+  {{#if permissionDenied}}
+    <div class="alert alert-info">{{i18n "bookmarks.list_permission_denied"}}</div>
+  {{else if userDoesNotHaveBookmarks}}
+    <div class="empty-state">
+      <span class="empty-state-title">{{i18n "user.no_bookmarks_title"}}</span>
+      <div class="empty-state-body">
+        <p>{{emptyStateBody}}</p>
+      </div>
+    </div>
+  {{else}}
+    <div class="form-horizontal bookmark-search-form">
+      {{input type="text"
+        value=searchTerm
+        placeholder=(i18n "bookmarks.search_placeholder")
+        enter=(action "search")
+        id="bookmark-search" autocomplete="discourse"}}
+      {{d-button
+        class="btn-primary"
+        action=(action "search")
+        type="button"
+        icon="search"}}
+    </div>
+    {{#if nothingFound}}
+      <div class="alert alert-info">{{i18n "user.no_bookmarks_search"}}</div>
+    {{else}}
+      {{bookmark-list
+        loadMore=(action "loadMore")
+        reload=(action "reload")
+        loadingMore=loadingMore
+        content=content}}
+    {{/if}}
+  {{/if}}
+{{/conditional-loading-spinner}}
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
index 3664653..a0fd707 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
@@ -1,8 +1,4 @@
-import {
-  acceptance,
-  exists,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import { cloneJSON } from "discourse-common/lib/object";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -22,6 +18,11 @@ acceptance("User's bookmarks", function (needs) {
 
     assert.not(exists(".bootbox.modal"), "it should not show the modal");
   });
+
+  test("it renders search controls if there are bookmarks", async function (assert) {
+    await visit("/u/eviltrout/activity/bookmarks");
+    assert.ok(exists("div.bookmark-search-form"));
+  });
 });
 
 acceptance("User's bookmarks - reminder", function (needs) {
@@ -55,13 +56,16 @@ acceptance("User's bookmarks - no bookmarks", function (needs) {
     server.get("/u/eviltrout/bookmarks.json", () =>
       helper.response({
         bookmarks: [],
-        no_results_help: "no bookmarks",
       })
     );
   });
 
   test("listing users bookmarks - no bookmarks", async function (assert) {
     await visit("/u/eviltrout/activity/bookmarks");
-    assert.equal(queryAll(".alert.alert-info").text(), "no bookmarks");
+    assert.notOk(
+      exists("div.bookmark-search-form"),
+      "does not render search controls"
+    );
+    assert.ok(exists("div.empty-state", "renders the empty-state message"));
   });
 });
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 9f24c2f..d87a6ec 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1067,6 +1067,7 @@ en:
       no_bookmarks_title: "You haven’t bookmarked anything yet"
       no_bookmarks_body: >
         Start bookmarking posts with the %{icon} button and they will be listed here for easy reference. You can schedule a reminder too!
+      no_bookmarks_search: "No bookmarks found with the provided search query."
       no_notifications_title: "You don’t have any notifications yet"
       no_notifications_body: >
         You will be notified in this panel about activity directly relevant to you, including replies to your topics and posts, when someone <b>@mentions</b> you or quotes you, and replies to topics you are watching. Notifications will also be sent to your email when you haven’t logged in for a while.

GitHub sha: d1781e4c7df28f4f32b7cc9a296369d2bf43e006

This commit appears in #14093 which was approved by eviltrout. It was merged by AndrewPrigorshnev.