REFACTOR: setting component mixin (#14437)

REFACTOR: setting component mixin (#14437)

  • REFACTOR: setting component mixing
  • drops jquery usage
  • extract spit function
  • uses @action
  • removes get usage where possible
  • uses helpers
  • minor changes

Co-authored-by: Jarek Radosz jradosz@gmail.com

diff --git a/app/assets/javascripts/admin/addon/mixins/setting-component.js b/app/assets/javascripts/admin/addon/mixins/setting-component.js
index b55c045..9585168 100644
--- a/app/assets/javascripts/admin/addon/mixins/setting-component.js
+++ b/app/assets/javascripts/admin/addon/mixins/setting-component.js
@@ -1,14 +1,18 @@
+import { isNone } from "@ember/utils";
+import { fmt, propertyNotEqual } from "discourse/lib/computed";
 import { alias, oneWay } from "@ember/object/computed";
 import I18n from "I18n";
 import Mixin from "@ember/object/mixin";
 import { Promise } from "rsvp";
 import { ajax } from "discourse/lib/ajax";
 import { categoryLinkHTML } from "discourse/helpers/category-link";
-import discourseComputed from "discourse-common/utils/decorators";
+import discourseComputed, { bind } from "discourse-common/utils/decorators";
 import { htmlSafe } from "@ember/template";
 import { on } from "@ember/object/evented";
 import showModal from "discourse/lib/show-modal";
 import { warn } from "@ember/debug";
+import { action } from "@ember/object";
+import { splitString } from "discourse/lib/utilities";
 
 const CUSTOM_TYPES = [
   "bool",
@@ -32,26 +36,20 @@ const CUSTOM_TYPES = [
 
 const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
 
-function splitPipes(str) {
-  if (typeof str === "string") {
-    return str.split("|").filter(Boolean);
-  } else {
-    return [];
-  }
-}
-
 export default Mixin.create({
   classNameBindings: [":row", ":setting", "overridden", "typeClass"],
   content: alias("setting"),
   validationMessage: null,
   isSecret: oneWay("setting.secret"),
+  setting: null,
 
   @discourseComputed("buffered.value", "setting.value")
   dirty(bufferVal, settingVal) {
-    if (bufferVal === null || bufferVal === undefined) {
+    if (isNone(bufferVal)) {
       bufferVal = "";
     }
-    if (settingVal === null || settingVal === undefined) {
+
+    if (isNone(settingVal)) {
       settingVal = "";
     }
 
@@ -61,21 +59,17 @@ export default Mixin.create({
   @discourseComputed("setting", "buffered.value")
   preview(setting, value) {
     // A bit hacky, but allows us to use helpers
-    if (setting.get("setting") === "category_style") {
-      let category = this.site.get("categories.firstObject");
+    if (setting.setting === "category_style") {
+      const category = this.site.get("categories.firstObject");
       if (category) {
-        return categoryLinkHTML(category, {
-          categoryStyle: value,
-        });
+        return categoryLinkHTML(category, { categoryStyle: value });
       }
     }
-    let preview = setting.get("preview");
+
+    const preview = setting.preview;
     if (preview) {
-      return htmlSafe(
-        "<div class='preview'>" +
-          preview.replace(/\{\{value\}\}/g, value) +
-          "</div>"
-      );
+      const escapedValue = preview.replace(/\{\{value\}\}/g, value);
+      return htmlSafe(`<div class='preview'>${escapedValue}</div>`);
     }
   },
 
@@ -103,51 +97,156 @@ export default Mixin.create({
     return setting.type;
   },
 
-  @discourseComputed("typeClass")
-  componentName(typeClass) {
-    return "site-settings/" + typeClass;
-  },
+  componentName: fmt("typeClass", "site-settings/%@"),
 
   @discourseComputed("setting.anyValue")
   allowAny(anyValue) {
     return anyValue !== false;
   },
 
-  @discourseComputed("setting.default", "buffered.value")
-  overridden(settingDefault, bufferedValue) {
-    return settingDefault !== bufferedValue;
-  },
+  overridden: propertyNotEqual("setting.default", "buffered.value"),
 
   @discourseComputed("buffered.value")
-  bufferedValues: splitPipes,
+  bufferedValues(value) {
+    return splitString(value, "|");
+  },
 
   @discourseComputed("setting.defaultValues")
-  defaultValues: splitPipes,
+  defaultValues(value) {
+    return splitString(value, "|");
+  },
 
   @discourseComputed("defaultValues", "bufferedValues")
   defaultIsAvailable(defaultValues, bufferedValues) {
     return (
-      defaultValues &&
       defaultValues.length > 0 &&
       !defaultValues.every((value) => bufferedValues.includes(value))
     );
   },
 
-  _watchEnterKey: on("didInsertElement", function () {
-    $(this.element).on(
-      "keydown.setting-enter",
-      ".input-setting-string",
-      (e) => {
-        if (e.key === "Enter") {
-          // enter key
-          this.send("save");
+  @action
+  update() {
+    const defaultUserPreferences = [
+      "default_email_digest_frequency",
+      "default_include_tl0_in_digests",
+      "default_email_level",
+      "default_email_messages_level",
+      "default_email_mailing_list_mode",
+      "default_email_mailing_list_mode_frequency",
+      "default_email_previous_replies",
+      "default_email_in_reply_to",
+      "default_other_new_topic_duration_minutes",
+      "default_other_auto_track_topics_after_msecs",
+      "default_other_notification_level_when_replying",
+      "default_other_external_links_in_new_tab",
+      "default_other_enable_quoting",
+      "default_other_enable_defer",
+      "default_other_dynamic_favicon",
+      "default_other_like_notification_frequency",
+      "default_other_skip_new_user_tips",
+      "default_topics_automatic_unpin",
+      "default_categories_watching",
+      "default_categories_tracking",
+      "default_categories_muted",
+      "default_categories_watching_first_post",
+      "default_categories_regular",
+      "default_tags_watching",
+      "default_tags_tracking",
+      "default_tags_muted",
+      "default_tags_watching_first_post",
+      "default_text_size",
+      "default_title_count_mode",
+    ];
+    const key = this.buffered.get("setting");
+
+    if (defaultUserPreferences.includes(key)) {
+      const data = {};
+      data[key] = this.buffered.get("value");
+
+      ajax(`/admin/site_settings/${key}/user_count.json`, {
+        type: "PUT",
+        data,
+      }).then((result) => {
+        const count = result.user_count;
+
+        if (count > 0) {
+          const controller = showModal("site-setting-default-categories", {
+            model: { count, key: key.replaceAll("_", " ") },
+            admin: true,
+          });
+
+          controller.set("onClose", () => {
+            this.updateExistingUsers = controller.updateExistingUsers;
+            this.save();
+          });
+        } else {
+          this.save();
         }
-      }
+      });
+    } else {
+      this.save();
+    }
+  },
+
+  @action
+  save() {
+    this._save()
+      .then(() => {
+        this.set("validationMessage", null);
+        this.commitBuffer();
+        if (AUTO_REFRESH_ON_SAVE.includes(this.setting.setting)) {
+          this.afterSave();
+        }
+      })
+      .catch((e) => {
+        if (e.jqXHR?.responseJSON?.errors) {
+          this.set("validationMessage", e.jqXHR.responseJSON.errors[0]);
+        } else {
+          this.set("validationMessage", I18n.t("generic_error"));
+        }
+      });
+  },
+
+  @action
+  cancel() {
+    this.rollbackBuffer();
+  },
+
+  @action
+  resetDefault() {
+    this.set("buffered.value", this.get("setting.default"));
+  },
+
+  @action
+  toggleSecret() {
+    this.toggleProperty("isSecret");
+  },
+
+  @action
+  setDefaultValues() {
+    this.set(
+      "buffered.value",
+      this.bufferedValues.concat(this.defaultValues).uniq().join("|")
     );
+    return false;
+  },
+
+  @bind
+  _handleKeydown(event) {
+    if (
+      event.key === "Enter" &&
+      event.target.classList.contains("input-setting-string")
+    ) {
+      this.save();
+    }
+  },
+
+  _watchEnterKey: on("didInsertElement", function () {
+    this.element.addEventListener("keydown", this._handleKeydown);
   }),
 
   _removeBindings: on("willDestroyElement", function () {
-    $(this.element).off("keydown.setting-enter");
+    this.element.removeEventListener("keydown", this._handleKeydown);
   }),
 
   _save() {
@@ -156,110 +255,4 @@ export default Mixin.create({
     });

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

GitHub sha: 1abe80752893066467fd29dd7bd7e0b389f3bb55

This commit appears in #14437 which was approved by CvX and pmusaraj. It was merged by jjaffeux.