From 920161b9208a538a6796b5d7ea3269562db4d374 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Wed, 21 Jun 2023 12:28:52 +0100 Subject: [PATCH] settings: add "note" type, shows as card also comes with a "style" attribute, to apply a color to the aside it's shown in. Used in User Page/Messages to mention the customize button, and on User page w/ a critical color to mention the jellyfin login requirement. --- api.go | 2 +- config/config-base.json | 27 ++++++++++++- models.go | 1 + scripts/generate_ini.py | 2 + ts/modules/settings.ts | 89 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 113 insertions(+), 8 deletions(-) diff --git a/api.go b/api.go index 06e2ffd..2aec453 100644 --- a/api.go +++ b/api.go @@ -268,7 +268,7 @@ func (app *appContext) GetConfig(gc *gin.Context) { val := app.config.Section(sectName).Key(settingName) s := resp.Sections[sectName].Settings[settingName] switch setting.Type { - case "text", "email", "select", "password": + case "text", "email", "select", "password", "note": s.Value = val.MustString("") case "number": s.Value = val.MustInt(0) diff --git a/config/config-base.json b/config/config-base.json index 2a77336..c0fb5f8 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -377,7 +377,7 @@ "order": [], "meta": { "name": "User Page", - "description": "Settings for the user page, which provides useful info and tools to users directly. NOTE: Jellyfin Login must be enabled to use this feature.", + "description": "Settings for the user page, which provides useful info and tools to users directly.", "depends_true": "ui|jellyfin_login" }, "settings": { @@ -387,6 +387,23 @@ "requires_restart": false, "type": "bool", "value": true + }, + "jellyfin_login_note": { + "name": "Note:", + "type": "note", + "value": "", + "depends_true": "enabled", + "required": "false", + "description": "Jellyfin Login must be enabled to use this feature.", + "style": "critical" + }, + "edit_note": { + "name": "Message Cards:", + "type": "note", + "value": "", + "depends_true": "enabled", + "required": "false", + "description": "Click the edit icon next to the \"User Page\" Setting to add custom Markdown messages that will be shown to the user." } } }, @@ -482,6 +499,14 @@ "type": "text", "value": "Need help? contact me.", "description": "Message displayed at bottom of emails." + }, + "edit_note": { + "name": "Customize Messages:", + "type": "note", + "value": "", + "depends_true": "enabled", + "required": "false", + "description": "Click the edit icon next to the \"Messages/Notifications\" Setting to customize the messages sent to users with Markdown." } } }, diff --git a/models.go b/models.go index dbbb5d0..bc2204b 100644 --- a/models.go +++ b/models.go @@ -215,6 +215,7 @@ type setting struct { Options [][2]string `json:"options,omitempty"` DependsTrue string `json:"depends_true,omitempty"` // If specified, this field is enabled when the specified bool setting is enabled. DependsFalse string `json:"depends_false,omitempty"` // If specified, opposite behaviour of DependsTrue. + Style string `json:"style,omitempty"` } type section struct { diff --git a/scripts/generate_ini.py b/scripts/generate_ini.py index edfb6ca..e47cc1c 100644 --- a/scripts/generate_ini.py +++ b/scripts/generate_ini.py @@ -21,6 +21,8 @@ def generate_ini(base_file, ini_file): if "meta" in config_base["sections"][section]: ini.set(section, fix_description(config_base["sections"][section]["meta"]["description"])) for entry in config_base["sections"][section]["settings"]: + if config_base["sections"][section]["settings"][entry]["type"] == "note": + continue if "description" in config_base["sections"][section]["settings"][entry]: ini.set(section, fix_description(config_base["sections"][section]["settings"][entry]["description"])) value = config_base["sections"][section]["settings"][entry]["value"] diff --git a/ts/modules/settings.ts b/ts/modules/settings.ts index 7d9a74d..2c12fac 100644 --- a/ts/modules/settings.ts +++ b/ts/modules/settings.ts @@ -453,6 +453,78 @@ class DOMSelect implements SSelect { asElement = (): HTMLDivElement => { return this._container; } } +interface SNote extends Setting { + value: string; + style?: string; +} +class DOMNote implements SNote { + private _container: HTMLDivElement; + private _aside: HTMLElement; + private _name: HTMLElement; + private _description: HTMLElement; + type: string = "note"; + private _style: string; + + get name(): string { return this._name.textContent; } + set name(n: string) { this._name.textContent = n; } + + get description(): string { return this._description.textContent; } + set description(d: string) { this._description.textContent = d; } + + get value(): string { return ""; } + set value(v: string) { return; } + + get required(): boolean { return false; } + set required(v: boolean) { return; } + + get requires_restart(): boolean { return false; } + set requires_restart(v: boolean) { return; } + + get style(): string { return this._style; } + set style(s: string) { + this._aside.classList.remove("~" + this._style); + this._style = s; + this._aside.classList.add("~" + this._style); + } + + constructor(setting: SNote, section: string) { + this._container = document.createElement("div"); + this._container.classList.add("setting"); + this._container.innerHTML = ` + + `; + this._name = this._container.querySelector(".setting-name"); + this._description = this._container.querySelector(".setting-description"); + this._aside = this._container.querySelector("aside"); + + if (setting.depends_false || setting.depends_true) { + let dependant = splitDependant(section, setting.depends_true || setting.depends_false); + let state = true; + if (setting.depends_false) { state = false; } + document.addEventListener(`settings-${dependant[0]}-${dependant[1]}`, (event: settingsBoolEvent) => { + if (Boolean(event.detail) !== state) { + this._container.classList.add("unfocused"); + } else { + this._container.classList.remove("unfocused"); + } + }); + } + + this.update(setting); + } + + update = (s: SNote) => { + this.name = s.name; + this.description = s.description; + this.style = ("style" in s && s.style) ? s.style : "info"; + }; + + asElement = (): HTMLDivElement => { return this._container; } +} + interface Section { meta: Meta; order: string[]; @@ -502,13 +574,18 @@ class sectionPanel { case "select": setting = new DOMSelect(setting as SSelect, this._sectionName, name); break; + case "note": + setting = new DOMNote(setting as SNote, this._sectionName); + break; + } + if (setting.type != "note") { + this.values[name] = ""+setting.value; + document.addEventListener(`settings-${this._sectionName}-${name}`, (event: CustomEvent) => { + // const oldValue = this.values[name]; + this.values[name] = ""+event.detail; + document.dispatchEvent(new CustomEvent("settings-section-changed")); + }); } - this.values[name] = ""+setting.value; - document.addEventListener(`settings-${this._sectionName}-${name}`, (event: CustomEvent) => { - // const oldValue = this.values[name]; - this.values[name] = ""+event.detail; - document.dispatchEvent(new CustomEvent("settings-section-changed")); - }); this._section.appendChild(setting.asElement()); this._settings[name] = setting; }