feat(layout): full-height collapsible filter sidebar; sync status + scope relocated
Restructure the app shell into three top-level columns: - The per-user sync status (video counts + live sync state) moves from the top bar to a compact block at the top of the left nav rail (icon-only with a tooltip when collapsed). - The feed's Mine/Library scope toggle moves to the top of the filter sidebar. - The filter sidebar becomes a full-height sibling column with its own collapse control (a thin rail carrying the active-filter count), mirroring the nav rail. The top bar is now just the feed search / page title. - Both panels' collapsed state is persisted to the user's preferences (server-side, so it follows the account across devices), seeded from a localStorage cache to avoid a flash. Default: both panels open.
This commit is contained in:
parent
c6fe94450b
commit
072b3296a3
9 changed files with 255 additions and 78 deletions
|
|
@ -23,11 +23,11 @@ import {
|
|||
import { api, type Me } from "../lib/api";
|
||||
import { useLiveQuery } from "../lib/useLiveQuery";
|
||||
import { getUnreadCount, subscribe } from "../lib/notifications";
|
||||
import { LS } from "../lib/storage";
|
||||
import type { Page } from "../lib/urlState";
|
||||
import { type LangCode } from "../i18n";
|
||||
import AvatarImg from "./Avatar";
|
||||
import LanguageSwitcher from "./LanguageSwitcher";
|
||||
import SyncStatus from "./SyncStatus";
|
||||
|
||||
// Primary app navigation: a collapsible left rail with icon+label entries (Design C). The
|
||||
// modules used to hide under the avatar dropdown; they now live here. Collapsed, it becomes
|
||||
|
|
@ -39,6 +39,9 @@ export default function NavSidebar({
|
|||
onOpenAbout,
|
||||
onChangeLanguage,
|
||||
language,
|
||||
collapsed,
|
||||
onToggleCollapse,
|
||||
onGoToFullHistory,
|
||||
}: {
|
||||
me: Me;
|
||||
page: Page;
|
||||
|
|
@ -46,11 +49,13 @@ export default function NavSidebar({
|
|||
onOpenAbout: () => void;
|
||||
onChangeLanguage: (code: LangCode) => void;
|
||||
language: LangCode;
|
||||
// Collapse state is owned by App (persisted to the user's preferences); the rail just reflects
|
||||
// it and asks App to flip it.
|
||||
collapsed: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
onGoToFullHistory: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [collapsed, setCollapsed] = useState<boolean>(
|
||||
() => localStorage.getItem(LS.navCollapsed) === "1"
|
||||
);
|
||||
const [acctOpen, setAcctOpen] = useState(false);
|
||||
const acctBtnRef = useRef<HTMLButtonElement | null>(null);
|
||||
const acctPanelRef = useRef<HTMLDivElement | null>(null);
|
||||
|
|
@ -88,14 +93,6 @@ export default function NavSidebar({
|
|||
};
|
||||
}, [acctOpen]);
|
||||
|
||||
function toggleCollapsed() {
|
||||
setCollapsed((c) => {
|
||||
const next = !c;
|
||||
localStorage.setItem(LS.navCollapsed, next ? "1" : "0");
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await fetch("/auth/logout", { method: "POST", credentials: "include" });
|
||||
location.reload();
|
||||
|
|
@ -247,7 +244,7 @@ export default function NavSidebar({
|
|||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={toggleCollapsed}
|
||||
onClick={onToggleCollapse}
|
||||
title={collapsed ? t("nav.expand") : t("nav.collapse")}
|
||||
aria-label={collapsed ? t("nav.expand") : t("nav.collapse")}
|
||||
className="p-1 rounded-md text-muted hover:text-fg hover:bg-card transition"
|
||||
|
|
@ -260,6 +257,16 @@ export default function NavSidebar({
|
|||
</button>
|
||||
</div>
|
||||
|
||||
{/* Per-user sync status (video counts + live sync state), moved out of the old top bar. */}
|
||||
<div className="mb-3 pb-3 border-b border-border">
|
||||
<SyncStatus
|
||||
isAdmin={me.role === "admin"}
|
||||
onGoToFullHistory={onGoToFullHistory}
|
||||
variant="rail"
|
||||
collapsed={collapsed}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-1">
|
||||
{userItems.map(renderItem)}
|
||||
{systemItems.length > 0 &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue