yggdrasil/components/comments/
item.rs1use leptos::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) -> impl IntoView {
26 let ctx = expect_context::<CommentContext>();
27 let 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 comment_id = comment.id;
37 let is_replying = Memo::new(move |_| active_reply.get() == Some(comment_id));
38 let show_reply = depth < 20;
39 let indent_px = if depth > 1 {
42 (depth.min(5) - 1) * 16
43 } else {
44 0
45 };
46
47 let author_element = match &comment.author_url {
48 Some(url) if !url.is_empty() => view! {
49 <a class="comment-author" href=url.clone() rel="nofollow noopener" target="_blank">
50 {comment.author_name.clone()}
51 </a>
52 }
53 .into_any(),
54 _ => view! { <span class="comment-author">{comment.author_name.clone()}</span> }.into_any(),
55 };
56
57 let author_badge = if comment.is_author {
58 view! { <span class="comment-badge">"作者"</span> }.into_any()
59 } else {
60 ().into_any()
61 };
62
63 let timestamp = view! {
64 <time
65 class="comment-time"
66 datetime=comment.created_at_iso.clone()
67 title=comment.created_at_iso.clone()
68 >
69 {comment.created_at.clone()}
70 </time>
71 }
72 .into_any();
73
74 let reply_label = format!("回复 {} 的评论", comment.author_name);
75
76 view! {
77 <CommentCardShell
78 depth=depth
79 avatar_url=comment.avatar_url.clone()
80 author_name=comment.author_name.clone()
81 author_element=author_element
82 author_badge=author_badge
83 timestamp=timestamp
84 status_badge=().into_any()
85 content_html=comment.content_html.clone().unwrap_or_default()
86 content_extra_class="md-content"
87 >
88 {show_reply
89 .then(|| {
90 view! {
91 <div class="comment-actions">
92 <button
93 class="comment-reply"
94 type="button"
95 aria-expanded=move || is_replying.get().to_string()
96 aria-label=move || {
97 if is_replying.get() {
98 "收起回复".to_string()
99 } else {
100 reply_label.clone()
101 }
102 }
103 on:click=move |_| {
104 if is_replying.get_untracked() {
105 active_reply.set(None);
106 } else {
107 active_reply.set(Some(comment_id));
108 }
109 }
110 >
111 {move || if is_replying.get() { "收起回复" } else { "回复" }}
112 </button>
113 </div>
114 }
115 })}
116
117 <div class=move || if is_replying.get() { "comment-reply-form" } else { "hidden" }>
118 <CommentForm
119 post_id=post_id
120 parent_id=Some(comment_id)
121 parent_indent=Some(indent_px)
122 />
123 </div>
124 </CommentCardShell>
125 }
126}