From f2834cc7e262599ef958fc3e2a0238964750da9b Mon Sep 17 00:00:00 2001 From: Ken Sternberg Date: Mon, 27 Nov 2023 15:48:24 -0800 Subject: [PATCH] web: provide tier-3 click capability. This commit enables the search function to work when changing from one tier 3 link to another in the tier 2 category (Providers, Events, Stages, etc.). Because the router ignores such changes, we must bring those changes to the attention of the receiver by hand. This commit makes the recepient Table object locatable with a data attribute tag and a painstakingly researched path to its most common location in our code. It then checks any `anchor:click` event from the Sidebar to see if its likely to be elided by the router. If it is, we take over, identify the Table object, set or clear its search state to the desired state, and call the `.fetch()` method on the Table to initiate a new search with that state. This is, frankly, _gross_. The URL and internal states mirror the actual desired state more or less by accident. But it's what we've got to work with at the moment. --- web/src/admin/AdminInterface/AdminSidebar.ts | 4 +- web/src/elements/sidebar/SidebarItems.ts | 52 +++++++++++++++++++- web/src/elements/table/TablePage.ts | 20 ++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/web/src/admin/AdminInterface/AdminSidebar.ts b/web/src/admin/AdminInterface/AdminSidebar.ts index 8f7872da5..42b381095 100644 --- a/web/src/admin/AdminInterface/AdminSidebar.ts +++ b/web/src/admin/AdminInterface/AdminSidebar.ts @@ -160,14 +160,14 @@ export class AkAdminSidebar extends AKElement { [ reload, msg( - str`You're currently impersonating ${this.impersonation}. Click to stop.` + str`You're currently impersonating ${this.impersonation}. Click to stop.`, ), ], ] : []; const enterpriseMenu: LocalSidebarEntry[] = this.config?.capabilities.includes( - CapabilitiesEnum.IsEnterprise + CapabilitiesEnum.IsEnterprise, ) ? [[null, msg("Enterprise"), null, [["/enterprise/licenses", msg("Licenses")]]]] : []; diff --git a/web/src/elements/sidebar/SidebarItems.ts b/web/src/elements/sidebar/SidebarItems.ts index 53b56bb57..8e873c559 100644 --- a/web/src/elements/sidebar/SidebarItems.ts +++ b/web/src/elements/sidebar/SidebarItems.ts @@ -1,4 +1,6 @@ +import { ROUTE_SEPARATOR } from "@goauthentik/common/constants"; import { AKElement } from "@goauthentik/elements/Base"; +import { findTable } from "@goauthentik/elements/table/TablePage"; import { CSSResult, TemplateResult, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators.js"; @@ -113,6 +115,8 @@ export class SidebarItems extends AKElement { super(); this.renderItem = this.renderItem.bind(this); this.toggleExpand = this.toggleExpand.bind(this); + this.onHashChange = this.onHashChange.bind(this); + this.reclick = this.reclick.bind(this); } connectedCallback() { @@ -153,6 +157,46 @@ export class SidebarItems extends AKElement { this.requestUpdate(); } + // This is gross and feels like 2007: using a path from the root through the shadowDoms (see + // `TablePage:findTable()`), this code finds the element that *should* be triggered by an event + // on the URL, and forcibly injects the text of the search and the click of the search button. + + reclick(ev: Event, path: string) { + const oldPath = window.location.hash.split(ROUTE_SEPARATOR)[0]; + const [curPath, ...curSearchComponents] = path.split(ROUTE_SEPARATOR); + const curSearch: string = + curSearchComponents.length > 0 ? curSearchComponents.join(ROUTE_SEPARATOR) : ""; + + if (curPath !== oldPath) { + // A Tier 1 or Tier 2 change should be handled by the router. (So should a Tier 3 + // change, but... here we are.) + return; + } + + const table = findTable(); + if (!table) { + return; + } + + // Always wrap the minimal exceptional code possible in an IIFE and supply the failure + // alternative. Turn exceptions into expressions with the smallest functional rewind + // whenever possible. + const search = (() => { + try { + return curSearch ? JSON.parse(decodeURIComponent(curSearch)) : { search: "" }; + } catch { + return { search: "" }; + } + })(); + + if ("search" in search) { + ev.preventDefault(); + ev.stopPropagation(); + table.search = search.search; + table.fetch(); + } + } + render(): TemplateResult { const lightThemed = { "pf-m-light": this.activeTheme === UiThemeEnum.Light }; @@ -213,8 +257,10 @@ export class SidebarItems extends AKElement { ${entry.label} `; } + const path = `${entry.attributes?.isAbsoluteLink ? "" : "#"}${entry.path}`; return html` this.reclick(ev, path)} class=${classMap(this.getLinkClasses(entry))} > ${entry.label} @@ -246,9 +292,11 @@ export class SidebarItems extends AKElement { renderLinkAndChildren(entry: SidebarEntry): TemplateResult { const handler = () => this.toggleExpand(entry); + const path = `${entry.attributes?.isAbsoluteLink ? "" : "#"}${entry.path}`; return html`