yggdrasil/components/comments/
item.rs1use dioxus::prelude::*;
6
7use crate::components::comments::card::CommentCardShell;
8use crate::components::comments::form::CommentForm;
9use crate::components::comments::section::CommentContext;
10use crate::models::comment::PublicComment;
11
12#[component]
25pub fn CommentItem(comment: PublicComment, post_id: i32) -> Element {
26 let ctx: CommentContext = use_context();
27 let mut active_reply = ctx.active_reply;
28
29 let depth = if comment.parent_id.is_none() && comment.depth > 0 {
31 0
32 } else {
33 comment.depth
34 };
35
36 let is_replying = active_reply() == Some(comment.id);
37 let show_reply = depth < 20;
38 let indent_px = if depth > 1 {
41 (depth.min(5) - 1) * 16
42 } else {
43 0
44 };
45
46 let author_element = match &comment.author_url {
48 Some(url) if !url.is_empty() => rsx! {
49 a {
50 href: "{url}",
51 rel: "nofollow noopener",
52 target: "_blank",
53 class: "font-semibold text-paper-primary hover:text-paper-accent transition-colors",
54 "{comment.author_name}"
55 }
56 },
57 _ => rsx! {
58 span { class: "font-semibold text-paper-primary", "{comment.author_name}" }
59 },
60 };
61
62 let author_badge = if comment.is_author {
63 rsx! {
64 span { class: "inline-flex items-center px-1.5 py-0.5 rounded-full text-[10px] font-medium bg-[var(--color-paper-accent)]/15 text-[var(--color-paper-accent)]",
65 "作者"
66 }
67 }
68 } else {
69 rsx! {}
70 };
71
72 let timestamp = rsx! {
73 span {
74 class: "text-paper-tertiary",
75 title: "{comment.created_at_iso}",
76 "{comment.created_at}"
77 }
78 };
79
80 rsx! {
81 CommentCardShell {
82 depth,
83 avatar_url: comment.avatar_url.clone(),
84 author_name: comment.author_name.clone(),
85 author_element,
86 author_badge,
87 timestamp,
88 status_badge: rsx! {},
89 content_html: comment.content_html.clone().unwrap_or_default(),
90 content_extra_class: "md-content",
91
92 div { class: "flex items-center gap-3 mt-2",
93 if show_reply {
94 button {
95 class: "inline-flex items-center gap-1 text-xs font-medium text-paper-tertiary hover:text-paper-accent hover:bg-[var(--color-paper-entry)] px-2 py-1 rounded-md transition-all cursor-pointer",
96 class: if is_replying { "text-[var(--color-paper-accent)] bg-[var(--color-paper-accent)]/10" } else { "" },
97 aria_label: "回复 {comment.author_name} 的评论",
98 onclick: move |_| {
99 if is_replying {
100 active_reply.set(None);
101 } else {
102 active_reply.set(Some(comment.id));
103 }
104 },
105 svg {
106 class: "w-3.5 h-3.5",
107 fill: "none",
108 stroke: "currentColor",
109 stroke_width: "2",
110 view_box: "0 0 24 24",
111 path {
112 stroke_linecap: "round",
113 stroke_linejoin: "round",
114 d: "M3 10h10a5 5 0 015 5v2m-15-7l4-4m-4 4l4 4",
115 }
116 }
117 if is_replying {
118 "取消回复"
119 } else {
120 "回复"
121 }
122 }
123 }
124 }
125
126 div {
127 class: if is_replying { "mt-2 pt-1" } else { "hidden" },
128 CommentForm {
129 post_id,
130 parent_id: Some(comment.id),
131 parent_indent: Some(indent_px),
132 }
133 }
134 }
135 }
136}