diff --git a/package.json b/package.json index b0f2d38..28cf9e9 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "lucide-react": "^0.477.0", "next": "^15.0.1", "next-auth": "5.0.0-beta.25", - "next-safe-action": "^7.10.4", "next-themes": "^0.4.4", "novel": "^1.0.2", "postgres": "^3.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79b0961..8a5d974 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,9 +122,6 @@ importers: next-auth: specifier: 5.0.0-beta.25 version: 5.0.0-beta.25(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) - next-safe-action: - specifier: ^7.10.4 - version: 7.10.4(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.24.2) next-themes: specifier: ^0.4.4 version: 0.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3572,27 +3569,6 @@ packages: nodemailer: optional: true - next-safe-action@7.10.4: - resolution: {integrity: sha512-rZE89DTNgiTJ8oPQBOZm+jd0Uf/pLkN+GE3PndER6vWqHGYGN7HMLvhpggkbE8W4TGp+qM7CPyrXye1wCjZLVg==} - engines: {node: '>=18.17'} - peerDependencies: - '@sinclair/typebox': '>= 0.33.3' - next: '>= 14.0.0' - react: '>= 18.2.0' - react-dom: '>= 18.2.0' - valibot: '>= 0.36.0' - yup: '>= 1.0.0' - zod: '>= 3.0.0' - peerDependenciesMeta: - '@sinclair/typebox': - optional: true - valibot: - optional: true - yup: - optional: true - zod: - optional: true - next-themes@0.4.4: resolution: {integrity: sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==} peerDependencies: @@ -7182,8 +7158,8 @@ snapshots: '@typescript-eslint/parser': 8.26.0(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.4(eslint@8.57.1) eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1) @@ -7202,7 +7178,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1): + eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0(supports-color@5.5.0) @@ -7213,18 +7189,18 @@ snapshots: stable-hash: 0.0.4 tinyglobby: 0.2.12 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.26.0(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.8.3(eslint-plugin-import@2.31.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -7232,7 +7208,7 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.3)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -7243,7 +7219,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.3)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -8260,14 +8236,6 @@ snapshots: next: 15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - next-safe-action@7.10.4(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.24.2): - dependencies: - next: 15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - zod: 3.24.2 - next-themes@0.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 diff --git a/src/components/comment/comment-form.tsx b/src/components/comment/comment-form.tsx index d42f444..15a1beb 100644 --- a/src/components/comment/comment-form.tsx +++ b/src/components/comment/comment-form.tsx @@ -15,7 +15,6 @@ import { } from "@/components/ui/form"; import { commentSchema } from "@/lib/validation/zod/comment"; import CommentEditor from "../editor/comment-editor"; -import { createComment } from "@/server/actions/comment"; import { toast } from "sonner"; import { EditorInstance } from "novel"; import { XIcon } from "lucide-react"; @@ -39,8 +38,15 @@ function CommentForm({ onSuccess: (comment) => { setLoading(false); socket?.emit(ADD_COMMENT_EVENT, comment); - toast.success("Kommentar hinzugefügt."); + setTimeout(() => { + const el = commentRefs.current.get(comment?.id!); + el?.scrollIntoView({ + behavior: "smooth", + block: "start", + }); + el?.classList.add("animate-fade-in"); + }, 500); }, onError: () => { setLoading(false); @@ -65,15 +71,6 @@ function CommentForm({ form.reset(); editor?.commands?.clearContent(); removeReplyComment(); - // setTimeout(() => { - // const el = commentRefs.current.get(res?.data?.data!); - // console.log("el", el); - // el?.scrollIntoView({ - // behavior: "smooth", - // block: "start", - // }); - // el?.classList.add("animate-fade-in"); - // }, 500); } React.useEffect(() => { @@ -116,6 +113,7 @@ function CommentForm({ type="submit" className="ml-auto h-max py-1" size="sm" + disabled={loading || !form.formState.isDirty} > Absenden diff --git a/src/components/comment/comment-list.tsx b/src/components/comment/comment-list.tsx index a7461a7..f06ddab 100644 --- a/src/components/comment/comment-list.tsx +++ b/src/components/comment/comment-list.tsx @@ -4,7 +4,7 @@ import { buildCommentTree } from "@/lib/utils/comments"; import Comment from "./comment"; import { useSession } from "../session-provider"; -function CommentList({ +export const CommentList = React.memo(function ({ comments, setReplyComment, commentRefs, @@ -29,5 +29,5 @@ function CommentList({ ))} ); -} +}); export default CommentList; diff --git a/src/components/comment/comment.tsx b/src/components/comment/comment.tsx index 9c37e99..c84d66e 100644 --- a/src/components/comment/comment.tsx +++ b/src/components/comment/comment.tsx @@ -19,32 +19,22 @@ const Comment = React.memo( level?: number; }) => { const { comment, setReplyComment, setRef, session, level = 0 } = props; - const isAuthor = session?.user?.id === comment.author?.id; + + const isAuthor = session?.user?.id === comment?.author?.id; return ( - //
- - //
- //
- // {comment.children.map((child) => ( - // setRef(el)} // Ensure this is stable - // session={session} - // level={level + 1} - // /> - // ))} - //
- // -
0 && "ml-4 border-l", - comment.children.length && "border-l", + comment?.children?.length && "border-l", + comment?.missingParent && "border-l", )} > + {comment.missingParent && ( +
+ Antwort auf: Kommentar wurde gelöscht +
+ )}
- {comment.author?.name} {level} + {comment.author?.name}
{isAuthor && ( @@ -85,39 +75,21 @@ const Comment = React.memo(
- {comment.children && comment.children.length > 0 && ( + {comment?.children && comment.children.length > 0 && (
- {comment.children.map((child) => ( + {comment.children.map((child, idx) => ( ))}
)} - //
0 ? "ml-4" : ""} w-full`}> - //
0 ? "border-l" : ""} w-full pl-2`} - // > - - // {comment.children && comment.children.length > 0 && ( - //
- // {comment.children.map((child) => ( - // - // ))} - //
- // )} - //
- //
); }, ); + export default Comment; diff --git a/src/lib/utils/comments.ts b/src/lib/utils/comments.ts index 76db452..4d558dc 100644 --- a/src/lib/utils/comments.ts +++ b/src/lib/utils/comments.ts @@ -9,28 +9,32 @@ export function buildCommentTree(comments: Comment[]): Array { commentMap[comment.id] = { ...comment, children: [], + missingParent: false, }; }); // Second pass: Build the tree by connecting children to parents - const rootComments: CommentNode[] = []; + const rootComments: Array = []; comments.forEach((comment) => { - const node = commentMap[comment.id]; + const node = commentMap[comment.id]!; // If parentId is null or empty string, it's a root comment if (!comment.parentId) { - rootComments.push(node!); + rootComments.push(node); } else { - // Add as a child to its parent if parent exists const parent = commentMap[comment.parentId]; if (parent) { - parent.children.push(node!); + // Add as a child to its parent if parent exists + parent.children.push(node); } else { // Parent doesn't exist, treat as root comment + // Mark that the parent is missing + node.missingParent = true; rootComments.push(node!); } } }); + return rootComments; } diff --git a/src/server/actions/comment.ts b/src/server/actions/comment.ts deleted file mode 100644 index 443e486..0000000 --- a/src/server/actions/comment.ts +++ /dev/null @@ -1,48 +0,0 @@ -"use server"; - -import { appRoutes } from "@/config"; -import { commentSchema } from "@/lib/validation/zod/comment"; -import { api } from "@/trpc/server"; -import { revalidatePath } from "next/cache"; -import { z } from "zod"; -import { actionClient } from "."; - -export const createComment = actionClient - .schema( - z.object({ - comment: commentSchema, - articleId: z.string(), - }), - ) - .action(async ({ parsedInput: { comment, articleId } }) => { - const commentId = await api.comment.create({ - comment, - articleId, - }); - if (!commentId) - return { - failure: "Incorrect credentials", - }; - revalidatePath(appRoutes.article(articleId)); - return { - data: commentId, - success: true, - }; - }); - -export async function deleteComment(commentId: string) { - const articleId = ( - await api.comment.delete({ - commentId, - }) - )?.articleId; - if (!articleId) - return { - success: false, - message: "Error deleting comment", - }; - revalidatePath(appRoutes.article(articleId)); - return { - success: true, - }; -} diff --git a/src/server/actions/index.ts b/src/server/actions/index.ts deleted file mode 100644 index 7f62198..0000000 --- a/src/server/actions/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createSafeActionClient } from "next-safe-action"; - -export const actionClient = createSafeActionClient(); diff --git a/src/server/db/schema/comments.ts b/src/server/db/schema/comments.ts index 31e374c..7eb08f5 100644 --- a/src/server/db/schema/comments.ts +++ b/src/server/db/schema/comments.ts @@ -60,7 +60,8 @@ export type Comment = typeof comments.$inferSelect & { }; export type CommentNode = Comment & { - children: CommentNode[]; + children: Array; + missingParent: boolean; }; export const commentVotes = createTable( diff --git a/src/server/socket/server.ts b/src/server/socket/server.ts index f96249b..7354cc5 100644 --- a/src/server/socket/server.ts +++ b/src/server/socket/server.ts @@ -19,7 +19,7 @@ const createSocketServer = () => { if (!global.io) { global.io = new Server(server, { cors: { - origin, + origin: "*", methods: ["GET", "POST"], }, });