FEATURE: Introduce ignore duration selection (#7266)

FEATURE: Introduce ignore duration selection (#7266)

  • FEATURE: Introducing new UI for tracking User’s ignored or muted states
diff --git a/app/assets/javascripts/discourse/components/date-picker-future.js.es6 b/app/assets/javascripts/discourse/components/date-picker-future.js.es6
index 4cf37cd..b69e8dc 100644
--- a/app/assets/javascripts/discourse/components/date-picker-future.js.es6
+++ b/app/assets/javascripts/discourse/components/date-picker-future.js.es6
@@ -10,8 +10,7 @@ export default DatePicker.extend({
         moment()
           .add(1, "day")
           .toDate(),
-      setDefaultDate: !!this.get("defaultDate"),
-      minDate: new Date()
+      setDefaultDate: !!this.get("defaultDate")
     };
   }
 });
diff --git a/app/assets/javascripts/discourse/components/future-date-input.js.es6 b/app/assets/javascripts/discourse/components/future-date-input.js.es6
index 42ac342..9d6a470 100644
--- a/app/assets/javascripts/discourse/components/future-date-input.js.es6
+++ b/app/assets/javascripts/discourse/components/future-date-input.js.es6
@@ -10,11 +10,13 @@ export default Ember.Component.extend({
   selection: null,
   date: null,
   time: null,
+  includeDateTime: true,
   isCustom: Ember.computed.equal("selection", "pick_date_and_time"),
   isBasedOnLastPost: Ember.computed.equal(
     "selection",
     "set_based_on_last_post"
   ),
+  displayDateAndTimePicker: Ember.computed.and("includeDateTime", "isCustom"),
   displayLabel: null,
 
   init() {
diff --git a/app/assets/javascripts/discourse/controllers/ignore-duration.js.es6 b/app/assets/javascripts/discourse/controllers/ignore-duration.js.es6
new file mode 100644
index 0000000..e734e77
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/ignore-duration.js.es6
@@ -0,0 +1,31 @@
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+
+export default Ember.Controller.extend(ModalFunctionality, {
+  loading: false,
+  ignoredUntil: null,
+  actions: {
+    ignore() {
+      if (!this.get("ignoredUntil")) {
+        this.flash(
+          I18n.t("user.user_notifications.ignore_duration_time_frame_required"),
+          "alert-error"
+        );
+        return;
+      }
+      this.set("loading", true);
+      this.get("model")
+        .updateNotificationLevel("ignore", this.get("ignoredUntil"))
+        .then(() => {
+          this.set("model.ignored", true);
+          this.set("model.muted", false);
+          if (this.get("onSuccess")) {
+            this.get("onSuccess")();
+          }
+          this.send("closeModal");
+        })
+        .catch(popupAjaxError)
+        .finally(() => this.set("loading", false));
+    }
+  }
+});
diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6
index 72da24d..c8c0db7 100644
--- a/app/assets/javascripts/discourse/models/user.js.es6
+++ b/app/assets/javascripts/discourse/models/user.js.es6
@@ -615,10 +615,10 @@ const User = RestModel.extend({
     }
   },
 
-  updateNotificationLevel(level) {
+  updateNotificationLevel(level, expiringAt) {
     return ajax(`${userPath(this.get("username"))}/notification_level.json`, {
       type: "PUT",
-      data: { notification_level: level }
+      data: { notification_level: level, expiring_at: expiringAt }
     });
   },
 
diff --git a/app/assets/javascripts/discourse/templates/components/future-date-input.hbs b/app/assets/javascripts/discourse/templates/components/future-date-input.hbs
index 645fbc4..336fabe 100644
--- a/app/assets/javascripts/discourse/templates/components/future-date-input.hbs
+++ b/app/assets/javascripts/discourse/templates/components/future-date-input.hbs
@@ -6,13 +6,15 @@
         statusType=statusType
         value=selection
         input=input
+        includeDateTime=includeDateTime
         includeWeekend=includeWeekend
         includeFarFuture=includeFarFuture
+        includeMidFuture=includeMidFuture
         clearable=clearable
         none="topic.auto_update_input.none"}}
   </div>
 
-  {{#if isCustom}}
+  {{#if displayDateAndTimePicker}}
     <div class="control-group">
       {{d-icon "calendar-alt"}} {{date-picker-future value=date defaultDate=date}}
     </div>
diff --git a/app/assets/javascripts/discourse/templates/modal/ignore-duration.hbs b/app/assets/javascripts/discourse/templates/modal/ignore-duration.hbs
new file mode 100644
index 0000000..79cd89a
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/modal/ignore-duration.hbs
@@ -0,0 +1,19 @@
+{{#d-modal-body title="user.user_notifications.ignore_duration_title" autoFocus="false"}}
+  {{future-date-input
+    label="user.user_notifications.ignore_duration_when"
+    input=ignoredUntil
+    includeWeekend=true
+    includeDateTime=false
+    includeMidFuture=true
+    includeFarFuture=false}}
+  <p>{{i18n "user.user_notifications.ignore_duration_note"}}</p>
+{{/d-modal-body}}
+
+<div class="modal-footer">
+  {{d-button class="btn-primary"
+             disabled=saveDisabled
+             label="user.user_notifications.ignore_duration_save"
+             action=(action "ignore")}}
+
+  {{conditional-loading-spinner size="small" condition=loading}}
+</div>
diff --git a/app/assets/javascripts/select-kit/components/future-date-input-selector.js.es6 b/app/assets/javascripts/select-kit/components/future-date-input-selector.js.es6
index 5454e36..a9c325a 100644
--- a/app/assets/javascripts/select-kit/components/future-date-input-selector.js.es6
+++ b/app/assets/javascripts/select-kit/components/future-date-input-selector.js.es6
@@ -91,9 +91,21 @@ export const TIMEFRAMES = [
     icon: "briefcase"
   }),
   buildTimeframe({
+    id: "two_months",
+    format: "MMM D",
+    enabled: opts => opts.includeMidFuture,
+    when: (time, timeOfDay) =>
+      time
+        .add(2, "month")
+        .startOf("month")
+        .hour(timeOfDay)
+        .minute(0),
+    icon: "briefcase"
+  }),
+  buildTimeframe({
     id: "three_months",
     format: "MMM D",
-    enabled: opts => opts.includeFarFuture,
+    enabled: opts => opts.includeMidFuture,
     when: (time, timeOfDay) =>
       time
         .add(3, "month")
@@ -103,6 +115,18 @@ export const TIMEFRAMES = [
     icon: "briefcase"
   }),
   buildTimeframe({
+    id: "four_months",
+    format: "MMM D",
+    enabled: opts => opts.includeMidFuture,
+    when: (time, timeOfDay) =>
+      time
+        .add(4, "month")
+        .startOf("month")
+        .hour(timeOfDay)
+        .minute(0),
+    icon: "briefcase"
+  }),
+  buildTimeframe({
     id: "six_months",
     format: "MMM D",
     enabled: opts => opts.includeFarFuture,
@@ -139,6 +163,7 @@ export const TIMEFRAMES = [
   }),
   buildTimeframe({
     id: "pick_date_and_time",
+    enabled: opts => opts.includeDateTime,
     icon: "far-calendar-plus"
   }),
   buildTimeframe({
@@ -192,7 +217,9 @@ export default ComboBoxComponent.extend(DatetimeMixin, {
       now,
       day: now.day(),
       includeWeekend: this.get("includeWeekend"),
+      includeMidFuture: this.get("includeMidFuture") || true,
       includeFarFuture: this.get("includeFarFuture"),
+      includeDateTime: this.get("includeDateTime"),
       includeBasedOnLastPost: this.get("statusType") === CLOSE_STATUS_TYPE,
       canScheduleToday: 24 - now.hour() > 6
     };
diff --git a/app/assets/javascripts/select-kit/components/user-notifications-dropdown.js.es6 b/app/assets/javascripts/select-kit/components/user-notifications-dropdown.js.es6
index 4f90a2e..0d31bfc 100644
--- a/app/assets/javascripts/select-kit/components/user-notifications-dropdown.js.es6
+++ b/app/assets/javascripts/select-kit/components/user-notifications-dropdown.js.es6
@@ -1,85 +1,99 @@
 import DropdownSelectBox from "select-kit/components/dropdown-select-box";
 import { popupAjaxError } from "discourse/lib/ajax-error";
+import showModal from "discourse/lib/show-modal";
 
 export default DropdownSelectBox.extend({
   classNames: ["user-notifications", "user-notifications-dropdown"],
   nameProperty: "label",
-  allowInitialValueMutation: false,
-
-  computeHeaderContent() {

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

GitHub sha: b1cb95fc