diff --git a/src/app/api/posts/[id]/comments/route.ts b/src/app/api/posts/[id]/comments/route.ts
new file mode 100644
index 0000000..0ea4bd1
--- /dev/null
+++ b/src/app/api/posts/[id]/comments/route.ts
@@ -0,0 +1,36 @@
+import { LimitType } from "@lens-protocol/client";
+import { type NextRequest, NextResponse } from "next/server";
+import { lensItemToPost } from "~/components/post/Post";
+import { getLensClient } from "~/utils/getLensClient";
+
+export const dynamic = "force-dynamic";
+
+export async function GET(req: NextRequest, { params }: { params: { id: string } }) {
+ const id = params.id;
+ const cursor = req.nextUrl.searchParams.get("cursor") ?? undefined;
+
+ if (!id) {
+ return NextResponse.json({ error: "Missing publication id" }, { status: 400 });
+ }
+
+ try {
+ const { client } = await getLensClient();
+
+ const comments = await client.publication.fetchAll({
+ where: { commentOn: { id } },
+ limit: LimitType.TwentyFive,
+ cursor,
+ });
+
+ if (!comments.items) {
+ throw new Error("No comments found");
+ }
+
+ const commentsPosts = comments.items.map((comment) => lensItemToPost(comment));
+
+ return NextResponse.json({ comments: commentsPosts, nextCursor: comments.pageInfo.next }, { status: 200 });
+ } catch (error) {
+ console.error("Failed to load comments: ", error.message);
+ return NextResponse.json({ error: `${error.message}` }, { status: 500 });
+ }
+}
diff --git a/src/components/LoadingIcon.tsx b/src/components/LoadingIcon.tsx
index 7734c71..3f0497a 100644
--- a/src/components/LoadingIcon.tsx
+++ b/src/components/LoadingIcon.tsx
@@ -1,5 +1,9 @@
import { LoaderCircleIcon } from "lucide-react";
-export const LoadingSpinner = ({ size = 22 }: { size?: number }) => {
- return ;
+export const LoadingSpinner = ({ size = 22, className }: { size?: number; className?: string }) => {
+ return (
+
+
+
+ );
};
diff --git a/src/components/post/PostComments.tsx b/src/components/post/PostComments.tsx
index 3835ff4..330ca72 100644
--- a/src/components/post/PostComments.tsx
+++ b/src/components/post/PostComments.tsx
@@ -1,28 +1,70 @@
+import { PlusIcon } from "lucide-react";
+import React, { useState, useEffect, useCallback } from "react";
+import { LoadingSpinner } from "../LoadingIcon";
+import { Button } from "../ui/button";
import type { Post } from "./Post";
import { PostView } from "./PostView";
import PostWizard from "./PostWizard";
export const PostComments = ({ post, isExpanded }: { post: Post; isExpanded: boolean }) => {
- const comments = post.comments.map((comment, index) => (
+ const [comments, setComments] = useState(post.comments);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [cursor, setCursor] = useState(undefined);
+
+ useEffect(() => {
+ isExpanded ? loadMoreComments() : setComments(post.comments);
+ }, [isExpanded]);
+
+ const loadMoreComments = useCallback(async () => {
+ if (loading) return;
+ setLoading(true);
+ try {
+ const res = await fetch(`/api/posts/${post.id}/comments?${cursor ? `cursor=${cursor}` : ""}`, {
+ method: "GET",
+ });
+ if (!res.ok) throw new Error(res.statusText);
+ const { comments: newComments, nextCursor } = await res.json();
+ const diffComments = newComments.filter((comment) => !post.comments.find((c) => c.id === comment.id));
+ setComments((prevComments) => [...prevComments, ...diffComments]);
+ setCursor(nextCursor);
+ } catch (err) {
+ setError(`Could not fetch comments: ${err.message}`);
+ } finally {
+ setLoading(false);
+ }
+ }, [cursor, loading, post.id]);
+
+ const commentElements = comments.map((comment, index) => (
));
+ if (error) throw new Error(error);
+
return (
- {isExpanded && (
-
- )}
-
+
+ {isExpanded && (
+
+ )}
+
+ {loading &&
}
+ {isExpanded && cursor && !loading && (
+
+ )}
+
);
};