import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; import { fn } from "@ember/helper"; import { action } from "@ember/object"; import { service } from "@ember/service"; import { gt } from "truth-helpers"; import DButton from "discourse/components/d-button"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { cloneJSON } from "discourse-common/lib/object"; import I18n from "discourse-i18n"; import Tree from "admin/components/schema-theme-setting/editor/tree"; import FieldInput from "admin/components/schema-theme-setting/field"; export default class SchemaThemeSettingNewEditor extends Component { @service router; @tracked history = []; @tracked activeIndex = 0; @tracked activeDataPaths = []; @tracked activeSchemaPaths = []; @tracked saveButtonDisabled = false; @tracked validationErrorMessage; inputFieldObserver = new Map(); data = cloneJSON(this.args.setting.value); schema = this.args.setting.objects_schema; @action onChildClick(index, propertyName, parentNodeIndex) { this.history.pushObject({ dataPaths: [...this.activeDataPaths], schemaPaths: [...this.activeSchemaPaths], index: this.activeIndex, }); this.activeIndex = index; this.activeDataPaths.pushObjects([parentNodeIndex, propertyName]); this.activeSchemaPaths.pushObject(propertyName); this.inputFieldObserver.clear(); } @action updateIndex(index) { this.activeIndex = index; } @action generateSchemaTitle(object, schema, index) { let title; if (schema.properties[schema.identifier]?.type === "categories") { title = this.activeData[index][schema.identifier] ?.map((categoryId) => { return this.args.setting.metadata.categories[categoryId].name; }) .join(", "); } else { title = object[schema.identifier]; } return title || `${schema.name} ${index + 1}`; } get backButtonText() { if (this.history.length === 0) { return; } const lastHistory = this.history[this.history.length - 1]; return I18n.t("admin.customize.theme.schema.back_button", { name: this.generateSchemaTitle( this.#resolveDataFromPaths(lastHistory.dataPaths)[lastHistory.index], this.#resolveSchemaFromPaths(lastHistory.schemaPaths), lastHistory.index ), }); } get activeData() { return this.#resolveDataFromPaths(this.activeDataPaths); } #resolveDataFromPaths(paths) { if (paths.length === 0) { return this.data; } let data = this.data; paths.forEach((path) => { data = data[path]; }); return data; } get activeSchema() { return this.#resolveSchemaFromPaths(this.activeSchemaPaths); } #resolveSchemaFromPaths(paths) { if (paths.length === 0) { return this.schema; } let schema = this.schema; paths.forEach((path) => { schema = schema.properties[path].schema; }); return schema; } @action registerInputFieldObserver(index, callback) { this.inputFieldObserver[index] = callback; } @action unregisterInputFieldObserver(index) { delete this.inputFieldObserver[index]; } descriptions(fieldName, key) { // The `property_descriptions` metadata is an object with keys in the following format as an example: // // { // some_property.description: , // some_property.label: , // some_objects_property.some_other_property.description: , // some_objects_property.some_other_property.label: , // } const descriptions = this.args.setting.metadata?.property_descriptions; if (!descriptions) { return; } if (this.activeSchemaPaths.length > 0) { key = `${this.activeSchemaPaths.join(".")}.${fieldName}.${key}`; } else { key = `${fieldName}.${key}`; } return descriptions[key]; } fieldLabel(fieldName) { return this.descriptions(fieldName, "label") || fieldName; } fieldDescription(fieldName) { return this.descriptions(fieldName, "description"); } get fields() { const list = []; const activeObject = this.activeData[this.activeIndex]; if (activeObject) { for (const [name, spec] of Object.entries(this.activeSchema.properties)) { if (spec.type === "objects") { continue; } list.push({ name, spec, value: activeObject[name], description: this.fieldDescription(name), label: this.fieldLabel(name), }); } } return list; } @action clickBack() { const { dataPaths: lastDataPaths, schemaPaths: lastSchemaPaths, index: lastIndex, } = this.history.popObject(); this.activeDataPaths = lastDataPaths; this.activeSchemaPaths = lastSchemaPaths; this.activeIndex = lastIndex; this.inputFieldObserver.clear(); } @action addChildItem(propertyName, parentNodeIndex) { this.activeData[parentNodeIndex][propertyName].pushObject({}); this.onChildClick( this.activeData[parentNodeIndex][propertyName].length - 1, propertyName, parentNodeIndex ); } @action addItem() { this.activeData.pushObject({}); this.activeIndex = this.activeData.length - 1; } @action removeItem() { this.activeData.removeAt(this.activeIndex); if (this.activeData.length > 0) { this.activeIndex = Math.max(this.activeIndex - 1, 0); } else if (this.history.length > 0) { this.clickBack(); } else { this.activeIndex = 0; } } @action inputFieldChanged(field, newVal) { this.activeData[this.activeIndex][field.name] = newVal; if (field.name === this.activeSchema.identifier) { this.inputFieldObserver[this.activeIndex](); } } @action saveChanges() { this.saveButtonDisabled = true; this.args.setting .updateSetting(this.args.themeId, this.data) .then((result) => { this.args.setting.set("value", result[this.args.setting.setting]); this.router.transitionTo( "adminCustomizeThemes.show", this.args.themeId ); }) .catch((e) => { if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) { this.validationErrorMessage = e.jqXHR.responseJSON.errors[0]; } else { popupAjaxError(e); } }) .finally(() => (this.saveButtonDisabled = false)); } }