From 6d422a8033fb31821203f2725a7fb667ef031e65 Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Tue, 8 Mar 2022 19:44:18 +0200 Subject: [PATCH] FEATURE: Highlight expired bookmark reminders (#15317) The user can select what happens with a bookamrk after it expires. New option allow bookmark's reminder to be kept even after it has expired. After a bookmark's reminder notification is created, the reminder date will be highlighted in red until the user resets the reminder date. User can do that using the new Clear Reminder button from the dropdown. --- .../components/bookmark-actions-dropdown.js | 64 ++++++++++++------- .../discourse/app/components/bookmark-list.js | 11 ++++ .../discourse/app/controllers/topic.js | 4 ++ .../discourse/app/models/bookmark.js | 6 ++ .../javascripts/discourse/app/models/user.js | 1 + .../templates/components/bookmark-list.hbs | 3 +- .../tests/acceptance/user-bookmarks-test.js | 16 +++++ .../common/components/bookmark-list.scss | 4 ++ .../bookmark_reminder_notifications.rb | 2 +- app/models/bookmark.rb | 16 ++--- app/models/user_option.rb | 1 + app/serializers/current_user_serializer.rb | 5 ++ config/locales/client.en.yml | 12 ++-- ...k_auto_delete_preference_to_user_option.rb | 7 ++ lib/bookmark_manager.rb | 15 ++++- lib/bookmark_reminder_notification_handler.rb | 4 ++ .../bookmark_reminder_notifications_spec.rb | 11 ++-- spec/lib/bookmark_manager_spec.rb | 24 +++++-- ...mark_reminder_notification_handler_spec.rb | 31 +++++---- 19 files changed, 173 insertions(+), 64 deletions(-) create mode 100644 db/migrate/20220209210449_add_bookmark_auto_delete_preference_to_user_option.rb diff --git a/app/assets/javascripts/discourse/app/components/bookmark-actions-dropdown.js b/app/assets/javascripts/discourse/app/components/bookmark-actions-dropdown.js index e039f4d5aea..f20f9ddc1e4 100644 --- a/app/assets/javascripts/discourse/app/components/bookmark-actions-dropdown.js +++ b/app/assets/javascripts/discourse/app/components/bookmark-actions-dropdown.js @@ -5,6 +5,7 @@ import I18n from "I18n"; const ACTION_REMOVE = "remove"; const ACTION_EDIT = "edit"; +const ACTION_CLEAR_REMINDER = "clear_reminder"; const ACTION_PIN = "pin"; export default DropdownSelectBoxComponent.extend({ @@ -18,32 +19,45 @@ export default DropdownSelectBoxComponent.extend({ @discourseComputed("bookmark") content(bookmark) { - return [ - { - id: ACTION_REMOVE, - icon: "trash-alt", - name: I18n.t("post.bookmarks.actions.delete_bookmark.name"), + const actions = []; + + actions.push({ + id: ACTION_REMOVE, + icon: "trash-alt", + name: I18n.t("post.bookmarks.actions.delete_bookmark.name"), + description: I18n.t("post.bookmarks.actions.delete_bookmark.description"), + }); + + actions.push({ + id: ACTION_EDIT, + icon: "pencil-alt", + name: I18n.t("post.bookmarks.actions.edit_bookmark.name"), + description: I18n.t("post.bookmarks.actions.edit_bookmark.description"), + }); + + if (bookmark.reminder_at) { + actions.push({ + id: ACTION_CLEAR_REMINDER, + icon: "history", + name: I18n.t("post.bookmarks.actions.clear_bookmark_reminder.name"), description: I18n.t( - "post.bookmarks.actions.delete_bookmark.description" + "post.bookmarks.actions.clear_bookmark_reminder.description" ), - }, - { - id: ACTION_EDIT, - icon: "pencil-alt", - name: I18n.t("post.bookmarks.actions.edit_bookmark.name"), - description: I18n.t("post.bookmarks.actions.edit_bookmark.description"), - }, - { - id: ACTION_PIN, - icon: "thumbtack", - name: I18n.t( - `post.bookmarks.actions.${bookmark.pinAction()}_bookmark.name` - ), - description: I18n.t( - `post.bookmarks.actions.${bookmark.pinAction()}_bookmark.description` - ), - }, - ]; + }); + } + + actions.push({ + id: ACTION_PIN, + icon: "thumbtack", + name: I18n.t( + `post.bookmarks.actions.${bookmark.pinAction()}_bookmark.name` + ), + description: I18n.t( + `post.bookmarks.actions.${bookmark.pinAction()}_bookmark.description` + ), + }); + + return actions; }, @action @@ -52,6 +66,8 @@ export default DropdownSelectBoxComponent.extend({ this.removeBookmark(this.bookmark); } else if (selectedAction === ACTION_EDIT) { this.editBookmark(this.bookmark); + } else if (selectedAction === ACTION_CLEAR_REMINDER) { + this.clearBookmarkReminder(this.bookmark); } else if (selectedAction === ACTION_PIN) { this.togglePinBookmark(this.bookmark); } diff --git a/app/assets/javascripts/discourse/app/components/bookmark-list.js b/app/assets/javascripts/discourse/app/components/bookmark-list.js index cafebfb4ec5..066a9019cde 100644 --- a/app/assets/javascripts/discourse/app/components/bookmark-list.js +++ b/app/assets/javascripts/discourse/app/components/bookmark-list.js @@ -4,6 +4,7 @@ import { schedule } from "@ember/runloop"; import bootbox from "bootbox"; import discourseDebounce from "discourse-common/lib/debounce"; import { openBookmarkModal } from "discourse/controllers/bookmark"; +import { ajax } from "discourse/lib/ajax"; import { openLinkInNewTab, shouldOpenInNewTab, @@ -107,6 +108,16 @@ export default Component.extend(Scrolling, { }); }, + @action + clearBookmarkReminder(bookmark) { + return ajax(`/bookmarks/${bookmark.id}`, { + type: "PUT", + data: { reminder_at: null }, + }).then(() => { + bookmark.set("reminder_at", null); + }); + }, + @action togglePinBookmark(bookmark) { bookmark.togglePin().then(this.reload); diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js index 3354a0cfaaa..1398450d050 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic.js +++ b/app/assets/javascripts/discourse/app/controllers/topic.js @@ -767,6 +767,8 @@ export default Controller.extend(bufferedProperty("model"), { post_id: post.id, topic_id: post.topic_id, for_topic: false, + auto_delete_preference: this.currentUser + .bookmark_auto_delete_preference, }), post ); @@ -1320,6 +1322,8 @@ export default Controller.extend(bufferedProperty("model"), { post_id: firstPost.id, topic_id: this.model.id, for_topic: true, + auto_delete_preference: this.currentUser + .bookmark_auto_delete_preference, }) ); } diff --git a/app/assets/javascripts/discourse/app/models/bookmark.js b/app/assets/javascripts/discourse/app/models/bookmark.js index bbe656d63b2..8852409a773 100644 --- a/app/assets/javascripts/discourse/app/models/bookmark.js +++ b/app/assets/javascripts/discourse/app/models/bookmark.js @@ -14,6 +14,7 @@ import { none } from "@ember/object/computed"; export const AUTO_DELETE_PREFERENCES = { NEVER: 0, + CLEAR_REMINDER: 3, WHEN_REMINDER_SENT: 1, ON_OWNER_REPLY: 2, }; @@ -129,6 +130,11 @@ const Bookmark = RestModel.extend({ ).capitalize(); }, + @discourseComputed("reminder_at") + reminderAtExpired(bookmarkReminderAt) { + return moment(bookmarkReminderAt) < moment(); + }, + @discourseComputed() topicForList() { // for topic level bookmarks we want to jump to the last unread post URL, diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js index 1e773db6489..22108d871cc 100644 --- a/app/assets/javascripts/discourse/app/models/user.js +++ b/app/assets/javascripts/discourse/app/models/user.js @@ -98,6 +98,7 @@ let userOptionFields = [ "timezone", "skip_new_user_tips", "default_calendar", + "bookmark_auto_delete_preference", ]; export function addSaveableUserOptionField(fieldName) { diff --git a/app/assets/javascripts/discourse/app/templates/components/bookmark-list.hbs b/app/assets/javascripts/discourse/app/templates/components/bookmark-list.hbs index a5e6d62b177..819b95296ae 100644 --- a/app/assets/javascripts/discourse/app/templates/components/bookmark-list.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/bookmark-list.hbs @@ -17,7 +17,7 @@