FEATURE: Add "delete on owner reply" bookmark functionality (#10231)

FEATURE: Add “delete on owner reply” bookmark functionality (#10231)

This adds an option to “delete on owner reply” to bookmarks. If you select this option in the modal, then reply to the topic the bookmark is in, the bookmark will be deleted on reply.

This PR also changes the checkboxes for these additional bookmark options to an Integer column in the DB with a combobox to select the option you want.

The use cases are:

  • Sometimes I will bookmark the topics to read it later. In this case we definitely don’t need to keep the bookmark after I replied to it.
  • Sometimes I will read the topic in mobile and I will prefer to reply in PC later. Or I may have to do some research before reply. So I will bookmark it for reply later.
diff --git a/app/assets/javascripts/discourse/app/controllers/bookmark.js b/app/assets/javascripts/discourse/app/controllers/bookmark.js
index c2a6b55..6d4b5f9 100644
--- a/app/assets/javascripts/discourse/app/controllers/bookmark.js
+++ b/app/assets/javascripts/discourse/app/controllers/bookmark.js
@@ -27,6 +27,20 @@ const LATER_TODAY_CUTOFF_HOUR = 17;
 const LATER_TODAY_MAX_HOUR = 18;
 const MOMENT_MONDAY = 1;
 const MOMENT_THURSDAY = 4;
+const AUTO_DELETE_PREFERENCES = [
+  {
+    id: 0,
+    name: I18n.t("bookmarks.auto_delete_preference.never")
+  },
+  {
+    id: 1,
+    name: I18n.t("bookmarks.auto_delete_preference.when_reminder_sent")
+  },
+  {
+    id: 2,
+    name: I18n.t("bookmarks.auto_delete_preference.on_owner_reply")
+  }
+];
 
 const BOOKMARK_BINDINGS = {
   enter: { handler: "saveAndClose" },
@@ -65,7 +79,6 @@ export default Controller.extend(ModalFunctionality, {
   mouseTrap: null,
   userTimezone: null,
   showOptions: false,
-  options: null,
 
   onShow() {
     this.setProperties({
@@ -79,7 +92,6 @@ export default Controller.extend(ModalFunctionality, {
       lastCustomReminderTime: null,
       userTimezone: this.currentUser.resolvedTimezone(this.currentUser),
       showOptions: false,
-      options: {},
       model: this.model || {}
     });
 
@@ -143,19 +155,26 @@ export default Controller.extend(ModalFunctionality, {
 
   _loadBookmarkOptions() {
     this.set(
-      "options.deleteWhenReminderSent",
-      this.model.deleteWhenReminderSent ||
-        localStorage.bookmarkOptionsDeleteWhenReminderSent === "true"
+      "autoDeletePreference",
+      this.model.autoDeletePreference || this._preferredDeleteOption() || 0
     );
 
     // we want to make sure the options panel opens so the user
     // knows they have set these options previously. run next otherwise
     // the modal is not visible when it tries to slide down the options
-    if (this.options.deleteWhenReminderSent) {
+    if (this.autoDeletePreference) {
       next(() => this.toggleOptionsPanel());
     }
   },
 
+  _preferredDeleteOption() {
+    let preferred = localStorage.bookmarkDeleteOption;
+    if (preferred && preferred !== "") {
+      preferred = parseInt(preferred, 10);
+    }
+    return preferred;
+  },
+
   _loadLastUsedCustomReminderDatetime() {
     let lastTime = localStorage.lastCustomBookmarkReminderTime;
     let lastDate = localStorage.lastCustomBookmarkReminderDate;
@@ -217,6 +236,11 @@ export default Controller.extend(ModalFunctionality, {
     return REMINDER_TYPES;
   },
 
+  @discourseComputed()
+  autoDeletePreferences: () => {
+    return AUTO_DELETE_PREFERENCES;
+  },
+
   showLastCustom: and("lastCustomReminderTime", "lastCustomReminderDate"),
 
   get showLaterToday() {
@@ -294,7 +318,7 @@ export default Controller.extend(ModalFunctionality, {
       localStorage.lastCustomBookmarkReminderDate = this.customReminderDate;
     }
 
-    localStorage.bookmarkOptionsDeleteWhenReminderSent = this.options.deleteWhenReminderSent;
+    localStorage.bookmarkDeleteOption = this.autoDeletePreference;
 
     let reminderType;
     if (this.selectedReminderType === REMINDER_TYPES.NONE) {
@@ -311,7 +335,7 @@ export default Controller.extend(ModalFunctionality, {
       name: this.model.name,
       post_id: this.model.postId,
       id: this.model.id,
-      delete_when_reminder_sent: this.options.deleteWhenReminderSent
+      auto_delete_preference: this.autoDeletePreference
     };
 
     if (this._editingExistingBookmark()) {
@@ -323,7 +347,7 @@ export default Controller.extend(ModalFunctionality, {
           this.afterSave({
             reminderAt: reminderAtISO,
             reminderType: this.selectedReminderType,
-            deleteWhenReminderSent: this.options.deleteWhenReminderSent,
+            autoDeletePreference: this.autoDeletePreference,
             id: this.model.id,
             name: this.model.name
           });
@@ -335,7 +359,7 @@ export default Controller.extend(ModalFunctionality, {
           this.afterSave({
             reminderAt: reminderAtISO,
             reminderType: this.selectedReminderType,
-            deleteWhenReminderSent: this.options.deleteWhenReminderSent,
+            autoDeletePreference: this.autoDeletePreference,
             id: response.id,
             name: this.model.name
           });
diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js
index 54e69b2..4845a00 100644
--- a/app/assets/javascripts/discourse/app/controllers/topic.js
+++ b/app/assets/javascripts/discourse/app/controllers/topic.js
@@ -110,6 +110,10 @@ export default Controller.extend(bufferedProperty("model"), {
     this._super(...arguments);
 
     this.appEvents.on("post:show-revision", this, "_showRevision");
+    this.appEvents.on("post:created", this, () => {
+      this._removeDeleteOnOwnerReplyBookmarks();
+      this.appEvents.trigger("post-stream:refresh", { force: true });
+    });
 
     this.setProperties({
       selectedPostIds: [],
@@ -183,6 +187,15 @@ export default Controller.extend(bufferedProperty("model"), {
       : null;
   },
 
+  _removeDeleteOnOwnerReplyBookmarks() {
+    let posts = this.model.get("postStream").posts;
+    posts
+      .filter(p => p.bookmarked && p.bookmark_auto_delete_preference === 2) // 2 is on_owner_reply
+      .forEach(p => {
+        p.clearBookmark();
+      });
+  },
+
   _forceRefreshPostStream() {
     this.appEvents.trigger("post-stream:refresh", { force: true });
   },
diff --git a/app/assets/javascripts/discourse/app/models/post.js b/app/assets/javascripts/discourse/app/models/post.js
index 18bc421..f181eae 100644
--- a/app/assets/javascripts/discourse/app/models/post.js
+++ b/app/assets/javascripts/discourse/app/models/post.js
@@ -305,7 +305,7 @@ const Post = RestModel.extend({
           postId: this.id,
           id: this.bookmark_id,
           reminderAt: this.bookmark_reminder_at,
-          deleteWhenReminderSent: this.bookmark_delete_when_reminder_sent,
+          autoDeletePreference: this.bookmark_auto_delete_preference,
           name: this.bookmark_name
         },
         title: this.bookmark_id
@@ -324,8 +324,7 @@ const Post = RestModel.extend({
             bookmarked: true,
             bookmark_reminder_at: savedData.reminderAt,
             bookmark_reminder_type: savedData.reminderType,
-            bookmark_delete_when_reminder_sent:
-              savedData.deleteWhenReminderSent,
+            bookmark_auto_delete_preference: savedData.autoDeletePreference,
             bookmark_name: savedData.name,
             bookmark_id: savedData.id
           });
@@ -334,19 +333,24 @@ const Post = RestModel.extend({
         },
         afterDelete: topicBookmarked => {
           this.set("topic.bookmarked", topicBookmarked);
-          this.setProperties({
-            bookmark_reminder_at: null,
-            bookmark_reminder_type: null,
-            bookmark_name: null,
-            bookmark_id: null,
-            bookmarked: false
-          });
+          this.clearBookmark();
           this.appEvents.trigger("page:bookmark-post-toggled", this);
         }
       });
     });
   },
 
+  clearBookmark() {
+    this.setProperties({
+      bookmark_reminder_at: null,
+      bookmark_reminder_type: null,
+      bookmark_name: null,
+      bookmark_id: null,
+      bookmarked: false,
+      bookmark_auto_delete_preference: null
+    });
+  },
+
   updateActionsSummary(json) {
     if (json && json.id === this.id) {
       json = Post.munge(json);
diff --git a/app/assets/javascripts/discourse/app/routes/topic-from-params.js b/app/assets/javascripts/discourse/app/routes/topic-from-params.js
index 1d70023..c592b85 100644
--- a/app/assets/javascripts/discourse/app/routes/topic-from-params.js
+++ b/app/assets/javascripts/discourse/app/routes/topic-from-params.js
@@ -73,12 +73,7 @@ export default DiscourseRoute.extend({

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

GitHub sha: 41b43a2a

This commit appears in #10231 which was merged by martin.

These I18n strings will be evaluated when the file is parsed which is not ideal. (For example, we might want to change the language based on some application logic).

I would recommend storing the I18n key here instead and then have the I18n translation execute when rendered.

1 Like

Can we make this a proper const somewhere?

1 Like

@eviltrout fixed in FIX: Move consts and translations for bookmark auto delete prefs (#10… · discourse/discourse@e027acd · GitHub