From d261a809e2b4d7c24a4a3fd2790dab948a315e09 Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Thu, 2 Apr 2020 09:57:48 +1000 Subject: [PATCH] FEATURE: Bookmark reminder type changes and bugfixes (#9329) New Reminder Types ------------------------------------- * Add a "later this week" reminder which is today + 2 days, will not show if we are on the days Thu-Sun * Add a "start of next business week" reminder which is 8am Monday Bugfixes and Tweaks -------------------------------------- * Move dates out of translation for reminder types and yield HTML for tap-tile for more customizable content and styling * Make sure double clicking the bookmark icon in quick access takes users to the new bookmarks-with-reminders page * Sane default to 8am (start of day) for custom reminder with no time --- .../discourse/controllers/bookmark.js | 95 ++++++++----------- .../templates/components/tap-tile.hbs | 2 +- .../discourse/templates/modal/bookmark.hbs | 48 ++++++++-- .../discourse/widgets/user-menu.js | 5 +- app/models/bookmark.rb | 4 +- config/locales/client.en.yml | 14 +-- test/javascripts/controllers/bookmark-test.js | 84 ++++++++-------- 7 files changed, 139 insertions(+), 113 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/bookmark.js b/app/assets/javascripts/discourse/controllers/bookmark.js index 18a6112a25..c14329d253 100644 --- a/app/assets/javascripts/discourse/controllers/bookmark.js +++ b/app/assets/javascripts/discourse/controllers/bookmark.js @@ -3,7 +3,6 @@ import { Promise } from "rsvp"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import discourseComputed from "discourse-common/utils/decorators"; import { popupAjaxError } from "discourse/lib/ajax-error"; -import { htmlSafe } from "@ember/template"; import { ajax } from "discourse/lib/ajax"; const START_OF_DAY_HOUR = 8; @@ -17,7 +16,9 @@ const REMINDER_TYPES = { NEXT_MONTH: "next_month", CUSTOM: "custom", LAST_CUSTOM: "last_custom", - NONE: "none" + NONE: "none", + START_OF_NEXT_BUSINESS_WEEK: "start_of_next_business_week", + LATER_THIS_WEEK: "later_this_week" }; export default Controller.extend(ModalFunctionality, { @@ -115,60 +116,48 @@ export default Controller.extend(ModalFunctionality, { ); }, + @discourseComputed() + showLaterThisWeek() { + return this.now().day() < 4; // 4 is Thursday + }, + @discourseComputed("parsedLastCustomReminderDatetime") lastCustomFormatted(parsedLastCustomReminderDatetime) { - return htmlSafe( - I18n.t("bookmarks.reminders.last_custom", { - date: parsedLastCustomReminderDatetime.format( - I18n.t("dates.long_no_year") - ) - }) + return parsedLastCustomReminderDatetime.format( + I18n.t("dates.long_no_year") ); }, + @discourseComputed() + startNextBusinessWeekFormatted() { + return this.nextWeek() + .day("Monday") + .format(I18n.t("dates.long_no_year")); + }, + @discourseComputed() laterTodayFormatted() { - return htmlSafe( - I18n.t("bookmarks.reminders.later_today", { - date: this.laterToday().format(I18n.t("dates.time")) - }) - ); + return this.laterToday().format(I18n.t("dates.time")); }, @discourseComputed() tomorrowFormatted() { - return htmlSafe( - I18n.t("bookmarks.reminders.tomorrow", { - date: this.tomorrow().format(I18n.t("dates.time_short_day")) - }) - ); - }, - - @discourseComputed() - nextBusinessDayFormatted() { - return htmlSafe( - I18n.t("bookmarks.reminders.next_business_day", { - date: this.nextBusinessDay().format(I18n.t("dates.time_short_day")) - }) - ); + return this.tomorrow().format(I18n.t("dates.time_short_day")); }, @discourseComputed() nextWeekFormatted() { - return htmlSafe( - I18n.t("bookmarks.reminders.next_week", { - date: this.nextWeek().format(I18n.t("dates.long_no_year")) - }) - ); + return this.nextWeek().format(I18n.t("dates.long_no_year")); + }, + + @discourseComputed() + laterThisWeekFormatted() { + return this.laterThisWeek().format(I18n.t("dates.time_short_day")); }, @discourseComputed() nextMonthFormatted() { - return htmlSafe( - I18n.t("bookmarks.reminders.next_month", { - date: this.nextMonth().format(I18n.t("dates.long_no_year")) - }) - ); + return this.nextMonth().format(I18n.t("dates.long_no_year")); }, @discourseComputed() @@ -237,9 +226,17 @@ export default Controller.extend(ModalFunctionality, { return this.tomorrow(); case REMINDER_TYPES.NEXT_WEEK: return this.nextWeek(); + case REMINDER_TYPES.START_OF_NEXT_BUSINESS_WEEK: + return this.nextWeek().day("Monday"); + case REMINDER_TYPES.LATER_THIS_WEEK: + return this.laterThisWeek(); case REMINDER_TYPES.NEXT_MONTH: return this.nextMonth(); case REMINDER_TYPES.CUSTOM: + this.set( + "customReminderTime", + this.customReminderTime || `0${START_OF_DAY_HOUR}:00` + ); const customDateTime = this.parseCustomDateTime( this.customReminderDate, this.customReminderTime @@ -265,23 +262,6 @@ export default Controller.extend(ModalFunctionality, { return this.startOfDay(this.now().add(1, "month")); }, - nextBusinessDay() { - const currentDay = this.now().isoWeekday(); // 1=Mon, 7=Sun - let next = null; - - // friday - if (currentDay === 5) { - next = this.now().add(3, "days"); - // saturday - } else if (currentDay === 6) { - next = this.now().add(2, "days"); - } else { - next = this.now().add(1, "day"); - } - - return this.startOfDay(next); - }, - tomorrow() { return this.startOfDay(this.now().add(1, "day")); }, @@ -301,6 +281,13 @@ export default Controller.extend(ModalFunctionality, { : later.add(30, "minutes").startOf("hour"); }, + laterThisWeek() { + if (!this.showLaterThisWeek) { + return; + } + return this.startOfDay(this.now().add(2, "days")); + }, + handleSaveError(e) { this.isSavingBookmarkManually = false; if (typeof e === "string") { diff --git a/app/assets/javascripts/discourse/templates/components/tap-tile.hbs b/app/assets/javascripts/discourse/templates/components/tap-tile.hbs index 4e04200945..df17a2fd98 100644 --- a/app/assets/javascripts/discourse/templates/components/tap-tile.hbs +++ b/app/assets/javascripts/discourse/templates/components/tap-tile.hbs @@ -1,2 +1,2 @@ {{d-icon icon}} -{{text}} +{{ yield }} diff --git a/app/assets/javascripts/discourse/templates/modal/bookmark.hbs b/app/assets/javascripts/discourse/templates/modal/bookmark.hbs index 2fa0345732..5a1082c711 100644 --- a/app/assets/javascripts/discourse/templates/modal/bookmark.hbs +++ b/app/assets/javascripts/discourse/templates/modal/bookmark.hbs @@ -21,21 +21,51 @@ {{#if userHasTimezoneSet}} {{#tap-tile-grid activeTile=selectedReminderType as |grid|}} {{#if showAtDesktop}} - {{tap-tile icon="desktop" text=(i18n "bookmarks.reminders.at_desktop") tileId=reminderTypes.AT_DESKTOP activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{#tap-tile icon="desktop" tileId=reminderTypes.AT_DESKTOP activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.at_desktop"}} + {{/tap-tile}} {{/if}} {{#if showLaterToday}} - {{tap-tile icon="far-moon" text=laterTodayFormatted tileId=reminderTypes.LATER_TODAY activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{#tap-tile icon="far-moon" tileId=reminderTypes.LATER_TODAY activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.later_today"}}
+ {{laterTodayFormatted}} + {{/tap-tile}} {{/if}} - {{tap-tile icon="briefcase" text=nextBusinessDayFormatted tileId=reminderTypes.NEXT_BUSINESS_DAY activeTile=grid.activeTile onChange=(action "selectReminderType")}} - {{tap-tile icon="far-sun" text=tomorrowFormatted tileId=reminderTypes.TOMORROW activeTile=grid.activeTile onChange=(action "selectReminderType")}} - {{tap-tile icon="far-clock" text=nextWeekFormatted tileId=reminderTypes.NEXT_WEEK activeTile=grid.activeTile onChange=(action "selectReminderType")}} - {{tap-tile icon="far-calendar-plus" text=nextMonthFormatted tileId=reminderTypes.NEXT_MONTH activeTile=grid.activeTile onChange=(action "selectReminderType")}} - {{tap-tile icon="calendar-alt" text=(I18n "bookmarks.reminders.custom") tileId=reminderTypes.CUSTOM activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{#tap-tile icon="far-sun" tileId=reminderTypes.TOMORROW activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.tomorrow"}}
+ {{tomorrowFormatted}} + {{/tap-tile}} + {{#if showLaterThisWeek}} + {{#tap-tile icon="angle-double-right" tileId=reminderTypes.LATER_THIS_WEEK activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.later_this_week"}}
+ {{laterThisWeekFormatted}} + {{/tap-tile}} + {{/if}} + {{#tap-tile icon="briefcase" tileId=reminderTypes.START_OF_NEXT_BUSINESS_WEEK activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.start_of_next_business_week"}}
+ {{startNextBusinessWeekFormatted}} + {{/tap-tile}} + {{#tap-tile icon="far-clock" tileId=reminderTypes.NEXT_WEEK activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.next_week"}}
+ {{nextWeekFormatted}} + {{/tap-tile}} + {{#tap-tile icon="far-calendar-plus" tileId=reminderTypes.NEXT_MONTH activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.next_month"}}
+ {{nextMonthFormatted}} + {{/tap-tile}} + {{#tap-tile icon="calendar-alt" tileId=reminderTypes.CUSTOM activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.custom"}} + {{/tap-tile}} {{#if showLastCustom}} - {{tap-tile icon="undo" text=lastCustomFormatted tileId=reminderTypes.LAST_CUSTOM activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{#tap-tile icon="undo" tileId=reminderTypes.LAST_CUSTOM activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.last_custom"}}
+ {{lastCustomFormatted}} + {{/tap-tile}} {{/if}} - {{tap-tile icon="ban" text=(I18n "bookmarks.reminders.none") tileId=reminderTypes.NONE activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{#tap-tile icon="ban" tileId=reminderTypes.NONE activeTile=grid.activeTile onChange=(action "selectReminderType")}} + {{i18n "bookmarks.reminders.none"}} + {{/tap-tile}} {{/tap-tile-grid}} {{#if customDateTimeSelected}}
diff --git a/app/assets/javascripts/discourse/widgets/user-menu.js b/app/assets/javascripts/discourse/widgets/user-menu.js index 6f4b5c323c..1198729726 100644 --- a/app/assets/javascripts/discourse/widgets/user-menu.js +++ b/app/assets/javascripts/discourse/widgets/user-menu.js @@ -56,13 +56,16 @@ createWidget("user-menu-links", { }, bookmarksGlyph() { + let path = this.siteSettings.enable_bookmarks_with_reminders + ? "bookmarks-with-reminders" + : "bookmarks"; return { action: UserMenuAction.QUICK_ACCESS, actionParam: QuickAccess.BOOKMARKS, label: "user.bookmarks", className: "user-bookmarks-link", icon: "bookmark", - href: `${this.attrs.path}/activity/bookmarks` + href: `${this.attrs.path}/activity/${path}` }; }, diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb index 198895fa5c..f34e58c695 100644 --- a/app/models/bookmark.rb +++ b/app/models/bookmark.rb @@ -67,7 +67,9 @@ class Bookmark < ActiveRecord::Base tomorrow: 3, next_week: 4, next_month: 5, - custom: 6 + custom: 6, + start_of_next_business_week: 7, + later_this_week: 8 ) end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e9bbb29609..289e14161b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -316,13 +316,15 @@ en: list_permission_denied: "You do not have permission to view this user's bookmarks." reminders: at_desktop: "Next time I'm at my desktop" - later_today: "Later today
{{date}}" - next_business_day: "Next business day
{{date}}" - tomorrow: "Tomorrow
{{date}}" - next_week: "Next week
{{date}}" - next_month: "Next month
{{date}}" + later_today: "Later today" + next_business_day: "Next business day" + tomorrow: "Tomorrow" + next_week: "Next week" + later_this_week: "Later this week" + start_of_next_business_week: "Next Monday" + next_month: "Next month" custom: "Custom date and time" - last_custom: "Last
{{date}}" + last_custom: "Last" none: "No reminder needed" drafts: diff --git a/test/javascripts/controllers/bookmark-test.js b/test/javascripts/controllers/bookmark-test.js index c517ad2a89..84d323d382 100644 --- a/test/javascripts/controllers/bookmark-test.js +++ b/test/javascripts/controllers/bookmark-test.js @@ -6,6 +6,7 @@ moduleFor("controller:bookmark", { beforeEach() { logIn(); BookmarkController = this.subject({ currentUser: User.current() }); + BookmarkController.onShow(); }, afterEach() { @@ -58,54 +59,34 @@ QUnit.test("nextMonth gets next month correctly", function(assert) { ); }); -QUnit.test( - "nextBusinessDay gets next business day of monday correctly if today is friday", - function(assert) { - mockMomentTz("2019-12-13T08:00:00"); +QUnit.test("laterThisWeek gets 2 days from now", function(assert) { + mockMomentTz("2019-12-10T08:00:00"); - assert.equal( - BookmarkController.nextBusinessDay().format("YYYY-MM-DD"), - "2019-12-16" - ); - } -); + assert.equal( + BookmarkController.laterThisWeek().format("YYYY-MM-DD"), + "2019-12-12" + ); +}); -QUnit.test( - "nextBusinessDay gets next business day of monday correctly if today is saturday", - function(assert) { - mockMomentTz("2019-12-14T08:00:00"); +QUnit.test("laterThisWeek returns null if we are at Thursday already", function( + assert +) { + mockMomentTz("2019-12-12T08:00:00"); - assert.equal( - BookmarkController.nextBusinessDay().format("YYYY-MM-DD"), - "2019-12-16" - ); - } -); + assert.equal(BookmarkController.laterThisWeek(), null); +}); -QUnit.test( - "nextBusinessDay gets next business day of monday correctly if today is sunday", - function(assert) { - mockMomentTz("2019-12-15T08:00:00"); +QUnit.test("showLaterThisWeek returns true if < Thursday", function(assert) { + mockMomentTz("2019-12-10T08:00:00"); - assert.equal( - BookmarkController.nextBusinessDay().format("YYYY-MM-DD"), - "2019-12-16" - ); - } -); + assert.equal(BookmarkController.showLaterThisWeek, true); +}); -QUnit.test( - "nextBusinessDay gets next business day of thursday correctly if today is wednesday", - function(assert) { - mockMomentTz("2019-12-11T08:00:00"); - - assert.equal( - BookmarkController.nextBusinessDay().format("YYYY-MM-DD"), - "2019-12-12" - ); - } -); +QUnit.test("showLaterThisWeek returns false if > Thursday", function(assert) { + mockMomentTz("2019-12-12T08:00:00"); + assert.equal(BookmarkController.showLaterThisWeek, false); +}); QUnit.test("tomorrow gets tomorrow correctly", function(assert) { mockMomentTz("2019-12-11T08:00:00"); @@ -154,6 +135,27 @@ QUnit.test( } ); +QUnit.test( + "reminderAt - custom - defaults to 8:00am if the time is not selected", + function(assert) { + BookmarkController.customReminderDate = "2028-12-12"; + BookmarkController.selectedReminderType = + BookmarkController.reminderTypes.CUSTOM; + const reminderAt = BookmarkController.reminderAt(); + assert.equal(BookmarkController.customReminderTime, "08:00"); + assert.equal( + reminderAt.toString(), + moment + .tz( + "2028-12-12 08:00", + BookmarkController.currentUser.resolvedTimezone() + ) + .toString(), + "the custom date and time are parsed correctly with default time" + ); + } +); + QUnit.test( "loadLastUsedCustomReminderDatetime fills the custom reminder date + time if present in localStorage", function(assert) {