实现评论本地更新
This commit is contained in:
@@ -3,26 +3,28 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { BlogApi } from "@/lib/api";
|
import { BlogApi } from "@/lib/api";
|
||||||
|
import { BlogComment } from "@/lib/types/blogComment";
|
||||||
import { Send } from "lucide-react";
|
import { Send } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export function BlogCommentTool({ blogId }: { blogId: string }) {
|
export function BlogCommentTool({ blogId, onInsertComment }: { blogId: string, onInsertComment: (b: BlogComment) => void }) {
|
||||||
|
|
||||||
const [comment, setComment] = useState('');
|
const [comment, setComment] = useState('');
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
|
if (comment.trim().length === 0) return;
|
||||||
const res = await BlogApi.createComment(blogId, comment);
|
const res = await BlogApi.createComment(blogId, comment);
|
||||||
if (res) {
|
if (res) {
|
||||||
toast.success('发布成功');
|
toast.success('发布成功');
|
||||||
setComment('');
|
setComment('');
|
||||||
// 提交界面刷新
|
onInsertComment(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-3 flex items-end gap-2">
|
<div className="my-3 flex items-end gap-2">
|
||||||
<Textarea placeholder="评论" onChange={v => setComment(v.target.value)} value={comment} />
|
<Textarea placeholder="评论" onChange={v => setComment(v.target.value)} value={comment} />
|
||||||
<Button variant='outline' size='icon' onClick={() => submit()}>
|
<Button variant='outline' size='icon' onClick={() => submit()} disabled={comment.trim().length === 0}>
|
||||||
<Send />
|
<Send />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,23 +1,34 @@
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { BlogCommentTool } from "./BlogCommentTool";
|
import { BlogCommentTool } from "./BlogCommentTool";
|
||||||
import { BlogApi } from "@/lib/api";
|
import { BlogApi } from "@/lib/api";
|
||||||
|
import { BlogComment } from "@/lib/types/blogComment";
|
||||||
|
|
||||||
export function BlogComments({ blogId }: { blogId: string }) {
|
export function BlogComments({ blogId }: { blogId: string }) {
|
||||||
const { data, isLoading, error } = useSWR(
|
const { data, isLoading, error, mutate } = useSWR(
|
||||||
`/api/blog/${blogId}/comments`,
|
`/api/blog/${blogId}/comments`,
|
||||||
() => BlogApi.getComments(blogId),
|
() => BlogApi.getComments(blogId),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const insertComment = async (newOne: BlogComment) => {
|
||||||
|
await mutate(
|
||||||
|
(comments) => {
|
||||||
|
if (!comments) return [newOne];
|
||||||
|
return [newOne, ...comments]
|
||||||
|
},
|
||||||
|
{ revalidate: false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
data && <div className="" >
|
data && <div className="" >
|
||||||
<h1 className="px-2 border-l-4 border-zinc-300">评论 {data.length}</h1>
|
<h1 className="px-2 border-l-4 border-zinc-300">评论 {data.length}</h1>
|
||||||
<BlogCommentTool blogId={blogId} />
|
<BlogCommentTool blogId={blogId} onInsertComment={insertComment} />
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
{
|
{
|
||||||
data.filter(d => !d.parentId)
|
data.filter(d => !d.parentId)
|
||||||
.map(d => (
|
.map(d => (
|
||||||
<div key={d.id}>
|
<div key={d.id}>
|
||||||
<h1 className="text-zinc-500">{d.user ? d.user : '匿名'}</h1>
|
<h1 className="text-zinc-500">{d.user ? d.user.nickname : '匿名'}</h1>
|
||||||
<div>{d.content}</div>
|
<div>{d.content}</div>
|
||||||
<div className="text-xs text-zinc-500 flex gap-2">
|
<div className="text-xs text-zinc-500 flex gap-2">
|
||||||
<p>{new Date(d.createdAt).toLocaleString()}</p>
|
<p>{new Date(d.createdAt).toLocaleString()}</p>
|
||||||
@@ -30,7 +41,7 @@ export function BlogComments({ blogId }: { blogId: string }) {
|
|||||||
{
|
{
|
||||||
data.filter(c => c.parentId === d.id).map(c => (
|
data.filter(c => c.parentId === d.id).map(c => (
|
||||||
<div key={c.id}>
|
<div key={c.id}>
|
||||||
<h1 className="text-zinc-500">{c.user ? c.user : '匿名'}</h1>
|
<h1 className="text-zinc-500">{c.user ? c.user.nickname : '匿名'}</h1>
|
||||||
<div>{c.content}</div>
|
<div>{c.content}</div>
|
||||||
<p className="text-xs text-zinc-500 flex gap-2">
|
<p className="text-xs text-zinc-500 flex gap-2">
|
||||||
<p>{new Date().toLocaleString()}</p>
|
<p>{new Date().toLocaleString()}</p>
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
|
import { BlogComment } from "@/lib/types/blogComment";
|
||||||
import fetcher from "../fetcher";
|
import fetcher from "../fetcher";
|
||||||
|
|
||||||
export async function createComment(blogId: string, content: string) {
|
export async function createComment(blogId: string, content: string) {
|
||||||
return fetcher<{
|
return fetcher<BlogComment>(`/api/blog/${blogId}/comment`, {
|
||||||
blogId: string;
|
|
||||||
content: string;
|
|
||||||
createdAt: string
|
|
||||||
deletedAt: null; // 原则上能看到就是null
|
|
||||||
id: string;
|
|
||||||
parentId: string | null;
|
|
||||||
}>(`/api/blog/${blogId}/comment`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ content }),
|
body: JSON.stringify({ content }),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
|
import { BlogComment } from "@/lib/types/blogComment";
|
||||||
import fetcher from "../fetcher";
|
import fetcher from "../fetcher";
|
||||||
|
|
||||||
export async function getComments(blogId: string) {
|
export async function getComments(blogId: string) {
|
||||||
return fetcher<{
|
return fetcher<BlogComment[]>(`/api/blog/${blogId}/comments`, { method: 'GET' });
|
||||||
blogId: string;
|
|
||||||
content: string;
|
|
||||||
createdAt: string;
|
|
||||||
deletedAt: string | null;// 原则上能看到就是null,
|
|
||||||
id: string;
|
|
||||||
parentId: string | null; // 如果是回复,则有parentId
|
|
||||||
user: null;// TODO需要完善
|
|
||||||
}[]>(`/api/blog/${blogId}/comments`, { method: 'GET' });
|
|
||||||
}
|
}
|
||||||
11
tone-page-web/lib/types/blogComment.ts
Normal file
11
tone-page-web/lib/types/blogComment.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { User } from "./user";
|
||||||
|
|
||||||
|
export interface BlogComment {
|
||||||
|
id: string;
|
||||||
|
blogId: string;
|
||||||
|
content: string;
|
||||||
|
createdAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
parentId: string | null;
|
||||||
|
user: User | null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user