ts: use pages modules in admin (kinda), change pseudo-links

pseudo-links are now just links, because i'm lazy and it's easier than
fixing an issue. They now take the form `/?invite=code` and
`/accounts/?user=userid`.

ts/modules.tabs.ts is now a wrapper for ts/modules/pages.ts.

Also, fixed no section appearing when visiting the settings tab.
This commit is contained in:
Harvey Tindall
2024-08-28 14:18:52 +01:00
parent d2e5209832
commit 02f4ba6e8e
10 changed files with 388 additions and 151 deletions
+8 -2
View File
@@ -1804,10 +1804,16 @@ export class accountsList {
this.focusAccount(event.detail);
});
isAccountURL = () => { return window.location.pathname.startsWith(window.URLBase + "/accounts/user/"); }
// FIXME: Use Query Param! so it doesn't get cleared by pages.ts.
isAccountURL = () => {
const urlParams = new URLSearchParams(window.location.search);
const userID = urlParams.get("user");
return Boolean(userID);
}
loadAccountURL = () => {
let userID = window.location.pathname.split(window.URLBase + "/accounts/user/")[1].split("?lang")[0];
const urlParams = new URLSearchParams(window.location.search);
const userID = urlParams.get("user");
this.focusAccount(userID);
}
+7 -7
View File
@@ -64,17 +64,17 @@ export class Activity implements activity, SearchableItem {
}
_genUserLink = (): string => {
return `<span role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" data-href="${this._urlBase}accounts/user/${this._act.user_id}">${this._genUserText()}</span>`;
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${this._urlBase}accounts?user=${this._act.user_id}">${this._genUserText()}</a>`;
}
_genSrcUserLink = (): string => {
return `<span role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" data-href="${this._urlBase}accounts/user/${this._act.source}">${this._genSrcUserText()}</span>`;
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${this._urlBase}accounts?user=${this._act.source}">${this._genSrcUserText()}</a>`;
}
private _renderInvText = (): string => { return `<span class="font-medium font-mono">${this.value || this.invite_code || "???"}</span>`; }
private _genInvLink = (): string => {
return `<span role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-invite" data-id="${this.invite_code}" data-href="${this._urlBase}invites/${this.invite_code}">${this._renderInvText()}</span>`;
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-invite" data-id="${this.invite_code}" href="${this._urlBase}?invite=${this.invite_code}">${this._renderInvText()}</a>`;
}
@@ -307,17 +307,17 @@ export class Activity implements activity, SearchableItem {
const pseudoInvites = this._card.getElementsByClassName("activity-pseudo-link-invite") as HTMLCollectionOf<HTMLAnchorElement>;
for (let i = 0; i < pseudoUsers.length; i++) {
const navigate = (event: Event) => {
/*const navigate = (event: Event) => {
event.preventDefault()
window.tabs.switch("accounts");
document.dispatchEvent(accountURLEvent(pseudoUsers[i].getAttribute("data-id")));
window.history.pushState(null, document.title, pseudoUsers[i].getAttribute("data-href"));
}
pseudoUsers[i].onclick = navigate;
pseudoUsers[i].onkeydown = navigate;
pseudoUsers[i].onkeydown = navigate;*/
}
for (let i = 0; i < pseudoInvites.length; i++) {
const navigate = (event: Event) => {
/*const navigate = (event: Event) => {
event.preventDefault();
window.invites.reload(() => {
window.tabs.switch("invites");
@@ -326,7 +326,7 @@ export class Activity implements activity, SearchableItem {
});
}
pseudoInvites[i].onclick = navigate;
pseudoInvites[i].onkeydown = navigate;
pseudoInvites[i].onkeydown = navigate;*/
}
}
+7 -2
View File
@@ -448,10 +448,15 @@ export class inviteList implements inviteList {
this.focusInvite(event.detail);
})
isInviteURL = () => { return window.location.pathname.startsWith(window.URLBase + "/invites/"); }
isInviteURL = () => {
const urlParams = new URLSearchParams(window.location.search);
const inviteCode = urlParams.get("invite");
return Boolean(inviteCode);
}
loadInviteURL = () => {
let inviteCode = window.location.pathname.split(window.URLBase + "/invites/")[1].split("?lang")[0];
const urlParams = new URLSearchParams(window.location.search);
const inviteCode = urlParams.get("invite");
this.focusInvite(inviteCode, window.lang.notif("errorInviteNotFound"));
}
+13 -19
View File
@@ -32,24 +32,21 @@ export class PageManager {
private _onpopstate = (event: PopStateEvent) => {
let name = event.state;
if (!(event.state in this.pages)) {
if (!this.pages.has(event.state)) {
name = this.pageList[0]
}
let success = this.pages[name].show();
let success = this.pages.get(name).show();
if (!success) {
console.log("failed");
return;
}
if (!(this.hideOthers)) {
console.log("shoudln't hide others");
return;
}
for (let k of this.pageList) {
if (name != k) {
this.pages[k].hide();
this.pages.get(k).hide();
}
}
console.log("loop ended", this);
}
constructor(c: PageConfig) {
@@ -65,42 +62,39 @@ export class PageManager {
setPage(p: Page) {
p.index = this.pageList.length;
this.pages[p.name] = p;
this.pages.set(p.name, p);
this.pageList.push(p.name);
}
load(name: string = "") {
if (!(name in this.pages)) return window.history.pushState(name || this.defaultName, this.defaultTitle, "")
const p = this.pages[name];
if (!this.pages.has(name)) return window.history.pushState(name || this.defaultName, this.defaultTitle, "")
const p = this.pages.get(name);
this.loadPage(p);
}
loadPage (p: Page) {
window.history.pushState(p.name || this.defaultName, p.title, p.url);
window.history.pushState(p.name || this.defaultName, p.title, p.url + window.location.search);
}
prev(name: string = "") {
if (!(name in this.pages)) return console.error(`previous page ${name} not found`);
let p = this.pages[name];
if (!this.pages.has(name)) return console.error(`previous page ${name} not found`);
let p = this.pages.get(name);
let shouldSkip = true;
while (shouldSkip && p.index > 0) {
p = this.pages[this.pageList[p.index-1]];
p = this.pages.get(this.pageList[p.index-1]);
shouldSkip = p.shouldSkip();
}
this.loadPage(p);
}
next(name: string = "") {
if (!(name in this.pages)) return console.error(`previous page ${name} not found`);
let p = this.pages[name];
console.log("next", name, p);
console.log("pages", this.pages, this.pageList);
if (!this.pages.has(name)) return console.error(`previous page ${name} not found`);
let p = this.pages.get(name);
let shouldSkip = true;
while (shouldSkip && p.index < this.pageList.length) {
p = this.pages[this.pageList[p.index+1]];
p = this.pages.get(this.pageList[p.index+1]);
shouldSkip = p.shouldSkip();
}
console.log("next ended with", p);
this.loadPage(p);
}
};
+6 -1
View File
@@ -636,6 +636,7 @@ export class settingsList {
}
private _showPanel = (name: string) => {
console.log("showing", name);
for (let n in this._sections) {
if (n == name) {
this._sections[name].visible = true;
@@ -919,7 +920,11 @@ export class settingsList {
for (let i = 0; i < this._loader.children.length; i++) {
this._loader.children[i].classList.remove("invisible");
}
this._showPanel(this._settings.sections[0].section);
for (let s of this._settings.sections) {
if (s.meta.disabled) continue;
this._showPanel(s.section);
break;
}
document.dispatchEvent(new CustomEvent("settings-loaded"));
document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: false }));
this._saveButton.classList.add("unfocused");
+62 -29
View File
@@ -1,44 +1,77 @@
import { PageManager, Page } from "../modules/pages.js";
export interface Tab {
page: Page;
tabEl: HTMLDivElement;
buttonEl: HTMLSpanElement;
preFunc?: () => void;
postFunc?: () => void;
}
export class Tabs implements Tabs {
private _current: string = "";
tabs: Array<Tab>;
private _baseOffset = -1;
tabs: Map<string, Tab>;
pages: PageManager;
constructor() {
this.tabs = [];
this.tabs = new Map<string, Tab>;
this.pages = new PageManager({
hideOthersOnPageShow: true,
defaultName: "invites",
defaultTitle: document.title,
});
}
addTab = (tabID: string, preFunc = () => void {}, postFunc = () => void {}) => {
let tab = {} as Tab;
tab.tabID = tabID;
tab.tabEl = document.getElementById("tab-" + tabID) as HTMLDivElement;
tab.buttonEl = document.getElementById("button-tab-" + tabID) as HTMLSpanElement;
addTab = (tabID: string, url: string, preFunc = () => void {}, postFunc = () => void {},) => {
let tab: Tab = {
page: null,
tabEl: document.getElementById("tab-" + tabID) as HTMLDivElement,
buttonEl: document.getElementById("button-tab-" + tabID) as HTMLSpanElement,
preFunc: preFunc,
postFunc: postFunc,
};
if (this._baseOffset == -1) {
this._baseOffset = tab.buttonEl.offsetLeft;
}
tab.page = {
name: tabID,
title: document.title, /*FIXME: Get actual names from translations*/
url: window.URLBase + "/" + url,
show: () => {
tab.buttonEl.classList.add("active", "~urge");
tab.tabEl.classList.remove("unfocused");
tab.buttonEl.parentElement.scrollTo(tab.buttonEl.offsetLeft-this._baseOffset, 0);
document.dispatchEvent(new CustomEvent("tab-change", { detail: tabID }));
return true;
},
hide: () => {
tab.buttonEl.classList.remove("active");
tab.buttonEl.classList.remove("~urge");
tab.tabEl.classList.add("unfocused");
return true;
},
shouldSkip: () => false,
};
this.pages.setPage(tab.page);
tab.buttonEl.onclick = () => { this.switch(tabID); };
tab.preFunc = preFunc;
tab.postFunc = postFunc;
this.tabs.push(tab);
this.tabs.set(tabID, tab);
}
get current(): string { return this._current; }
set current(tabID: string) { this.switch(tabID); }
switch = (tabID: string, noRun: boolean = false, keepURL: boolean = false) => {
this._current = tabID;
let baseOffset = -1;
for (let t of this.tabs) {
if (baseOffset == -1) baseOffset = t.buttonEl.offsetLeft;
if (t.tabID == tabID) {
t.buttonEl.classList.add("active", "~urge");
if (t.preFunc && !noRun) { t.preFunc(); }
t.tabEl.classList.remove("unfocused");
if (t.postFunc && !noRun) { t.postFunc(); }
document.dispatchEvent(new CustomEvent("tab-change", { detail: keepURL ? "" : tabID }));
// t.buttonEl.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
t.buttonEl.parentElement.scrollTo(t.buttonEl.offsetLeft-baseOffset, 0);
} else {
t.buttonEl.classList.remove("active");
t.buttonEl.classList.remove("~urge");
t.tabEl.classList.add("unfocused");
}
switch = (tabID: string, noRun: boolean = false) => {
let t = this.tabs.get(tabID);
if (t == undefined) {
[t] = this.tabs.values();
}
this._current = t.page.name;
if (t.preFunc && !noRun) { t.preFunc(); }
this.pages.load(tabID);
if (t.postFunc && !noRun) { t.postFunc(); }
}
}