diff --git a/app/web/src/components/AppShell.tsx b/app/web/src/components/AppShell.tsx new file mode 100644 index 0000000..eafc275 --- /dev/null +++ b/app/web/src/components/AppShell.tsx @@ -0,0 +1,44 @@ +import type { ReactNode } from "react"; +import { cn } from "~/lib/utils"; + +const NAV = [ + { hrefKey: "nav.library", href: "library", id: "library" }, + { hrefKey: "nav.browse", href: "browse", id: "browse" }, + { hrefKey: "nav.mounts", href: "mounts", id: "mounts" }, + { hrefKey: "nav.danmaku", href: "danmaku", id: "danmaku" }, +] as const; + +export function AppShell(props: { + locale: string; + active: (typeof NAV)[number]["id"]; + labels: Record; + children: ReactNode; +}) { + const base = `/${props.locale}`; + return ( +
+ +
{props.children}
+
+ ); +} diff --git a/app/web/src/components/pages/MountsBody.tsx b/app/web/src/components/pages/MountsBody.tsx new file mode 100644 index 0000000..a0911d5 --- /dev/null +++ b/app/web/src/components/pages/MountsBody.tsx @@ -0,0 +1,138 @@ +import { Button, Input } from "@app/ui"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useState } from "react"; +import { createTRPCReactClient, trpc } from "~/lib/trpc"; + +function MountsInner() { + const utils = trpc.useUtils(); + const list = trpc.media.mountList.useQuery(); + const create = trpc.media.mountCreate.useMutation({ + onSuccess: () => utils.media.mountList.invalidate(), + }); + const test = trpc.media.mountTest.useMutation(); + const del = trpc.media.mountDelete.useMutation({ + onSuccess: () => utils.media.mountList.invalidate(), + }); + const [form, setForm] = useState({ + name: "", + baseUrl: "", + username: "", + password: "", + rootPath: "/", + }); + + return ( +
+

挂载管理

+

+ OpenList 请填写其 WebDAV 地址;凭据仅保存在服务端。 +

+
{ + e.preventDefault(); + create.mutate({ + name: form.name, + type: "webdav", + baseUrl: form.baseUrl, + username: form.username || undefined, + password: form.password || undefined, + rootPath: form.rootPath || "/", + enabled: true, + }); + }} + > + setForm({ ...form, name: e.target.value })} + required + /> + setForm({ ...form, baseUrl: e.target.value })} + required + /> + setForm({ ...form, username: e.target.value })} + /> + setForm({ ...form, password: e.target.value })} + /> + setForm({ ...form, rootPath: e.target.value })} + /> +
+ + +
+ {test.data && ( +

+ {test.data.message} +

+ )} +
+ + +
+ ); +} + +const client = createTRPCReactClient(); +const qc = new QueryClient(); + +export function MountsBody() { + return ( + + + + + + ); +} diff --git a/app/web/src/pages/[locale]/mounts.astro b/app/web/src/pages/[locale]/mounts.astro new file mode 100644 index 0000000..d5d2560 --- /dev/null +++ b/app/web/src/pages/[locale]/mounts.astro @@ -0,0 +1,19 @@ +--- +import { COOKIE_LANG, isLocale, localePath, resolvePreferredLocale, type Locale } from "@app/i18n"; +import Mounts from "~/views/mounts.astro"; + +const raw = Astro.params["locale"]; +const normalized = raw === "zh-cn" ? "zh-CN" : raw; + +if (!normalized || !isLocale(normalized)) { + const preferred = resolvePreferredLocale( + Astro.cookies.get(COOKIE_LANG)?.value, + Astro.request.headers.get("accept-language"), + ); + return Astro.redirect(localePath(preferred, "/mounts")); +} + +const locale: Locale = normalized; +--- + + diff --git a/app/web/src/views/mounts.astro b/app/web/src/views/mounts.astro new file mode 100644 index 0000000..93c1eb0 --- /dev/null +++ b/app/web/src/views/mounts.astro @@ -0,0 +1,39 @@ +--- +import { alternatePath, type Locale, t } from "@app/i18n"; +import { Toaster } from "@app/ui"; +import { AppShell } from "~/components/AppShell"; +import { MountsBody } from "~/components/pages/MountsBody"; +import AppLayout from "~/layouts/AppLayout.astro"; + +export interface Props { + locale: Locale; +} + +const { locale } = Astro.props; +const other: Locale = locale === "zh-CN" ? "en" : "zh-CN"; +const labels: Record = { + "meta.brand": t(locale, "meta.brand"), + "nav.library": t(locale, "nav.library"), + "nav.browse": t(locale, "nav.browse"), + "nav.mounts": t(locale, "nav.mounts"), + "nav.danmaku": t(locale, "nav.danmaku"), +}; +--- + + + + + + +