Remote function for sidebar

This commit is contained in:
Kalle
2026-08-16 21:09:57 +03:00
parent 74c5059ccf
commit 4e731b0d8a
4 changed files with 44 additions and 27 deletions

View File

@@ -7,6 +7,7 @@ import { loggedInUser } from "#lib/features/auth/user-state.ts";
import { getPatrons } from "#lib/features/front-page/front-page.remote.ts";
import { getNotifications } from "#lib/features/notifications/notifications.remote.ts";
import { toNotificationRows } from "#lib/features/notifications/notifications-utils.ts";
import { setSidenavCollapsed } from "#lib/features/sidenav/sidenav.remote.ts";
import { m } from "#lib/paraglide/messages.js";
import { getLocale } from "#lib/paraglide/runtime.js";
import { GIT_COMMIT } from "#lib/utils/git-commit.ts";
@@ -100,13 +101,14 @@ function unseenRequestsLabel(count: number) {
: m.friends_unseenRequests_other({ count });
}
function toggleSideNavCollapsed() {
const sideNavCollapseForm = setSidenavCollapsed.enhance(async ({ submit }) => {
sideNavCollapsed = !sideNavCollapsed;
void fetch("/sidenav", {
method: "POST",
body: new URLSearchParams({ collapsed: String(sideNavCollapsed) }),
});
}
try {
await submit().updates();
} catch {
// losing the preference cookie is fine, the optimistic state stays
}
});
// mobile hide-on-scroll header, ported from the React useNavOffset
const MOBILE_BREAKPOINT = 600;
@@ -389,13 +391,17 @@ function formatRelativeDate(timestamp: number) {
class="sideNavCollapseButtonContainer"
data-testid="sidenav-collapse-button"
>
<button
type="button"
class="collapseButton sideNavCollapseButton"
onclick={toggleSideNavCollapsed}
>
<PanelLeft />
</button>
<form class="sideNavCollapseForm" {...sideNavCollapseForm}>
<input
{...setSidenavCollapsed.fields.collapsed.as(
"hidden",
!sideNavCollapsed,
)}
/>
<button class="collapseButton sideNavCollapseButton">
<PanelLeft />
</button>
</form>
{#if sideNavCollapsed && showUnseenDot}
<NotificationDot />
{/if}
@@ -663,6 +669,10 @@ function formatRelativeDate(timestamp: number) {
}
}
.sideNavCollapseForm {
display: contents;
}
.collapseButton {
display: none;
align-items: center;

View File

@@ -0,0 +1,11 @@
import * as v from "valibot";
import { form, getRequestEvent } from "$app/server";
import { writeSidenavCollapsed } from "./sidenav.server.ts";
/** Persists whether the desktop sidenav is collapsed. */
export const setSidenavCollapsed = form(
v.object({ collapsed: v.optional(v.boolean(), false) }),
async ({ collapsed }) => {
writeSidenavCollapsed(getRequestEvent().cookies, collapsed);
},
);

View File

@@ -1,19 +1,21 @@
import { json } from "@sveltejs/kit";
import type { Cookies } from "@sveltejs/kit";
import {
readSessionCookie,
writeSessionCookie,
} from "#lib/features/auth/session.server.ts";
import { ServerConfig } from "#lib/server/config.ts";
import { IS_E2E_TEST_RUN } from "#lib/utils/e2e.ts";
import type { RequestHandler } from "./$types";
const SIDENAV_COOKIE_NAME = "sidenav";
const TEN_YEARS_IN_SECONDS = 315_360_000;
export const POST: RequestHandler = async ({ request, cookies }) => {
const form = new URLSearchParams(await request.text());
const collapsed = form.get("collapsed") === "true";
/** Reads the desktop sidenav collapsed preference from its cookie. */
export function readSidenavCollapsed(cookies: Cookies): boolean {
return readSessionCookie(cookies.get(SIDENAV_COOKIE_NAME))?.collapsed === true;
}
/** Persists the desktop sidenav collapsed preference into its cookie. */
export function writeSidenavCollapsed(cookies: Cookies, collapsed: boolean) {
const session = readSessionCookie(cookies.get(SIDENAV_COOKIE_NAME)) ?? {};
session.collapsed = collapsed;
cookies.set(SIDENAV_COOKIE_NAME, writeSessionCookie(session), {
@@ -23,6 +25,4 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
httpOnly: true,
maxAge: TEN_YEARS_IN_SECONDS,
});
return json({ success: true });
};
}

View File

@@ -1,15 +1,11 @@
import { readSessionCookie } from "#lib/features/auth/session.server.ts";
import { resolveSidebarData } from "#lib/features/sidebar/sidebar.server.ts";
import { readSidenavCollapsed } from "#lib/features/sidenav/sidenav.server.ts";
import { getTheme } from "#lib/features/theme/theme.server.ts";
import type { LayoutServerLoad } from "./$types";
const SIDENAV_COOKIE_NAME = "sidenav";
export const load: LayoutServerLoad = async ({ locals, cookies }) => {
const user = locals.user;
const sidenavSession = readSessionCookie(cookies.get(SIDENAV_COOKIE_NAME));
return {
user: user
? {
@@ -28,7 +24,7 @@ export const load: LayoutServerLoad = async ({ locals, cookies }) => {
createdAt: user.createdAt,
}
: undefined,
sidenavCollapsed: sidenavSession?.collapsed === true,
sidenavCollapsed: readSidenavCollapsed(cookies),
sidebar: await resolveSidebarData(user?.id ?? null),
theme: getTheme(cookies),
};