diff --git a/client/src/App.tsx b/client/src/App.tsx index d73d7112..c64fc00f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -11,6 +11,7 @@ import { FriendsPage } from './page/friends' import { WritingPage } from './page/writing' import { Profile, ProfileContext } from './state/profile' import { headersWithAuth } from './utils/auth' +import { TimelinePage } from './page/timeline' function App() { const ref = useRef(false) const [profile, setProfile] = useState() @@ -38,6 +39,10 @@ function App() { + + + + {params => } diff --git a/client/src/components/header.tsx b/client/src/components/header.tsx index e13263e4..cdb99b2c 100644 --- a/client/src/components/header.tsx +++ b/client/src/components/header.tsx @@ -40,7 +40,7 @@ export function Header() { - {/* { }} /> */} + {profile?.permission && } diff --git a/client/src/index.css b/client/src/index.css index c99b0f14..568cacb1 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -42,7 +42,7 @@ ul { } .t-secondary { - @apply text-neutral-800 dark:text-neutral-200; + @apply text-neutral-500 dark:text-neutral-200; } .wauto { diff --git a/client/src/page/timeline.tsx b/client/src/page/timeline.tsx new file mode 100644 index 00000000..db9c8ad7 --- /dev/null +++ b/client/src/page/timeline.tsx @@ -0,0 +1,79 @@ +import { useEffect, useRef, useState } from "react" +import { Link } from "wouter" +import { Waiting } from "../components/loading" +import { client } from "../main" +import { headersWithAuth } from "../utils/auth" + +export function TimelinePage() { + const [feeds, setFeeds] = useState>>() + const [length, setLength] = useState(0) + const ref = useRef(false) + function fetchFeeds() { + client.feed.timeline.get({ + headers: headersWithAuth() + }).then(({ data }) => { + if (data && typeof data != 'string') { + setLength(data.length) + const groups = Object.groupBy(data, ({ createdAt }) => new Date(createdAt).getFullYear()) + setFeeds(groups) + } + }) + } + useEffect(() => { + if (ref.current) return + fetchFeeds() + ref.current = true + }, []) + return ( + <> + +
+
+

+ 时间轴 +

+
+

+ 共有 {length} 篇文章 +

+
+
+ {feeds && Object.keys(feeds).sort((a, b) => parseInt(b) - parseInt(a)).map(year => ( +
+

+ + {year} 年 + + {feeds[+year]?.length} 篇 +

+
+ {feeds[+year]?.map(({ id, title, createdAt }) => ( + + ))} +
+
+ ))} +
+
+ + ) +} + +export function FeedItem({ id, title, createdAt }: { id: string, title: string, createdAt: Date }) { + const formatter = new Intl.DateTimeFormat('en-US', { day: '2-digit', month: '2-digit' }); + return ( +
+
+
+
+
+ + {formatter.format(new Date(createdAt))} + + + {title} + +
+
+ ) +} \ No newline at end of file diff --git a/server/src/services/feed.ts b/server/src/services/feed.ts index 4bb28551..fbe34ff6 100644 --- a/server/src/services/feed.ts +++ b/server/src/services/feed.ts @@ -83,6 +83,19 @@ export const FeedService = (db: DB, env: Env) => new Elysia({ aot: false }) type: t.Optional(t.String()) }) }) + .get('/timeline', async () => { + const where = and(eq(feeds.draft, 0), eq(feeds.listed, 1)); + const feed_list = (await db.query.feeds.findMany({ + where: where, + columns: { + id: true, + title: true, + createdAt: true, + }, + orderBy: [desc(feeds.createdAt), desc(feeds.updatedAt)], + })) + return feed_list + }) .post('/', async ({ admin, set, uid, body: { title, alias, listed, content, summary, draft, tags } }) => { if (!admin) { set.status = 403;