added deleted comment indicator
This commit is contained in:
parent
4a67f1f94b
commit
b2cd0fc560
@ -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",
|
||||
|
||||
48
pnpm-lock.yaml
generated
48
pnpm-lock.yaml
generated
@ -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
|
||||
|
||||
@ -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
|
||||
</Button>
|
||||
|
||||
@ -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({
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
});
|
||||
export default CommentList;
|
||||
|
||||
@ -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 (
|
||||
// <div className="">
|
||||
|
||||
// </div>
|
||||
// <div className="comment-replies" style={{ marginLeft: "20px" }}>
|
||||
// {comment.children.map((child) => (
|
||||
// <Comment
|
||||
// key={child.id}
|
||||
// comment={child}
|
||||
// setReplyComment={setReplyComment}
|
||||
// setRef={(el) => setRef(el)} // Ensure this is stable
|
||||
// session={session}
|
||||
// level={level + 1}
|
||||
// />
|
||||
// ))}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
level > 0 && "ml-4 border-l",
|
||||
comment.children.length && "border-l",
|
||||
comment?.children?.length && "border-l",
|
||||
comment?.missingParent && "border-l",
|
||||
)}
|
||||
>
|
||||
{comment.missingParent && (
|
||||
<div className="p-4 pt-0 text-sm italic text-muted-foreground">
|
||||
Antwort auf: Kommentar wurde gelöscht
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="flex items-start gap-2 pl-2 hover:bg-muted"
|
||||
ref={setRef}
|
||||
@ -57,7 +47,7 @@ const Comment = React.memo(
|
||||
<div className="w-full">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<h5 className="text-lg font-medium capitalize">
|
||||
{comment.author?.name} {level}
|
||||
{comment.author?.name}
|
||||
</h5>
|
||||
{isAuthor && (
|
||||
<Badge variant={"outline"} className="text-xs">
|
||||
@ -85,39 +75,21 @@ const Comment = React.memo(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{comment.children && comment.children.length > 0 && (
|
||||
{comment?.children && comment.children.length > 0 && (
|
||||
<div className="pl-4">
|
||||
{comment.children.map((child) => (
|
||||
{comment.children.map((child, idx) => (
|
||||
<Comment
|
||||
key={child.id}
|
||||
key={child?.id ?? idx}
|
||||
{...props}
|
||||
comment={child}
|
||||
comment={child as CommentNode}
|
||||
level={level + 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
// <div className={`flex ${level > 0 ? "ml-4" : ""} w-full`}>
|
||||
// <div
|
||||
// className={`${comment.children?.length || level > 0 ? "border-l" : ""} w-full pl-2`}
|
||||
// >
|
||||
|
||||
// {comment.children && comment.children.length > 0 && (
|
||||
// <div className="pl-4">
|
||||
// {comment.children.map((child) => (
|
||||
// <Comment
|
||||
// key={child.id}
|
||||
// {...props}
|
||||
// comment={child}
|
||||
// level={level + 1}
|
||||
// />
|
||||
// ))}
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default Comment;
|
||||
|
||||
@ -9,28 +9,32 @@ export function buildCommentTree(comments: Comment[]): Array<CommentNode> {
|
||||
commentMap[comment.id] = {
|
||||
...comment,
|
||||
children: [],
|
||||
missingParent: false,
|
||||
};
|
||||
});
|
||||
|
||||
// Second pass: Build the tree by connecting children to parents
|
||||
const rootComments: CommentNode[] = [];
|
||||
const rootComments: Array<CommentNode> = [];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
import { createSafeActionClient } from "next-safe-action";
|
||||
|
||||
export const actionClient = createSafeActionClient();
|
||||
@ -60,7 +60,8 @@ export type Comment = typeof comments.$inferSelect & {
|
||||
};
|
||||
|
||||
export type CommentNode = Comment & {
|
||||
children: CommentNode[];
|
||||
children: Array<CommentNode | DeletedNode>;
|
||||
missingParent: boolean;
|
||||
};
|
||||
|
||||
export const commentVotes = createTable(
|
||||
|
||||
@ -19,7 +19,7 @@ const createSocketServer = () => {
|
||||
if (!global.io) {
|
||||
global.io = new Server(server, {
|
||||
cors: {
|
||||
origin,
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"],
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user