admin: tab system improvement, search: ?search qp

tab content classes (e.g. settingsList, activityList)
can implement "AsTab", "Navigatable" and or "PageEventBindable",
the first giving them a tab name, a subpath and a reloader function,
the second an "isURL" and "navigate" function for loading resources,
the last giving them bind/unbindPageEvent methods. These are looped
through in ts/admin.ts still crudely, maybe tabs.ts could accept "AsTab"
implementers directly.

"Search" class now has a ?search query param which just encodes the
search box content, set when you perform a server search (hit enter or
press the button). ?user queries from the accounts or activity tab will
be converted to this form on loading.
This commit is contained in:
Harvey Tindall
2025-12-24 12:56:02 +00:00
parent 748acc13c0
commit 3308739619
10 changed files with 145 additions and 129 deletions
+26 -80
View File
@@ -1,7 +1,7 @@
import { ThemeManager } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
import { Tabs, Tab } from "./modules/tabs.js";
import { Tabs, Tab, isPageEventBindable, isNavigatable } from "./modules/tabs.js";
import { DOMInviteList, createInvite } from "./modules/invites.js";
import { accountsList } from "./modules/accounts.js";
import { settingsList } from "./modules/settings.js";
@@ -143,91 +143,37 @@ var profiles = new ProfileEditor();
window.notifications = new notificationBox(document.getElementById("notification-box") as HTMLDivElement, 5);
/*const modifySettingsSource = function () {
const profile = document.getElementById('radio-use-profile') as HTMLInputElement;
const user = document.getElementById('radio-use-user') as HTMLInputElement;
const profileSelect = document.getElementById('modify-user-profiles') as HTMLDivElement;
const userSelect = document.getElementById('modify-user-users') as HTMLDivElement;
(user.nextElementSibling as HTMLSpanElement).classList.toggle('@low');
(user.nextElementSibling as HTMLSpanElement).classList.toggle('@high');
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('@low');
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('@high');
profileSelect.classList.toggle('unfocused');
userSelect.classList.toggle('unfocused');
}*/
// Determine if url references an invite or account
let isInviteURL = window.invites.isInviteURL();
let isAccountURL = accounts.isAccountURL();
let isActivityURL = activity.isActivityURL();
// only use a navigatable URL once
let navigated = false;
// load tabs
const tabs: { id: string; url: string; reloader: () => void; unloader?: () => void }[] = [
{
id: "invites",
url: "",
reloader: () =>
window.invites.reload(() => {
if (isInviteURL) {
window.invites.loadInviteURL();
// Don't keep loading the same item on every tab refresh
isInviteURL = false;
}
}),
},
{
id: "accounts",
url: "accounts",
reloader: () =>
accounts.reload(() => {
if (isAccountURL) {
accounts.loadAccountURL();
// Don't keep loading the same item on every tab refresh
isAccountURL = false;
// Since accounts and activity accept ?user=x, wipe the other one.
isActivityURL = false;
}
accounts.bindPageEvents();
}),
unloader: accounts.unbindPageEvents,
},
{
id: "activity",
url: "activity",
reloader: () => {
activity.reload(() => {
if (isActivityURL) {
activity.loadActivityURL();
// Don't keep loading the same item on every tab refresh
isActivityURL = false;
// Since accounts and activity accept ?user=x, wipe the other one.
isAccountURL = false;
}
});
activity.bindPageEvents();
},
unloader: activity.unbindPageEvents,
},
{
id: "settings",
url: "settings",
reloader: settings.reload,
},
];
const defaultTab = tabs[0];
const tabs: { id: string; url: string; reloader: () => void; unloader?: () => void }[] = [];
window.tabs = new Tabs();
for (let tab of tabs) {
[window.invites, accounts, activity, settings].forEach((p: AsTab) => {
let t: { id: string; url: string; reloader: () => void; unloader?: () => void } = {
id: p.tabName,
url: p.pagePath,
reloader: () =>
p.reload(() => {
if (!navigated && isNavigatable(p)) {
if (p.isURL()) {
navigated = true;
p.navigate();
}
}
if (isPageEventBindable(p)) p.bindPageEvents();
}),
};
if (isPageEventBindable(p)) t.unloader = p.unbindPageEvents;
tabs.push(t);
window.tabs.addTab(
tab.id,
window.pages.Base + window.pages.Admin + "/" + tab.url,
t.id,
window.pages.Base + window.pages.Admin + "/" + t.url,
null,
tab.reloader,
tab.unloader || null,
t.reloader,
t.unloader || null,
);
}
});
let matchedTab = false;
for (const tab of tabs) {