From a592828feb78a3fbf08b58faa9f4d1bd0a5ea234 Mon Sep 17 00:00:00 2001 From: jasinco Date: Tue, 10 Jun 2025 23:38:19 +0800 Subject: [PATCH] Add infinite scroll and modify fetch api to isolate the main object from list --- bun.lock | 5 ++++ package.json | 1 + src/App.tsx | 5 +++- src/Fetch.tsx | 73 +++++++++++++++++++++++++++++++++++---------------- src/Post.tsx | 4 +-- src/api.ts | 21 +++++++-------- 6 files changed, 73 insertions(+), 36 deletions(-) diff --git a/bun.lock b/bun.lock index 128dc1c..8769051 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "name": "nimfront", "dependencies": { "@tailwindcss/vite": "^4.1.8", + "@tanstack/react-query": "^5.80.6", "prettier": "^3.5.3", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -233,6 +234,10 @@ "@tailwindcss/vite": ["@tailwindcss/vite@4.1.8", "", { "dependencies": { "@tailwindcss/node": "4.1.8", "@tailwindcss/oxide": "4.1.8", "tailwindcss": "4.1.8" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-CQ+I8yxNV5/6uGaJjiuymgw0kEQiNKRinYbZXPdx1fk5WgiyReG0VaUx/Xq6aVNSUNJFzxm6o8FNKS5aMaim5A=="], + "@tanstack/query-core": ["@tanstack/query-core@5.80.6", "", {}, "sha512-nl7YxT/TAU+VTf+e2zTkObGTyY8YZBMnbgeA1ee66lIVqzKlYursAII6z5t0e6rXgwUMJSV4dshBTNacNpZHbQ=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.80.6", "", { "dependencies": { "@tanstack/query-core": "5.80.6" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-izX+5CnkpON3NQGcEm3/d7LfFQNo9ZpFtX2QsINgCYK9LT2VCIdi8D3bMaMSNhrAJCznRoAkFic76uvLroALBw=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], diff --git a/package.json b/package.json index 410885b..ec93e0a 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.1.8", + "@tanstack/react-query": "^5.80.6", "prettier": "^3.5.3", "react": "^19.1.0", "react-dom": "^19.1.0", diff --git a/src/App.tsx b/src/App.tsx index cb34a8e..78c8cd8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,9 @@ import PostForm from './Post' import { Zone } from './Zone' import { Delete } from './Delete' import { Fetch } from './Fetch' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +const queryClient = new QueryClient() function App() { const [zone, setZone] = useState("post") @@ -12,7 +15,7 @@ function App() { <> {zone == "post" && } {zone == "delete" && } - {zone == "view" && } + {zone == "view" && } diff --git a/src/Fetch.tsx b/src/Fetch.tsx index c68813c..4ef89e7 100644 --- a/src/Fetch.tsx +++ b/src/Fetch.tsx @@ -1,31 +1,23 @@ -import { useEffect, useState } from "react" -import { FetchPost, FetchPost_t, MediaPathTrc } from "./api" +import React from "react" +import { FetchPost, MediaPathTrc, SinglePost_t } from "./api" import { z } from 'zod/v4' +import { useInfiniteQuery } from "@tanstack/react-query" -type fetched = z.infer +// type fetched = z.infer -export const Fetch = () => { - let [fetched, setFetched] = useState([]) - useEffect(() => { - FetchPost().then(e => { - if (e) { - setFetched(e) - } - } - ) - }, []) - return (
- {fetched.map(post =>
+const Single = ({ post }: { post: z.infer }) => { + return ( +

{post.content}

{post.enclosure.length > 0 && post.enclosure[0] != null &&
{post.enclosure.map((enc, index) => { if (enc) { return {`Post + key={index} + src={MediaPathTrc(enc)} + alt={`Post media ${post.id}-${index}`} + className="rounded-md h-32 object-cover aspect-video flex-shrink-0" + /> } else { return "" } @@ -36,6 +28,43 @@ export const Fetch = () => {

PID: {post.id}

發文於: {post.post_at.toLocaleTimeString()}

-
)} -
) +
+ ) +} + +export const Fetch = () => { + const { + data, + error, + fetchNextPage, + hasNextPage, + isFetching, + status, + } = useInfiniteQuery({ + queryKey: ['id'], + queryFn: async ({ pageParam }) => { + return await FetchPost(pageParam == 0 ? undefined : pageParam) + }, + initialPageParam: 0, + getNextPageParam: (lastPage, _) => { + return lastPage !== null && lastPage.length > 0 && (lastPage[lastPage.length - 1].id - 1) > 0 ? lastPage[lastPage.length - 1].id - 1 : undefined + }, + }) + + return ( +
hasNextPage && !isFetching && fetchNextPage()}> + {status === 'pending' &&

Loading

} + {status === 'error' &&

Error: {error.message}

} + {data && data.pages.map((group, i) => ( + + {group && group.map(post => ( + + ))} + + ))} + {hasNextPage &&
+
} + {!hasNextPage &&

見底了

} +
+ ) } diff --git a/src/Post.tsx b/src/Post.tsx index ff3b335..8e27f0a 100644 --- a/src/Post.tsx +++ b/src/Post.tsx @@ -113,7 +113,7 @@ export default function PostForm() {
{/* Aligned items, added items-center */} ) -} \ No newline at end of file +} diff --git a/src/api.ts b/src/api.ts index c9453a2..e78ed21 100644 --- a/src/api.ts +++ b/src/api.ts @@ -6,18 +6,17 @@ type Inputs = { signing?: string files?: FileList } +export const SinglePost_t = z.object({ + id: z.number(), + content: z.string(), + signing: z.nullable(z.string()), + post_at: z.coerce.date(), + heart: z.number(), + igid: z.nullable(z.string()), + enclosure: z.array(z.nullable(z.string())), +}) -export const FetchPost_t = z.array( - z.object({ - id: z.number(), - content: z.string(), - signing: z.nullable(z.string()), - post_at: z.coerce.date(), - heart: z.number(), - igid: z.nullable(z.string()), - enclosure: z.array(z.nullable(z.string())), - }) -) +export const FetchPost_t = z.array(SinglePost_t) export const Post = async (post_form: Inputs) => { // Delete Blank Item