diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index a61256e..3308caf 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from "next/navigation"; import { BlogDetailContent } from "@/components/blog-detail-content"; import { getBlogDetailBySlug } from "@/data/blog-detail"; +import { getBlogEngagementBySlug } from "@/data/blog-engagement"; type BlogDetailPageProps = { params: Promise<{ slug: string }>; @@ -9,10 +10,11 @@ type BlogDetailPageProps = { export default async function BlogDetailPage({ params }: BlogDetailPageProps) { const { slug } = await params; const post = getBlogDetailBySlug(slug); + const engagement = getBlogEngagementBySlug(slug); if (!post) { notFound(); } - return ; + return ; } diff --git a/components/blog-content.tsx b/components/blog-content.tsx index e92508a..497325d 100644 --- a/components/blog-content.tsx +++ b/components/blog-content.tsx @@ -1,3 +1,5 @@ +"use client"; + import { Badge, Card, Typography } from "poyraz-ui/atoms"; import { ArticleCard, @@ -68,6 +70,7 @@ export function BlogContent() { ))} + diff --git a/components/blog-detail-content.tsx b/components/blog-detail-content.tsx index ab2a238..a63da26 100644 --- a/components/blog-detail-content.tsx +++ b/components/blog-detail-content.tsx @@ -5,13 +5,26 @@ import Link from "next/link"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import { useEffect, useMemo, useRef, useState } from "react"; +import { Icon } from "@iconify/react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; -import { Badge, Card, Typography } from "poyraz-ui/atoms"; +import { + Avatar, + AvatarFallback, + AvatarImage, + Badge, + Button, + Card, + Textarea, + Typography, +} from "poyraz-ui/atoms"; +import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "poyraz-ui/molecules"; import type { BlogDetail } from "@/data/blog-detail"; +import type { BlogComment, BlogEngagement } from "@/data/blog-engagement"; type BlogDetailContentProps = { post: BlogDetail; + engagement: BlogEngagement; }; function MermaidBlock({ chart }: { chart: string }) { @@ -71,9 +84,16 @@ function MermaidBlock({ chart }: { chart: string }) { ); } -export function BlogDetailContent({ post }: BlogDetailContentProps) { +export function BlogDetailContent({ post, engagement }: BlogDetailContentProps) { const scrollerRef = useRef(null); const [progress, setProgress] = useState(0); + const [liked, setLiked] = useState(false); + const [likeCount, setLikeCount] = useState(engagement.likes); + const [comments, setComments] = useState(engagement.comments); + const [commentSheetOpen, setCommentSheetOpen] = useState(false); + const [githubAuthed, setGithubAuthed] = useState(false); + const [draft, setDraft] = useState(""); + const [replyTo, setReplyTo] = useState<{ id: string; author: string } | null>(null); const progressWidth = useMemo(() => `${Math.min(100, Math.max(0, progress))}%`, [progress]); @@ -90,128 +110,346 @@ export function BlogDetailContent({ post }: BlogDetailContentProps) { setProgress((el.scrollTop / scrollable) * 100); }; + const toggleLike = () => { + setLiked((prev) => { + const next = !prev; + setLikeCount((count) => (next ? count + 1 : Math.max(0, count - 1))); + return next; + }); + }; + + const handleReplyClick = (id: string, author: string) => { + setReplyTo({ id, author }); + setCommentSheetOpen(true); + }; + + const handleSubmitComment = () => { + if (!githubAuthed) return; + const content = draft.trim(); + if (!content) return; + + if (replyTo) { + setComments((prev) => + prev.map((item) => + item.id === replyTo.id + ? { + ...item, + replies: [ + ...item.replies, + { + id: `reply-${Date.now()}`, + author: "You (GitHub)", + avatar: "/logo/logo.jpeg", + date: "Just now", + content, + likes: 0, + }, + ], + } + : item, + ), + ); + setReplyTo(null); + } else { + setComments((prev) => [ + { + id: `comment-${Date.now()}`, + author: "You (GitHub)", + avatar: "/logo/logo.jpeg", + date: "Just now", + content, + likes: 0, + replies: [], + }, + ...prev, + ]); + } + + setDraft(""); + }; + return ( -
-
-
+ <> +
+
-
- - ← Back to blog - - -
- {post.category} - - {post.title} - - - {post.author} - {post.date} - {post.readTime} - - - {post.excerpt} - -
- - - {post.title} - - -
- ( - - {children} - - ), - h3: ({ children }) => ( - - {children} - - ), - p: ({ children }) => ( - - {children} - - ), - li: ({ children }) => ( -
  • {children}
  • - ), - blockquote: ({ children }) => ( -
    - {children} -
    - ), - table: ({ children }) => ( -
    - {children}
    -
    - ), - thead: ({ children }) => {children}, - tr: ({ children }) => {children}, - th: ({ children }) => ( - {children} - ), - td: ({ children }) => ( - {children} - ), - code: ({ className, children }) => { - const match = /language-(\w+)/.exec(className ?? ""); - const codeText = String(children).replace(/\n$/, ""); - const language = match?.[1] ?? ""; - - if (!match) { - return ( - - {children} - - ); - } - - if (language === "mermaid") { - return ; - } - - return ( - - {codeText} - - ); - }, - }} +
    +
    + - {post.markdown} - -
    -
    -
    + {"<- Back to blog"} + + +
    + {post.category} + + {post.title} + + + {post.author} - {post.date} - {post.readTime} + + + {post.excerpt} + +
    + + + {post.title} + + +
    + + + + + + + + +
    + Comments +
    + +
    +
    + {comments.map((item) => ( + +
    + + + + {item.author.slice(0, 2).toUpperCase()} + + +
    +
    + + {item.author} + + + {item.date} + +
    + + {item.content} + +
    + + +
    + + {item.replies.length > 0 ? ( +
    + {item.replies.map((reply) => ( + +
    + + + + {reply.author.slice(0, 2).toUpperCase()} + + +
    +
    + + {reply.author} + + + {reply.date} + +
    + + {reply.content} + +
    +
    +
    + ))} +
    + ) : null} +
    +
    +
    + ))} +
    + + {!githubAuthed ? ( + + + You need to sign in with GitHub before posting comments. + + + + ) : null} + + {replyTo ? ( + +
    + + Replying to {replyTo.author} + + +
    +
    + ) : null} + + +