From 635c2be32c410eef3d633adc6c1b231c59f66710 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Fri, 13 Oct 2023 10:30:59 +0100 Subject: [PATCH 1/8] settings: initial search function not really plugged into anything yet. --- html/admin.html | 4 +++ ts/modules/settings.ts | 73 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/html/admin.html b/html/admin.html index 3c7ae1b..2c5aafc 100644 --- a/html/admin.html +++ b/html/admin.html @@ -729,6 +729,10 @@
+
+ + +
{{ .strings.aboutProgram }} {{ .strings.userProfiles }} diff --git a/ts/modules/settings.ts b/ts/modules/settings.ts index 3179e71..07b7e91 100644 --- a/ts/modules/settings.ts +++ b/ts/modules/settings.ts @@ -102,6 +102,7 @@ class DOMInput { constructor(inputType: string, setting: Setting, section: string, name: string) { this._container = document.createElement("div"); this._container.classList.add("setting"); + this._container.setAttribute("data-name", name) this._container.innerHTML = `
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json index a61318b..32f473f 100644 --- a/lang/admin/en-us.json +++ b/lang/admin/en-us.json @@ -55,6 +55,7 @@ "donate": "Donate", "unlink": "Unlink Account", "sendPWR": "Send Password Reset", + "noResultsFound": "No Results Found", "contactThrough": "Contact through:", "extendExpiry": "Extend expiry", "sendPWRManual": "User {n} has no method of contact, press copy to get a link to send to them.", @@ -86,6 +87,7 @@ "settingsHiddenDependency": "Matching settings are hidden because they depend on the value of another setting:", "settingsDependsOn": "{setting}: Depends on {dependency}", "settingsAdvancedMode": "{setting}: Advanced Settings must be enabled", + "settingsMaybeUnderAdvanced": "Tip: You might find what you're looking for by enabling Advanced Settings.", "ombiProfile": "Ombi user profile", "ombiUserDefaultsDescription": "Create an Ombi user and configure it, then select it below. It's settings/permissions will be stored and applied to new Ombi users created by jfa-go when this profile is selected.", "userProfiles": "User Profiles", @@ -125,7 +127,7 @@ "jellyfinID": "Jellyfin ID", "userPageLogin": "User Page: Login", "userPagePage": "User Page: Page", - "buildTime": "Build Time", + "buildTime": "Build Time", "builtBy": "Built By", "loginNotAdmin": "Not an Admin?" }, diff --git a/ts/modules/settings.ts b/ts/modules/settings.ts index 22d7f53..045d3a3 100644 --- a/ts/modules/settings.ts +++ b/ts/modules/settings.ts @@ -622,6 +622,7 @@ export class settingsList { private _panel = document.getElementById("settings-panel") as HTMLDivElement; private _sidebar = document.getElementById("settings-sidebar") as HTMLDivElement; + private _visibleSection: string; private _sections: { [name: string]: sectionPanel } private _buttons: { [name: string]: HTMLSpanElement } private _needsRestart: boolean = false; @@ -630,7 +631,9 @@ export class settingsList { private _advanced: boolean = false; private _searchbox: HTMLInputElement = document.getElementById("settings-search") as HTMLInputElement; - private _clearSearchbox: HTMLButtonElement = document.getElementById("settings-search-clear") as HTMLButtonElement; + private _clearSearchboxButtons: Array = Array.from(document.getElementsByClassName("settings-search-clear")) as Array; + + private _noResultsPanel: HTMLElement = document.getElementById("settings-not-found"); addSection = (name: string, s: Section, subButton?: HTMLElement) => { @@ -681,6 +684,7 @@ export class settingsList { for (let n in this._sections) { if (n == name) { this._sections[name].visible = true; + this._visibleSection = name; this._buttons[name].classList.add("selected"); } else { this._sections[n].visible = false; @@ -763,9 +767,11 @@ export class settingsList { this._searchbox.oninput = () => { this.search(this._searchbox.value); }; - this._clearSearchbox.onclick = () => { - this._searchbox.value = ""; - this._searchbox.oninput(null); + for (let b of this._clearSearchboxButtons) { + b.onclick = () => { + this._searchbox.value = ""; + this._searchbox.oninput(null); + }; }; } @@ -861,8 +867,11 @@ export class settingsList { } }) + // FIXME: Search "About" & "User profiles", pseudo-search "User profiles" for things like "Ombi", "Referrals", etc. search = (query: string) => { - query = query.toLowerCase(); + query = query.toLowerCase().trim(); + // Make sure a blank search is detected when there's just whitespace. + if (query.replace(/\s+/g, "") == "") query = ""; let firstVisibleSection = ""; for (let section of this._settings.order) { @@ -875,12 +884,15 @@ export class settingsList { // hide button, unhide if matched this._buttons[section].classList.add("unfocused"); + let matchedSection = false; + if (section.toLowerCase().includes(query) || this._settings.sections[section].meta.name.toLowerCase().includes(query) || this._settings.sections[section].meta.description.toLowerCase().includes(query)) { if ((this._settings.sections[section].meta.advanced && this._advanced) || !(this._settings.sections[section].meta.advanced)) { this._buttons[section].classList.remove("unfocused"); firstVisibleSection = firstVisibleSection || section; + matchedSection = true; } } const sectionElement = this._sections[section].asElement(); @@ -888,6 +900,13 @@ export class settingsList { if (this._settings.sections[section].settings[setting].type == "note") continue; const element = sectionElement.querySelector(`div[data-name="${setting}"]`) as HTMLElement; + // If we match the whole section, don't bother searching settings. + if (matchedSection) { + element.classList.remove("opacity-50", "pointer-events-none"); + element.setAttribute("aria-disabled", "false"); + continue; + } + // element.classList.remove("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low"); element.classList.add("opacity-50", "pointer-events-none"); element.setAttribute("aria-disabled", "true"); @@ -902,12 +921,12 @@ export class settingsList { const shouldShow = (query != "" && ((this._settings.sections[section].settings[setting].advanced && this._advanced) || !(this._settings.sections[section].settings[setting].advanced))); - if (shouldShow) { + if (shouldShow || query == "") { // element.classList.add("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low"); element.classList.remove("opacity-50", "pointer-events-none"); element.setAttribute("aria-disabled", "false"); } - if ((shouldShow && element.querySelector("label").classList.contains("unfocused")) || (!shouldShow)) { + if (query != "" && ((shouldShow && element.querySelector("label").classList.contains("unfocused")) || (!shouldShow))) { // Add a note explaining why the setting is hidden if (!dependencyCard) { dependencyCard = document.createElement("aside"); @@ -942,10 +961,16 @@ export class settingsList { } } } - if (firstVisibleSection) { + if (firstVisibleSection && (query != "" || this._visibleSection == "")) { this._buttons[firstVisibleSection].onclick(null); - } else { - // FIXME: Show "no results found" in right panel (mention enabling advanced settings + this._noResultsPanel.classList.add("unfocused"); + } else if (query != "") { + this._noResultsPanel.classList.remove("unfocused"); + if (this._visibleSection) { + this._sections[this._visibleSection].visible = false; + this._buttons[this._visibleSection].classList.remove("selected"); + this._visibleSection = ""; + } } } } From dba7d0bd4e7f8285d3d14722bc358b12251a76d8 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sat, 14 Oct 2023 12:46:39 +0100 Subject: [PATCH 6/8] admin: improve searchboxes appearance "Clear search" button is now fully over the search box, so the focus/click effects fully wrap round it. Rounded edges of the button are now only on the right edge. --- html/admin.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/html/admin.html b/html/admin.html index e82423c..e14ec29 100644 --- a/html/admin.html +++ b/html/admin.html @@ -645,7 +645,7 @@ - +
@@ -731,7 +731,7 @@
- +
{{ .strings.aboutProgram }} From 31b7ede6656d1e8ec12888f17612347d46569bde Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sat, 14 Oct 2023 12:52:10 +0100 Subject: [PATCH 7/8] accounts: fix search button (again) --- html/admin.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/admin.html b/html/admin.html index e14ec29..4792436 100644 --- a/html/admin.html +++ b/html/admin.html @@ -645,7 +645,7 @@
- +
From e2c24a2593216e09f4ecfabd18f408be7ef676c3 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sat, 14 Oct 2023 13:07:30 +0100 Subject: [PATCH 8/8] accounts: add "not results found" screen --- html/admin.html | 10 +++++++++- ts/modules/accounts.ts | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/html/admin.html b/html/admin.html index 4792436..48818a9 100644 --- a/html/admin.html +++ b/html/admin.html @@ -645,7 +645,7 @@
- +
@@ -708,6 +708,14 @@ +
+
+ {{ .strings.noResultsFound }} + +
+
diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index f4ca0b0..34595ad 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -946,6 +946,8 @@ export class accountsList { } } + private _notFoundPanel: HTMLElement = document.getElementById("accounts-not-found"); + search = (query: String): string[] => { console.log(this._queries); this._filterArea.textContent = ""; @@ -2021,17 +2023,25 @@ export class accountsList { this._inSearch = true; // this.setVisibility(this.search(query), true); } - this.setVisibility(this.search(query), true); + const results = this.search(query); + this.setVisibility(results, true); this._checkCheckCount(); this.showHideSearchOptionsHeader(); + if (results.length == 0) { + this._notFoundPanel.classList.remove("unfocused"); + } else { + this._notFoundPanel.classList.add("unfocused"); + } }; this._search.oninput = onchange; - const clearSearchButton = document.getElementById("accounts-search-clear") as HTMLSpanElement; - clearSearchButton.addEventListener("click", () => { - this._search.value = ""; - onchange(); - }); + const clearSearchButtons = Array.from(document.getElementsByClassName("accounts-search-clear")) as Array; + for (let b of clearSearchButtons) { + b.addEventListener("click", () => { + this._search.value = ""; + onchange(); + }); + } this._announceTextarea.onkeyup = this.loadPreview; addDiscord = newDiscordSearch(window.lang.strings("linkDiscord"), window.lang.strings("searchDiscordUser"), window.lang.strings("add"), (user: DiscordUser, id: string) => { @@ -2084,8 +2094,15 @@ export class accountsList { // console.log("ordering by", event.detail, ": ", this._ordering); if (!(this._inSearch)) { this.setVisibility(this._ordering, true); + this._notFoundPanel.classList.add("unfocused"); } else { - this.setVisibility(this.search(this._search.value), true); + const results = this.search(this._search.value); + this.setVisibility(results, true); + if (results.length == 0) { + this._notFoundPanel.classList.remove("unfocused"); + } else { + this._notFoundPanel.classList.add("unfocused"); + } } this.showHideSearchOptionsHeader(); }); @@ -2195,8 +2212,15 @@ export class accountsList { this._ordering = this._columns[this._activeSortColumn].sort(this._users); if (!(this._inSearch)) { this.setVisibility(this._ordering, true); + this._notFoundPanel.classList.add("unfocused"); } else { - this.setVisibility(this.search(this._search.value), true); + const results = this.search(this._search.value); + if (results.length == 0) { + this._notFoundPanel.classList.remove("unfocused"); + } else { + this._notFoundPanel.classList.add("unfocused"); + } + this.setVisibility(results, true); } this._checkCheckCount(); }