FEATURE: Decorate topic-level bookmark button with reminder time (#9426)

FEATURE: Decorate topic-level bookmark button with reminder time (#9426)

  • Show the correct bookmark with clock icon when topic-level bookmark reminder time is set and show the time of the reminder in the title on hover.
  • Add a new bookmark lib and reminder time formatting function to show time with today/tomorrow shorthand for readability. E.g. tomorrow at 8:00am instead of Apr 16 2020 at 8:00am. This only applies to today + tomorrow, future dates are still treated the same.
diff --git a/app/assets/javascripts/discourse/controllers/bookmark.js b/app/assets/javascripts/discourse/controllers/bookmark.js
index 88ed8e0..39baaa0 100644
--- a/app/assets/javascripts/discourse/controllers/bookmark.js
+++ b/app/assets/javascripts/discourse/controllers/bookmark.js
@@ -7,6 +7,7 @@ import discourseComputed from "discourse-common/utils/decorators";
 import { popupAjaxError } from "discourse/lib/ajax-error";
 import { ajax } from "discourse/lib/ajax";
 import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
+import { REMINDER_TYPES } from "discourse/lib/bookmark";
 
 // global shortcuts that interfere with these modal shortcuts, they are rebound when the
 // modal is closed
@@ -20,19 +21,6 @@ const GLOBAL_SHORTCUTS_TO_PAUSE = ["c", "r", "l", "d", "t"];
 const START_OF_DAY_HOUR = 8;
 const LATER_TODAY_CUTOFF_HOUR = 17;
 const LATER_TODAY_MAX_HOUR = 18;
-const REMINDER_TYPES = {
-  AT_DESKTOP: "at_desktop",
-  LATER_TODAY: "later_today",
-  NEXT_BUSINESS_DAY: "next_business_day",
-  TOMORROW: "tomorrow",
-  NEXT_WEEK: "next_week",
-  NEXT_MONTH: "next_month",
-  CUSTOM: "custom",
-  LAST_CUSTOM: "last_custom",
-  NONE: "none",
-  START_OF_NEXT_BUSINESS_WEEK: "start_of_next_business_week",
-  LATER_THIS_WEEK: "later_this_week"
-};
 
 const BOOKMARK_BINDINGS = {
   enter: { handler: "saveAndClose" },
diff --git a/app/assets/javascripts/discourse/initializers/topic-footer-buttons.js b/app/assets/javascripts/discourse/initializers/topic-footer-buttons.js
index 3cd0827..a75949b 100644
--- a/app/assets/javascripts/discourse/initializers/topic-footer-buttons.js
+++ b/app/assets/javascripts/discourse/initializers/topic-footer-buttons.js
@@ -1,10 +1,12 @@
 import showModal from "discourse/lib/show-modal";
 import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
+import { formattedReminderTime } from "discourse/lib/bookmark";
 
 export default {
   name: "topic-footer-buttons",
 
-  initialize() {
+  initialize(container) {
+    const currentUser = container.lookup("current-user:main");
     registerTopicFooterButton({
       id: "share-and-invite",
       icon: "link",
@@ -84,7 +86,12 @@ export default {
     registerTopicFooterButton({
       dependentKeys: ["topic.bookmarked", "topic.isPrivateMessage"],
       id: "bookmark",
-      icon: "bookmark",
+      icon() {
+        if (this.get("topic.bookmark_reminder_at")) {
+          return "discourse-bookmark-clock";
+        }
+        return "bookmark";
+      },
       priority: 1000,
       classNames() {
         const bookmarked = this.get("topic.bookmarked");
@@ -94,11 +101,21 @@ export default {
         const bookmarked = this.get("topic.bookmarked");
         return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title";
       },
-      title() {
+      translatedTitle() {
         const bookmarked = this.get("topic.bookmarked");
-        return bookmarked
-          ? "bookmarked.help.unbookmark"
-          : "bookmarked.help.bookmark";
+        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()
+              )
+            });
+          }
+          return I18n.t("bookmarked.help.unbookmark");
+        }
+        return I18n.t("bookmarked.help.bookmark");
       },
       action: "toggleBookmark",
       dropdown() {
diff --git a/app/assets/javascripts/discourse/lib/bookmark.js b/app/assets/javascripts/discourse/lib/bookmark.js
new file mode 100644
index 0000000..8874034
--- /dev/null
+++ b/app/assets/javascripts/discourse/lib/bookmark.js
@@ -0,0 +1,31 @@
+export function formattedReminderTime(reminderAt, timezone) {
+  let reminderAtDate = moment.tz(reminderAt, timezone);
+  let formatted = reminderAtDate.format(I18n.t("dates.time"));
+  let now = moment.tz(timezone);
+  let tomorrow = moment(now).add(1, "day");
+
+  if (reminderAtDate.isSame(tomorrow, "date")) {
+    return I18n.t("bookmarks.reminders.tomorrow_with_time", {
+      time: formatted
+    });
+  } else if (reminderAtDate.isSame(now, "date")) {
+    return I18n.t("bookmarks.reminders.today_with_time", { time: formatted });
+  }
+  return I18n.t("bookmarks.reminders.at_time", {
+    date_time: reminderAtDate.format(I18n.t("dates.long_with_year"))
+  });
+}
+
+export const REMINDER_TYPES = {
+  AT_DESKTOP: "at_desktop",
+  LATER_TODAY: "later_today",
+  NEXT_BUSINESS_DAY: "next_business_day",
+  TOMORROW: "tomorrow",
+  NEXT_WEEK: "next_week",
+  NEXT_MONTH: "next_month",
+  CUSTOM: "custom",
+  LAST_CUSTOM: "last_custom",
+  NONE: "none",
+  START_OF_NEXT_BUSINESS_WEEK: "start_of_next_business_week",
+  LATER_THIS_WEEK: "later_this_week"
+};
diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js
index c783fe8..ce951d6 100644
--- a/app/assets/javascripts/discourse/models/topic.js
+++ b/app/assets/javascripts/discourse/models/topic.js
@@ -401,6 +401,7 @@ const Topic = RestModel.extend({
     if (firstPost) {
       firstPost.set("bookmarked", true);
       if (this.siteSettings.enable_bookmarks_with_reminders) {
+        this.set("bookmark_reminder_at", firstPost.bookmark_reminder_at);
         firstPost.set("bookmarked_with_reminder", true);
       }
       return [firstPost.id];
@@ -440,7 +441,7 @@ const Topic = RestModel.extend({
           if (this.siteSettings.enable_bookmarks_with_reminders) {
             return firstPost.toggleBookmarkWithReminder().then(response => {
               this.set("bookmarking", false);
-              if (response.closedWithoutSaving) {
+              if (response && response.closedWithoutSaving) {
                 this.set("bookmarked", false);
               } else {
                 return this.afterTopicBookmarked(firstPost);
@@ -459,6 +460,7 @@ const Topic = RestModel.extend({
           return ajax(`/t/${this.id}/remove_bookmarks`, { type: "PUT" })
             .then(() => {
               this.toggleProperty("bookmarked");
+              this.set("bookmark_reminder_at", null);
               if (posts) {
                 const updated = [];
                 posts.forEach(post => {
@@ -474,6 +476,7 @@ const Topic = RestModel.extend({
                     updated.push(post.id);
                   }
                 });
+                firstPost.set("bookmarked_with_reminder", false);
                 return updated;
               }
             })
diff --git a/app/assets/javascripts/discourse/widgets/post-menu.js b/app/assets/javascripts/discourse/widgets/post-menu.js
index c21d60f..c837405 100644
--- a/app/assets/javascripts/discourse/widgets/post-menu.js
+++ b/app/assets/javascripts/discourse/widgets/post-menu.js
@@ -5,6 +5,7 @@ import { h } from "virtual-dom";
 import showModal from "discourse/lib/show-modal";
 import { Promise } from "rsvp";
 import ENV from "discourse-common/config/environment";
+import { formattedReminderTime } from "discourse/lib/bookmark";
 
 const LIKE_ACTION = 2;
 const VIBRATE_DURATION = 5;
@@ -314,12 +315,13 @@ registerButton("bookmarkWithReminder", (attrs, state, siteSettings) => {
     classNames.push("bookmarked");
 
     if (attrs.bookmarkReminderAt) {
-      let reminderAtDate = moment(attrs.bookmarkReminderAt).tz(
+      let formattedReminder = formattedReminderTime(
+        attrs.bookmarkReminderAt,
         Discourse.currentUser.resolvedTimezone()
       );
       title = "bookmarks.created_with_reminder";
       titleOptions = {
-        date: reminderAtDate.format(I18n.t("dates.long_with_year"))
+        date: formattedReminder
       };
     } else if (attrs.bookmarkReminderType === "at_desktop") {
       title = "bookmarks.created_with_at_desktop_reminder";

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

GitHub sha: d7f74449

This commit appears in #9426 which was approved by eviltrout. It was merged by martin.