FIX: topic level bookmark button (#13530)

FIX: topic level bookmark button (#13530)

We changed (https://github.com/discourse/discourse/pull/13407) behaviour of the topic level bookmark button recently. That PR made the button be opening the edit bookmark modal when there is only one bookmark on the topic instead of just removing that bookmark as it was before.

This PR fixes the next problems that weren’t taken into account in the previous PR:

  1. Everything should work fine even on very big topics when a bookmarked post is unloaded from the post stream. I’ve added code that loads the post we need and makes everything work as expected
  2. When at least one bookmark on the topic has a reminder, we should always be showing the icon with a clock on the topic level bookmark button
  3. We should show correct tooltips for the topic level bookmark button
diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js
index f8427f5..c27f4ba 100644
--- a/app/assets/javascripts/discourse/app/controllers/topic.js
+++ b/app/assets/javascripts/discourse/app/controllers/topic.js
@@ -1204,75 +1204,89 @@ export default Controller.extend(bufferedProperty("model"), {
           post.appEvents.trigger("post-stream:refresh", { id: post.id });
         },
         afterSave: (savedData) => {
+          this._addOrUpdateBookmarkedPost(post.id, savedData.reminderAt);
           post.createBookmark(savedData);
           resolve({ closedWithoutSaving: false });
         },
         afterDelete: (topicBookmarked) => {
+          this.model.set(
+            "bookmarked_posts",
+            this.model.bookmarked_posts.filter((x) => x.post_id !== post.id)
+          );
           post.deleteBookmark(topicBookmarked);
         },
       });
     });
   },
 
+  _addOrUpdateBookmarkedPost(postId, reminderAt) {
+    if (!this.model.bookmarked_posts) {
+      this.model.set("bookmarked_posts", []);
+    }
+
+    let bookmarkedPost = this.model.bookmarked_posts.findBy("post_id", postId);
+    if (!bookmarkedPost) {
+      bookmarkedPost = { post_id: postId };
+      this.model.bookmarked_posts.pushObject(bookmarkedPost);
+    }
+
+    bookmarkedPost.reminder_at = reminderAt;
+  },
+
   _toggleTopicBookmark() {
     if (this.model.bookmarking) {
       return Promise.resolve();
     }
     this.model.set("bookmarking", true);
-    const alreadyBookmarkedPosts = this.model.bookmarkedPosts;
-
-    return this.model.firstPost().then((firstPost) => {
-      const bookmarkPost = async (post) => {
-        const opts = await this._togglePostBookmark(post);
-        this.model.set("bookmarking", false);
-        if (opts.closedWithoutSaving) {
-          return;
-        }
-        this.model.afterPostBookmarked(post);
-        return [post.id];
-      };
-
-      const toggleBookmarkOnServer = () => {
-        if (alreadyBookmarkedPosts.length === 0) {
-          return bookmarkPost(firstPost);
-        } else if (alreadyBookmarkedPosts.length === 1) {
-          const post = alreadyBookmarkedPosts[0];
-          return bookmarkPost(post);
-        } else {
-          return this.model
-            .deleteBookmark()
-            .then(() => {
-              this.model.toggleProperty("bookmarked");
-              this.model.set("bookmark_reminder_at", null);
-              alreadyBookmarkedPosts.forEach((post) => {
-                post.clearBookmark();
-              });
-              return alreadyBookmarkedPosts.mapBy("id");
-            })
-            .catch(popupAjaxError)
-            .finally(() => this.model.set("bookmarking", false));
-        }
-      };
+    const bookmarkedPostsCount = this.model.bookmarked_posts
+      ? this.model.bookmarked_posts.length
+      : 0;
+
+    const bookmarkPost = async (post) => {
+      const opts = await this._togglePostBookmark(post);
+      this.model.set("bookmarking", false);
+      if (opts.closedWithoutSaving) {
+        return;
+      }
+      this.model.afterPostBookmarked(post);
+      return [post.id];
+    };
+
+    const toggleBookmarkOnServer = async () => {
+      if (bookmarkedPostsCount === 0) {
+        const firstPost = await this.model.firstPost();
+        return bookmarkPost(firstPost);
+      } else if (bookmarkedPostsCount === 1) {
+        const postId = this.model.bookmarked_posts[0].post_id;
+        const post = await this.model.postById(postId);
+        return bookmarkPost(post);
+      } else {
+        return this.model
+          .deleteBookmarks()
+          .then(() => this.model.clearBookmarks())
+          .catch(popupAjaxError)
+          .finally(() => this.model.set("bookmarking", false));
+      }
+    };
 
-      return new Promise((resolve) => {
-        if (alreadyBookmarkedPosts.length > 1) {
-          bootbox.confirm(
-            I18n.t("bookmarks.confirm_clear"),
-            I18n.t("no_value"),
-            I18n.t("yes_value"),
-            (confirmed) => {
-              if (confirmed) {
-                toggleBookmarkOnServer().then(resolve);
-              } else {
-                this.model.set("bookmarking", false);
-                resolve();
-              }
+    return new Promise((resolve) => {
+      if (bookmarkedPostsCount > 1) {
+        bootbox.confirm(
+          I18n.t("bookmarks.confirm_clear"),
+          I18n.t("no_value"),
+          I18n.t("yes_value"),
+          (confirmed) => {
+            if (confirmed) {
+              toggleBookmarkOnServer().then(resolve);
+            } else {
+              this.model.set("bookmarking", false);
+              resolve();
             }
-          );
-        } else {
-          toggleBookmarkOnServer().then(resolve);
-        }
-      });
+          }
+        );
+      } else {
+        toggleBookmarkOnServer().then(resolve);
+      }
     });
   },
 
diff --git a/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js b/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js
index d2c365b..344b4b0 100644
--- a/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js
+++ b/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js
@@ -1,5 +1,4 @@
 import I18n from "I18n";
-import { formattedReminderTime } from "discourse/lib/bookmark";
 import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
 import showModal from "discourse/lib/show-modal";
 
@@ -12,8 +11,7 @@ const DEFER_PRIORITY = 500;
 export default {
   name: "topic-footer-buttons",
 
-  initialize(container) {
-    const currentUser = container.lookup("current-user:main");
+  initialize() {
     registerTopicFooterButton({
       id: "share-and-invite",
       icon: "link",
@@ -66,22 +64,27 @@ export default {
     });
 
     registerTopicFooterButton({
-      dependentKeys: ["topic.bookmarked", "topic.bookmarkedPosts"],
+      dependentKeys: ["topic.bookmarked", "topic.bookmarksWereChanged"],
       id: "bookmark",
       icon() {
-        if (this.get("topic.bookmark_reminder_at")) {
+        const bookmarkedPosts = this.topic.bookmarked_posts;
+        if (bookmarkedPosts && bookmarkedPosts.find((x) => x.reminder_at)) {
           return "discourse-bookmark-clock";
         }
         return "bookmark";
       },
       priority: BOOKMARK_PRIORITY,
       classNames() {
-        const bookmarked = this.get("topic.bookmarked");
-        return bookmarked ? ["bookmark", "bookmarked"] : ["bookmark"];
+        return this.topic.bookmarked
+          ? ["bookmark", "bookmarked"]
+          : ["bookmark"];
       },
       label() {
-        if (!this.get("topic.isPrivateMessage") || this.site.mobileView) {
-          const bookmarkedPostsCount = this.get("topic.bookmarkedPosts").length;
+        if (!this.topic.isPrivateMessage || this.site.mobileView) {
+          const bookmarkedPosts = this.topic.bookmarked_posts;
+          const bookmarkedPostsCount = bookmarkedPosts
+            ? bookmarkedPosts.length
+            : 0;
 
           if (bookmarkedPostsCount === 0) {
             return "bookmarked.title";
@@ -93,20 +96,16 @@ export default {
         }
       },
       translatedTitle() {
-        const bookmarked = this.get("topic.bookmarked");
-        const bookmark_reminder_at = this.get("topic.bookmark_reminder_at");
-        if (bookmarked) {
-          if (bookmark_reminder_at) {
-            return I18n.t("bookmarked.help.unbookmark_with_reminder", {
-              reminder_at: formattedReminderTime(
-                bookmark_reminder_at,
-                currentUser.resolvedTimezone(currentUser)
-              ),
-            });
-          }
+        const bookmarkedPosts = this.topic.bookmarked_posts;
+        if (!bookmarkedPosts || bookmarkedPosts.length === 0) {
+          return I18n.t("bookmarked.help.bookmark");

[... diff too long, it was truncated ...]

GitHub sha: 6be4699954a0cc2f89e3eb9db4d794468877b4d7

This commit appears in #13530 which was approved by eviltrout and ZogStriP. It was merged by AndrewPrigorshnev.