♻️ Use keystonejs

This commit is contained in:
2024-01-22 00:28:49 +08:00
parent f0063afffa
commit 16aa7fd215
43 changed files with 13696 additions and 6448 deletions

View File

@@ -7,7 +7,7 @@ interface MenuItem {
const items: MenuItem[] = [
{ href: "/posts", label: "记录" },
{ href: "/events", label: "情报" },
{ href: "/events", label: "活动" },
{ href: "/projects", label: "企划" },
];
---

View File

@@ -13,11 +13,11 @@ const { posts } = Astro.props;
posts?.map((item) => (
<a href={`/posts/${item.slug}`}>
<div class="card sm:card-side hover:bg-base-200 transition-colors sm:max-w-none shadow-xl">
{item.heroImg && (
{item.cover.image.url && (
<figure class="mx-auto w-full object-cover p-6 max-sm:pb-0 sm:max-w-[12rem] sm:pe-0">
<img
loading="lazy"
src={item.heroImg}
src={item.cover.image.url}
class="border-base-content bg-base-300 rounded-btn border border-opacity-5"
alt={item.title}
/>
@@ -25,13 +25,13 @@ const { posts } = Astro.props;
)}
<div class="card-body">
<h2 class="text-xl">{item.title}</h2>
<div>
<div class="mx-[-2px] mt-[-4px]">
<span class="badge badge-accent">{POST_TYPES[item.type]}</span>
{item.categories?.map((category: string) => (
<span class="badge badge-primary">{category}</span>
{item.categories?.map((category: any) => (
<span class="badge badge-primary">{category.name}</span>
))}
{item.tags?.map((tag: string) => (
<span class="badge badge-secondary">{tag}</span>
{item.tags?.map((tag: any) => (
<span class="badge badge-secondary">{tag.name}</span>
))}
</div>
<div class="text-xs opacity-60 line-clamp-3">

View File

@@ -1,9 +1,9 @@
import { useState, Fragment } from "react";
export default function Video({
export default function Media({
sources,
}: {
sources: { caption: string; url: string }[];
sources: { caption: string; url: string; type: string }[];
}) {
const [focus, setFocus] = useState<boolean[]>(
sources.map((_, idx) => idx === 0)
@@ -30,9 +30,16 @@ export default function Video({
role="tabpanel"
className="tab-content bg-base-100 border-base-300 rounded-box"
>
<video className="mb-0 block w-full h-[360px]" controls>
{item.type === "video" && (
<video className="mb-0 block w-full h-[360px]" controls>
<source src={item.url} />
</video>
</video>
)}
{item.type === "audio" && (
<audio className="mb-0 block w-full h-[20px]" controls>
<source src={item.url} />
</audio>
)}
</div>
</Fragment>
))}

View File

@@ -1,33 +1,48 @@
---
import PageLayout from "../../layouts/PageLayout.astro";
import { client } from "../../../tina/__generated__/client";
import PostList from "../../components/PostList.astro";
import { graphQuery } from "../../scripts/requests";
export const prerender = false;
const { slug } = Astro.params;
const postsResponse = await client.queries.postConnection({
filter: { categories: { in: [slug ?? "index"] } },
});
const posts = postsResponse.data.postConnection.edges
?.sort((a, b) =>
new Date(a?.node?.date ?? 0).getTime() <=
new Date(b?.node?.date ?? 0).getTime()
? -1
: 0
const { posts } = (
await graphQuery(
`query Query($where: PostWhereInput!) {
posts(where: $where) {
slug
type
title
description
cover {
image {
url
}
}
content {
document
}
categories {
name
}
tags {
name
}
createdAt
}
}`,
{ where: { categories: { some: { slug: { equals: slug } } } } }
)
.map((event) => {
return { ...event?.node, slug: event?.node?._sys.filename };
});
).data;
---
<PageLayout>
<div class="max-w-[720px] mx-auto">
<div class="pt-16 pb-6 px-6">
<h1 class="text-4xl font-bold">分类检索</h1>
<p class="pt-3">以下是包含「{slug}」分类的记录……</p>
<p class="pt-3">以下是包含分类的记录……</p>
</div>
<PostList posts={posts as any[]} />

View File

@@ -1,29 +1,40 @@
---
import PageLayout from "../../layouts/PageLayout.astro";
import { client } from "../../../tina/__generated__/client";
import { TinaMarkdown } from "tinacms/dist/rich-text";
import { graphQuery } from "../../scripts/requests";
import { DocumentRenderer } from "@keystone-6/document-renderer";
export const prerender = false;
const eventsResponse = await client.queries.eventConnection();
const events = eventsResponse.data.eventConnection.edges
?.sort((a, b) =>
new Date(a?.node?.date ?? 0).getTime() <=
new Date(b?.node?.date ?? 0).getTime()
? -1
: 0
const { events } = (
await graphQuery(
`query Query($where: EventWhereInput!) {
events(where: $where) {
slug
title
description
content {
document
}
createdAt
}
}`,
{
where: {
isHistory: {
equals: true,
},
},
}
)
.map((event) => {
return { ...event?.node, slug: event?.node?._sys.filename };
});
).data;
---
<PageLayout>
<div class="max-w-[720px] mx-auto">
<div class="card w-full shadow-xl">
<div class="card-body">
<h2 class="card-title">情报</h2>
<h2 class="card-title">活动</h2>
<p>读岁月史书,涨人生阅历</p>
<div class="divider"></div>
@@ -32,8 +43,8 @@ const events = eventsResponse.data.eventConnection.edges
>
{
events?.map((item: any, idx: number) => {
let align = idx % 2 === 0 ? "start" : "end";
let textAlign = idx % 2 === 0 ? "left" : "right";
let align = idx % 2 === 0 ? "timeline-start" : "timeline-end";
let textAlign = idx % 2 === 0 ? "md:text-right" : "md:text-left";
return (
<li>
@@ -52,12 +63,12 @@ const events = eventsResponse.data.eventConnection.edges
/>
</svg>
</div>
<div class={`timeline-${align} md:text-${textAlign} mb-10`}>
<div class={`${align} ${textAlign} mb-10`}>
<time class="font-mono italic">
{new Date(item.date).toLocaleDateString()}
{new Date(item.createdAt).toLocaleDateString()}
</time>
<div class="text-lg font-black">{item.title}</div>
<TinaMarkdown content={item._body} />
<DocumentRenderer document={item.content.document} />
</div>
<hr />
</li>
@@ -65,8 +76,10 @@ const events = eventsResponse.data.eventConnection.edges
})
}
</ul>
<div class="text-center max-md:text-left italic">我们的故事还在继续……</div>
<div class="text-center max-md:text-left italic">
我们的故事还在继续……
</div>
</div>
</div>
</div>
</PageLayout>
</PageLayout>

View File

@@ -1,19 +1,27 @@
---
import RootLayout from "../layouts/RootLayout.astro";
import { client } from "../../tina/__generated__/client";
import { graphQuery } from "../scripts/requests";
const eventsResponse = await client.queries.eventConnection();
const events = eventsResponse.data.eventConnection.edges
?.sort((a, b) =>
new Date(a?.node?.date ?? 0).getTime() <=
new Date(b?.node?.date ?? 0).getTime()
? -1
: 0
const { events } = (
await graphQuery(
`query Query($where: EventWhereInput!) {
events(where: $where) {
slug
title
description
createdAt
}
}`,
{
where: {
isHistory: {
equals: true,
},
},
}
)
.map((event) => {
return { ...event?.node, slug: event?.node?._sys.filename };
});
).data;
---
<RootLayout>
@@ -140,7 +148,7 @@ const events = eventsResponse.data.eventConnection.edges
<li>
{idx > 0 && <hr />}
<div class="timeline-start">
{new Date(item.date).toLocaleDateString()}
{new Date(item.createdAt).toLocaleDateString()}
</div>
<div class="timeline-middle">
<svg

View File

@@ -1,41 +1,78 @@
---
import PageLayout from "../../layouts/PageLayout.astro";
import Video from "../../components/posts/Video.tsx";
// @ts-ignore
import Media from "../../components/posts/Media";
import { POST_TYPES } from "../../scripts/consts";
import { client } from "../../../tina/__generated__/client";
import { TinaMarkdown } from "tinacms/dist/rich-text";
import { graphQuery } from "../../scripts/requests";
import { DocumentRenderer } from "@keystone-6/document-renderer";
export const prerender = false;
const { slug } = Astro.params;
const components = {
Video,
}
const { data } = await client.queries.post({
relativePath: (slug ?? "index") + ".mdx",
});
const { post } = (
await graphQuery(
`query Query($where: PostWhereUniqueInput!) {
post(where: $where) {
slug
type
title
description
assets {
caption
url
type
}
cover {
image {
url
}
}
content {
document
}
categories {
slug
name
}
tags {
slug
name
}
createdAt
}
}`,
{ where: { slug } }
)
).data;
---
<PageLayout>
<div class="wrapper">
<div class="card w-full shadow-xl">
{
data.post.heroImg && (
post.cover != null && (
<figure>
<img src={data.post.heroImg} alt={data.post.title} />
<img src={post.cover.image.url} alt={post.title} />
</figure>
)
}
<div class="card-body">
<h2 class="card-title">{data.post.title}</h2>
<p class="description">{data.post.description ?? "No description"}</p>
<h2 class="card-title">{post.title}</h2>
<p class="description">{post.description ?? "No description"}</p>
<div class="divider"></div>
{
post.assets?.length > 0 && (
<div class="mb-5">
<Media sources={post.assets} />
</div>
)
}
<div class="prose max-w-none">
<TinaMarkdown content={data.post._body} components={components} />
<DocumentRenderer document={post.content.document} />
</div>
</div>
</div>
@@ -46,19 +83,24 @@ const { data } = await client.queries.post({
<div class="gap-2 text-sm metadata description">
<div>
<div>作者</div>
<div>{data.post.author?.name ?? "佚名"}</div>
<div>{post.author?.name ?? "佚名"}</div>
</div>
<div>
<div>类型</div>
<div class="text-accent">{POST_TYPES[data.post.type as unknown as string]}</div>
<div class="text-accent">
{POST_TYPES[post.type as unknown as string]}
</div>
</div>
<div>
<div>分类</div>
<div class="flex gap-1">
{
data.post.categories?.map((category) => (
<a href={`/categories/${category}`} class="link link-primary">
{category}
post.categories?.map((category: any) => (
<a
href={`/categories/${category.slug}`}
class="link link-primary"
>
{category.name}
</a>
))
}
@@ -68,9 +110,9 @@ const { data } = await client.queries.post({
<div>标签</div>
<div class="flex gap-1">
{
data.post.tags?.map((tag) => (
<a href={`/tags/${tag}`} class="link link-secondary">
{tag}
post.tags?.map((tag: any) => (
<a href={`/tags/${tag.slug}`} class="link link-secondary">
{tag.name}
</a>
))
}
@@ -78,7 +120,7 @@ const { data } = await client.queries.post({
</div>
<div>
<div>发布于</div>
<div>{new Date(data.post.date ?? 0).toLocaleString()}</div>
<div>{new Date(post.createdAt).toLocaleString()}</div>
</div>
</div>
</div>

View File

@@ -1,30 +1,48 @@
---
import PageLayout from "../../layouts/PageLayout.astro";
import { client } from "../../../tina/__generated__/client";
import PostList from "../../components/PostList.astro";
import { graphQuery } from "../../scripts/requests";
export const prerender = false;
const postsResponse = await client.queries.postConnection();
const posts = postsResponse.data.postConnection.edges
?.sort((a, b) =>
new Date(a?.node?.date ?? 0).getTime() <=
new Date(b?.node?.date ?? 0).getTime()
? -1
: 0
const { posts } = (
await graphQuery(
`query Query($where: PostWhereInput!) {
posts(where: $where) {
slug
type
title
description
cover {
image {
url
}
}
content {
document
}
categories {
name
}
tags {
name
}
createdAt
}
}`,
{ where: {} }
)
.map((event) => {
return { ...event?.node, slug: event?.node?._sys.filename };
});
).data;
---
<PageLayout>
<div class="max-w-[720px] mx-auto">
<div class="pt-16 pb-6 px-6">
<h1 class="text-4xl font-bold">记录</h1>
<p class="pt-3">记录生活,记录理想,记录记录……</p>
<p class="pt-2">记录生活,记录理想,记录记录……</p>
</div>
<PostList posts={posts as any[]} />
</div>
</PageLayout>

View File

@@ -1,33 +1,48 @@
---
import PageLayout from "../../layouts/PageLayout.astro";
import { client } from "../../../tina/__generated__/client";
import PostList from "../../components/PostList.astro";
import { graphQuery } from "../../scripts/requests";
export const prerender = false;
const { slug } = Astro.params;
const postsResponse = await client.queries.postConnection({
filter: { tags: { in: [slug ?? "index"] } },
});
const posts = postsResponse.data.postConnection.edges
?.sort((a, b) =>
new Date(a?.node?.date ?? 0).getTime() <=
new Date(b?.node?.date ?? 0).getTime()
? -1
: 0
const { posts } = (
await graphQuery(
`query Query($where: PostWhereInput!) {
posts(where: $where) {
slug
type
title
description
cover {
image {
url
}
}
content {
document
}
categories {
name
}
tags {
name
}
createdAt
}
}`,
{ where: { tags: { some: { slug: { equals: slug } } } } }
)
.map((event) => {
return { ...event?.node, slug: event?.node?._sys.filename };
});
).data;
---
<PageLayout>
<div class="max-w-[720px] mx-auto">
<div class="pt-16 pb-6 px-6">
<h1 class="text-4xl font-bold">标签检索</h1>
<p class="pt-3">以下是包含「{slug}」标签的记录……</p>
<p class="pt-3">以下是包含标签的记录……</p>
</div>
<PostList posts={posts as any[]} />

15
src/scripts/requests.ts Normal file
View File

@@ -0,0 +1,15 @@
export async function graphQuery(query: string, variables: any) {
const response = await fetch(
`${import.meta.env.PUBLIC_CMS}/api/graphql`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query,
variables,
}),
}
);
return await response.json();
}