Replace the full-width top bar with a fixed floating header of glass pills: a fixed-position ModuleName (accent dot + label + prev/next cyclic module stepper) and a shared SearchBar. The header's left edge is anchored as if the nav rail and filter sidebar were both open, so it never shifts on collapse or module change; the right edge leaves a 10% margin. - ModuleName label width is measured live from the reachable modules' titles in the current language, recomputing on a language or account change. - Module names come from a shared moduleLabelKey (the same short labels as the nav rail); NavSidebar and the header now read from one source (lib/modules.ts). - The arrows step cyclically through moduleOrder(me) — dynamic, no hard-coded targets, wrapping at both ends. - Search is hoisted into the SearchBar for feed (YouTube search), plex, channels (including the Discovery tab) and playlists (new); Go button, Enter triggers it. - The Playlists left rail now reaches the top; header clearance via --hdr-h.
50 lines
2.3 KiB
TypeScript
50 lines
2.3 KiB
TypeScript
import type { Me } from "./api";
|
|
import type { Page } from "./urlState";
|
|
|
|
// Admin/system modules — rendered in their own section (below a divider) in the nav rail.
|
|
export const SYSTEM_PAGES: readonly Page[] = ["scheduler", "config", "users", "audit"];
|
|
|
|
// The i18n key for each module's SHORT display name — the same label the nav rail shows. Single
|
|
// source so the nav rail and the header's ModuleName pill always read identically (the longer,
|
|
// descriptive `pageTitleKey` names are kept only for the browser tab title).
|
|
export const moduleLabelKey: Record<Page, string> = {
|
|
feed: "header.account.feed",
|
|
channels: "header.account.channels",
|
|
playlists: "header.account.playlists",
|
|
plex: "plex.navLabel",
|
|
notifications: "inbox.navLabel",
|
|
messages: "messages.navLabel",
|
|
downloads: "downloads.navLabel",
|
|
stats: "header.account.stats",
|
|
scheduler: "header.account.scheduler",
|
|
config: "header.account.config",
|
|
users: "header.account.users",
|
|
audit: "header.account.audit",
|
|
settings: "header.account.settings",
|
|
};
|
|
|
|
// The ordered list of primary module pages available to THIS user, in nav-rail order. Single
|
|
// source of truth for both the nav rail (NavSidebar) and the header's ◀/▶ module stepper, so a
|
|
// new module (or a newly-gated one) shows up in both at once — nothing hard-codes the sequence.
|
|
// Availability is derived from the account (plex toggle, demo restrictions, admin role), so the
|
|
// reachable set changes with the language-independent account state, not the UI.
|
|
export function moduleOrder(me: Me): Page[] {
|
|
const pages: Page[] = ["feed", "channels", "playlists"];
|
|
if (me.plex_enabled) pages.push("plex");
|
|
pages.push("notifications");
|
|
if (!me.is_demo) pages.push("messages", "downloads");
|
|
pages.push("stats");
|
|
if (me.role === "admin") pages.push(...SYSTEM_PAGES);
|
|
return pages;
|
|
}
|
|
|
|
// Step to the next/previous module cyclically (wraps at both ends). `dir` = +1 forward, -1 back.
|
|
// If the current page isn't a primary module (e.g. Settings), forward lands on the first module
|
|
// and back on the last.
|
|
export function stepModule(me: Me, current: Page, dir: 1 | -1): Page {
|
|
const order = moduleOrder(me);
|
|
if (order.length === 0) return current;
|
|
const i = order.indexOf(current);
|
|
if (i === -1) return dir === 1 ? order[0] : order[order.length - 1];
|
|
return order[(i + dir + order.length) % order.length];
|
|
}
|