diff --git a/app/assets/javascripts/admin/addon/mixins/setting-component.js b/app/assets/javascripts/admin/addon/mixins/setting-component.js index b55c0455931..9585168210e 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( - "
" + - preview.replace(/\{\{value\}\}/g, value) + - "
" - ); + const escapedValue = preview.replace(/\{\{value\}\}/g, value); + return htmlSafe(`
${escapedValue}
`); } }, @@ -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({ }); return Promise.resolve(); }, - - actions: { - 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: result.user_count, - key: key.replace(/_/g, " "), - }, - admin: true, - }); - - controller.set("onClose", () => { - this.updateExistingUsers = controller.updateExistingUsers; - this.send("save"); - }); - } else { - this.send("save"); - } - }); - } else { - this.send("save"); - } - }, - - 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 && e.jqXHR.responseJSON.errors) { - this.set("validationMessage", e.jqXHR.responseJSON.errors[0]); - } else { - this.set("validationMessage", I18n.t("generic_error")); - } - }); - }, - - cancel() { - this.rollbackBuffer(); - }, - - resetDefault() { - this.set("buffered.value", this.get("setting.default")); - }, - - toggleSecret() { - this.toggleProperty("isSecret"); - }, - - setDefaultValues() { - this.set( - "buffered.value", - this.bufferedValues.concat(this.defaultValues).uniq().join("|") - ); - return false; - }, - }, }); diff --git a/app/assets/javascripts/discourse/app/lib/utilities.js b/app/assets/javascripts/discourse/app/lib/utilities.js index e9cdaaf3f02..178fa1e6e25 100644 --- a/app/assets/javascripts/discourse/app/lib/utilities.js +++ b/app/assets/javascripts/discourse/app/lib/utilities.js @@ -7,6 +7,14 @@ import toMarkdown from "discourse/lib/to-markdown"; let _defaultHomepage; +export function splitString(str, separator = ",") { + if (typeof str === "string") { + return str.split(separator).filter(Boolean); + } else { + return []; + } +} + export function translateSize(size) { switch (size) { case "tiny":