update: header
@ -9,3 +9,5 @@ MiSans.css
|
|||||||
*.yaml
|
*.yaml
|
||||||
*.yml
|
*.yml
|
||||||
*.mdx
|
*.mdx
|
||||||
|
packages/solid/src/drizzle/cred
|
||||||
|
packages/solid/src/drizzle/main
|
4
bun.lock
@ -95,7 +95,7 @@
|
|||||||
"packages/solid": {
|
"packages/solid": {
|
||||||
"name": "example-basic",
|
"name": "example-basic",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@m3-components/solid": "^0.0.3",
|
"@m3-components/solid": "0.0.7",
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
@ -346,7 +346,7 @@
|
|||||||
|
|
||||||
"@koshnic/ratelimit": ["@koshnic/ratelimit@1.0.3", "", { "dependencies": { "@types/chai": "^4.3.9", "@types/mocha": "^10.0.3", "chai": "^4.3.10", "ioredis": "^5.3.2", "mocha": "^10.2.0" } }, "sha512-cfDcSc+I+M4hNM+/4M+lfn8UuTq4OEFKl78ThOcGNaO7g8tWb1vm2qVpV1p1loYao1mqk00NBNwHQu2E/qFq2g=="],
|
"@koshnic/ratelimit": ["@koshnic/ratelimit@1.0.3", "", { "dependencies": { "@types/chai": "^4.3.9", "@types/mocha": "^10.0.3", "chai": "^4.3.10", "ioredis": "^5.3.2", "mocha": "^10.2.0" } }, "sha512-cfDcSc+I+M4hNM+/4M+lfn8UuTq4OEFKl78ThOcGNaO7g8tWb1vm2qVpV1p1loYao1mqk00NBNwHQu2E/qFq2g=="],
|
||||||
|
|
||||||
"@m3-components/solid": ["@m3-components/solid@0.0.3", "", { "dependencies": { "solid-js": "^1.9.5" } }, "sha512-3TjrTTQyiD41uXO7TPuV6Q0FaemsBik4FXg4/2wi858hIkzre3rh/rmHNYG90z73/U9K0kTB6zZgO3HpnhVEwg=="],
|
"@m3-components/solid": ["@m3-components/solid@0.0.7", "", { "dependencies": { "solid-js": "^1.9.5" } }, "sha512-pyu8Of2wys0pgAV08lz9aOGGkiQPjzCXfuQyJeTu0auKYe8EvmqlcgH3JbX0cRP4CVxp6yd9ppfQT9B/i9DgvQ=="],
|
||||||
|
|
||||||
"@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.0", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg=="],
|
"@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.0", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg=="],
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"version": "vinxi version"
|
"version": "vinxi version"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@m3-components/solid": "^0.0.3",
|
"@m3-components/solid": "0.0.7",
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
9
packages/solid/src/components/icons/MenuOpen.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { type SVGIconComponent } from "./types";
|
||||||
|
|
||||||
|
export const MenuOpen: SVGIconComponent = () => {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M4 18q-.425 0-.712-.288T3 17t.288-.712T4 16h11q.425 0 .713.288T16 17t-.288.713T15 18zm14.9-1.7l-3.6-3.6q-.3-.3-.3-.7t.3-.7l3.6-3.6q.275-.275.7-.275t.7.275t.275.7t-.275.7L17.4 12l2.9 2.9q.275.275.275.7t-.275.7t-.7.275t-.7-.275M4 13q-.425 0-.712-.288T3 12t.288-.712T4 11h8q.425 0 .713.288T13 12t-.288.713T12 13zm0-5q-.425 0-.712-.288T3 7t.288-.712T4 6h11q.425 0 .713.288T16 7t-.288.713T15 8z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
3
packages/solid/src/components/icons/types.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Component, JSX } from "solid-js";
|
||||||
|
|
||||||
|
type SVGIconComponent = Component<JSX.SvgSVGAttributes<SVGElement>>;
|
@ -1,5 +1,31 @@
|
|||||||
import { Component } from "solid-js";
|
import { Component } from "solid-js";
|
||||||
|
import {
|
||||||
|
AppBar,
|
||||||
|
AppBarSearchBox,
|
||||||
|
IconButton,
|
||||||
|
LeadingElement,
|
||||||
|
TrailingElementGroup,
|
||||||
|
TrailingElement
|
||||||
|
} from "@m3-components/solid";
|
||||||
|
import "@m3-components/solid/index.css";
|
||||||
|
import { MenuOpen } from "~/components/icons/MenuOpen";
|
||||||
|
|
||||||
export const Header: Component = () => {
|
export const Header: Component = () => {
|
||||||
return <img src="/icons/TitleBar Mobile Light.svg" alt="logo" />;
|
return (
|
||||||
|
<div class="mt-4 top-0 left-0 w-full">
|
||||||
|
<AppBar variant="search">
|
||||||
|
<LeadingElement>
|
||||||
|
<IconButton>
|
||||||
|
<MenuOpen/>
|
||||||
|
</IconButton>
|
||||||
|
</LeadingElement>
|
||||||
|
<AppBarSearchBox class="text-center placeholder:text-on-surface-variant text-on-surface" placeholder="搜索" />
|
||||||
|
<TrailingElementGroup>
|
||||||
|
<TrailingElement>
|
||||||
|
<IconButton></IconButton>
|
||||||
|
</TrailingElement>
|
||||||
|
</TrailingElementGroup>
|
||||||
|
</AppBar>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
// @refresh reload
|
// @refresh reload
|
||||||
import { mount, StartClient } from "@solidjs/start/client";
|
import { mount, StartClient } from "@solidjs/start/client";
|
||||||
import { RequestContextProvider } from "./components/requestContext";
|
import { RequestContextProvider } from "./components/requestContext";
|
||||||
|
import { MetaProvider } from "@solidjs/meta";
|
||||||
|
|
||||||
mount(
|
mount(
|
||||||
() => (
|
() => (
|
||||||
<RequestContextProvider>
|
<RequestContextProvider>
|
||||||
|
<MetaProvider>
|
||||||
<StartClient />
|
<StartClient />
|
||||||
|
</MetaProvider>
|
||||||
</RequestContextProvider>
|
</RequestContextProvider>
|
||||||
),
|
),
|
||||||
document.getElementById("app")!
|
document.getElementById("app")!
|
||||||
|
@ -5,6 +5,7 @@ import { RequestContextProvider } from "~/components/requestContext";
|
|||||||
|
|
||||||
export default createHandler(() => (
|
export default createHandler(() => (
|
||||||
<RequestContextProvider>
|
<RequestContextProvider>
|
||||||
|
<MetaProvider>
|
||||||
<StartServer
|
<StartServer
|
||||||
document={({ assets, children, scripts }) => (
|
document={({ assets, children, scripts }) => (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -12,8 +13,6 @@ export default createHandler(() => (
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
{/*<link rel="icon" href="/favicon.ico" />*/}
|
{/*<link rel="icon" href="/favicon.ico" />*/}
|
||||||
<MetaProvider></MetaProvider>
|
|
||||||
<title>中V档案馆</title>
|
|
||||||
{assets}
|
{assets}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -23,5 +22,6 @@ export default createHandler(() => (
|
|||||||
</html>
|
</html>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
</MetaProvider>
|
||||||
</RequestContextProvider>
|
</RequestContextProvider>
|
||||||
));
|
));
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
import { useParams } from "@solidjs/router";
|
import { useParams } from "@solidjs/router";
|
||||||
import { createResource } from "solid-js";
|
import { createResource } from "solid-js";
|
||||||
import { Title } from "@solidjs/meta";
|
|
||||||
import { Suspense } from "solid-js";
|
import { Suspense } from "solid-js";
|
||||||
import { For } from "solid-js";
|
import { For } from "solid-js";
|
||||||
import { useCachedFetch } from "~/lib/dbCache";
|
import { useCachedFetch } from "~/lib/dbCache";
|
||||||
@ -10,25 +9,44 @@ import { bilibiliMetadata, videoSnapshot } from "~db/main/schema";
|
|||||||
import { desc, eq } from "drizzle-orm";
|
import { desc, eq } from "drizzle-orm";
|
||||||
import { BilibiliMetadataType, VideoSnapshotType } from "~db/outerSchema";
|
import { BilibiliMetadataType, VideoSnapshotType } from "~db/outerSchema";
|
||||||
import { Context, useRequestContext } from "~/components/requestContext";
|
import { Context, useRequestContext } from "~/components/requestContext";
|
||||||
|
import { Header } from "~/components/shell/Header";
|
||||||
|
|
||||||
async function getAllSnapshots(aid: number, context: Context) {
|
async function getAllSnapshots(aid: number, context: Context) {
|
||||||
"use server";
|
"use server";
|
||||||
return useCachedFetch(async () => {
|
return useCachedFetch(
|
||||||
return dbMain.select().from(videoSnapshot).where(eq(videoSnapshot.aid,aid)).orderBy(desc(videoSnapshot.createdAt));
|
async () => {
|
||||||
}, "all-snapshots", context, [aid]);
|
return dbMain
|
||||||
|
.select()
|
||||||
|
.from(videoSnapshot)
|
||||||
|
.where(eq(videoSnapshot.aid, aid))
|
||||||
|
.orderBy(desc(videoSnapshot.createdAt));
|
||||||
|
},
|
||||||
|
"all-snapshots",
|
||||||
|
context,
|
||||||
|
[aid]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getVideoMetadata(avORbv: number | string, context: Context) {
|
async function getVideoMetadata(avORbv: number | string, context: Context) {
|
||||||
"use server";
|
"use server";
|
||||||
if (typeof avORbv === "number") {
|
if (typeof avORbv === "number") {
|
||||||
return useCachedFetch(async () => {
|
return useCachedFetch(
|
||||||
|
async () => {
|
||||||
return dbMain.select().from(bilibiliMetadata).where(eq(bilibiliMetadata.aid, avORbv)).limit(1);
|
return dbMain.select().from(bilibiliMetadata).where(eq(bilibiliMetadata.aid, avORbv)).limit(1);
|
||||||
}, "bili-metadata", context, [avORbv]);
|
},
|
||||||
}
|
"bili-metadata",
|
||||||
else {
|
context,
|
||||||
return useCachedFetch(async () => {
|
[avORbv]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return useCachedFetch(
|
||||||
|
async () => {
|
||||||
return dbMain.select().from(bilibiliMetadata).where(eq(bilibiliMetadata.bvid, avORbv)).limit(1);
|
return dbMain.select().from(bilibiliMetadata).where(eq(bilibiliMetadata.bvid, avORbv)).limit(1);
|
||||||
}, "bili-metadata", context, [avORbv]);
|
},
|
||||||
|
"bili-metadata",
|
||||||
|
context,
|
||||||
|
[avORbv]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,11 +96,16 @@ export default function VideoInfoPage() {
|
|||||||
t: title
|
t: title
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
<main class="flex flex-col items-center min-h-screen gap-8 mt-10 md:mt-6 relative z-0 overflow-x-auto pb-8">
|
<main class="flex flex-col items-center min-h-screen gap-8 mt-10 md:mt-6 relative z-0 overflow-x-auto pb-8">
|
||||||
<div class="w-full lg:max-w-4xl lg:mx-auto lg:p-6">
|
<div class="w-full lg:max-w-4xl lg:mx-auto lg:p-6">
|
||||||
<Suspense fallback={<div>loading</div>}>
|
<Suspense fallback={<div>loading</div>}>
|
||||||
<Title>{data()?.t}</Title>
|
<title>{data()?.t}</title>
|
||||||
|
<span>{data()?.t}</span>
|
||||||
<h1 class="text-2xl font-medium ml-2 mb-4">
|
<h1 class="text-2xl font-medium ml-2 mb-4">
|
||||||
视频信息:{" "}
|
视频信息:{" "}
|
||||||
<a href={`https://www.bilibili.com/video/av${data()?.v.aid}`} class="underline">
|
<a href={`https://www.bilibili.com/video/av${data()?.v.aid}`} class="underline">
|
||||||
@ -106,9 +129,9 @@ export default function VideoInfoPage() {
|
|||||||
title="发布时间"
|
title="发布时间"
|
||||||
desc={
|
desc={
|
||||||
data()?.v.publishedAt
|
data()?.v.publishedAt
|
||||||
? DateTime.fromJSDate(new Date(data()?.v.publishedAt || "")).toFormat(
|
? DateTime.fromJSDate(
|
||||||
"yyyy-MM-dd HH:mm:ss"
|
new Date(data()?.v.publishedAt || "")
|
||||||
)
|
).toFormat("yyyy-MM-dd HH:mm:ss")
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -151,13 +174,21 @@ export default function VideoInfoPage() {
|
|||||||
"yyyy-MM-dd HH:mm:ss"
|
"yyyy-MM-dd HH:mm:ss"
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td class="border dark:border-zinc-500 px-4 py-2">{snapshot.views}</td>
|
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||||
<td class="border dark:border-zinc-500 px-4 py-2">{snapshot.coins}</td>
|
{snapshot.views}
|
||||||
<td class="border dark:border-zinc-500 px-4 py-2">{snapshot.likes}</td>
|
</td>
|
||||||
|
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||||
|
{snapshot.coins}
|
||||||
|
</td>
|
||||||
|
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||||
|
{snapshot.likes}
|
||||||
|
</td>
|
||||||
<td class="border dark:border-zinc-500 px-4 py-2">
|
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||||
{snapshot.favorites}
|
{snapshot.favorites}
|
||||||
</td>
|
</td>
|
||||||
<td class="border dark:border-zinc-500 px-4 py-2">{snapshot.shares}</td>
|
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||||
|
{snapshot.shares}
|
||||||
|
</td>
|
||||||
<td class="border dark:border-zinc-500 px-4 py-2">
|
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||||
{snapshot.danmakus}
|
{snapshot.danmakus}
|
||||||
</td>
|
</td>
|
||||||
@ -174,5 +205,6 @@ export default function VideoInfoPage() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|