From 4627d34dbe29c127fdea37b955cb8a2d93432d91 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 9 Jul 2025 18:17:35 +0800 Subject: [PATCH] Implement reaction panel with backend support --- open-isle-cli/src/components/CommentItem.vue | 26 +-- .../src/components/ReactionsGroup.vue | 172 ++++++++++++++++++ open-isle-cli/src/views/PostPageView.vue | 36 ++-- .../controller/ReactionController.java | 6 + .../java/com/openisle/model/Reaction.java | 4 +- .../repository/ReactionRepository.java | 4 +- .../com/openisle/service/ReactionService.java | 16 +- 7 files changed, 213 insertions(+), 51 deletions(-) create mode 100644 open-isle-cli/src/components/ReactionsGroup.vue diff --git a/open-isle-cli/src/components/CommentItem.vue b/open-isle-cli/src/components/CommentItem.vue index e4dfcd4a0..0dad7f7e9 100644 --- a/open-isle-cli/src/components/CommentItem.vue +++ b/open-isle-cli/src/components/CommentItem.vue @@ -18,27 +18,14 @@
-
-
-
-
🤣
-
❤️
-
👏
-
-
1882
-
-
+
- - + -
+
({ id: r.id, userName: r.author.username, time: new Date(r.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }), avatar: r.avatar, text: r.content, + reactions: r.reactions || [], reply: [], openReplies: false })), @@ -168,7 +158,7 @@ const CommentItem = { return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply } } } -CommentItem.components = { CommentItem, CommentEditor, BaseTimeline } +CommentItem.components = { CommentItem, CommentEditor, BaseTimeline, ReactionsGroup } export default CommentItem diff --git a/open-isle-cli/src/components/ReactionsGroup.vue b/open-isle-cli/src/components/ReactionsGroup.vue new file mode 100644 index 000000000..ac9be94cd --- /dev/null +++ b/open-isle-cli/src/components/ReactionsGroup.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue index 51063c9a8..6fc447ee9 100644 --- a/open-isle-cli/src/views/PostPageView.vue +++ b/open-isle-cli/src/views/PostPageView.vue @@ -27,31 +27,11 @@
@@ -98,6 +78,7 @@ import CommentItem from '../components/CommentItem.vue' import CommentEditor from '../components/CommentEditor.vue' import BaseTimeline from '../components/BaseTimeline.vue' import ArticleTags from '../components/ArticleTags.vue' +import ReactionsGroup from '../components/ReactionsGroup.vue' import { renderMarkdown } from '../utils/markdown' import { API_BASE_URL, toast } from '../main' import { getToken } from '../utils/auth' @@ -107,7 +88,7 @@ hatch.register() export default { name: 'PostPageView', - components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags }, + components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ReactionsGroup }, setup() { const route = useRoute() const postId = route.params.id @@ -118,6 +99,7 @@ export default { const postContent = ref('') const category = ref('') const tags = ref([]) + const postReactions = ref([]) const comments = ref([]) const isWaitingFetchingPost = ref(false); const isWaitingPostingComment = ref(false); @@ -150,6 +132,7 @@ export default { time: new Date(c.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }), avatar: c.author.avatar, text: c.content, + reactions: c.reactions || [], reply: (c.replies || []).map(mapComment), openReplies: false, src: c.author.avatar, @@ -195,6 +178,7 @@ export default { title.value = data.title category.value = data.category tags.value = data.tags || [] + postReactions.value = data.reactions || [] comments.value = (data.comments || []).map(mapComment) postTime.value = new Date(data.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }) await nextTick() @@ -322,6 +306,8 @@ export default { mainContainer, currentIndex, totalPosts, + postReactions, + postId, postComment, onSliderInput, onScroll: updateCurrentIndex, diff --git a/src/main/java/com/openisle/controller/ReactionController.java b/src/main/java/com/openisle/controller/ReactionController.java index bef570999..ef8ddd1fa 100644 --- a/src/main/java/com/openisle/controller/ReactionController.java +++ b/src/main/java/com/openisle/controller/ReactionController.java @@ -28,6 +28,9 @@ public class ReactionController { @RequestBody ReactionRequest req, Authentication auth) { Reaction reaction = reactionService.reactToPost(auth.getName(), postId, req.getType()); + if (reaction == null) { + return ResponseEntity.noContent().build(); + } return ResponseEntity.ok(toDto(reaction)); } @@ -36,6 +39,9 @@ public class ReactionController { @RequestBody ReactionRequest req, Authentication auth) { Reaction reaction = reactionService.reactToComment(auth.getName(), commentId, req.getType()); + if (reaction == null) { + return ResponseEntity.noContent().build(); + } return ResponseEntity.ok(toDto(reaction)); } diff --git a/src/main/java/com/openisle/model/Reaction.java b/src/main/java/com/openisle/model/Reaction.java index 380d2ceb9..9fb71d8c1 100644 --- a/src/main/java/com/openisle/model/Reaction.java +++ b/src/main/java/com/openisle/model/Reaction.java @@ -14,8 +14,8 @@ import lombok.Setter; @NoArgsConstructor @Table(name = "reactions", uniqueConstraints = { - @UniqueConstraint(columnNames = {"user_id", "post_id"}), - @UniqueConstraint(columnNames = {"user_id", "comment_id"}) + @UniqueConstraint(columnNames = {"user_id", "post_id", "type"}), + @UniqueConstraint(columnNames = {"user_id", "comment_id", "type"}) }) public class Reaction { @Id diff --git a/src/main/java/com/openisle/repository/ReactionRepository.java b/src/main/java/com/openisle/repository/ReactionRepository.java index a9ffa9c43..c5ba190ac 100644 --- a/src/main/java/com/openisle/repository/ReactionRepository.java +++ b/src/main/java/com/openisle/repository/ReactionRepository.java @@ -13,8 +13,8 @@ import java.util.List; import java.util.Optional; public interface ReactionRepository extends JpaRepository { - Optional findByUserAndPost(User user, Post post); - Optional findByUserAndComment(User user, Comment comment); + Optional findByUserAndPostAndType(User user, Post post, com.openisle.model.ReactionType type); + Optional findByUserAndCommentAndType(User user, Comment comment, com.openisle.model.ReactionType type); List findByPost(Post post); List findByComment(Comment comment); diff --git a/src/main/java/com/openisle/service/ReactionService.java b/src/main/java/com/openisle/service/ReactionService.java index 57c39a28e..cfbea0c33 100644 --- a/src/main/java/com/openisle/service/ReactionService.java +++ b/src/main/java/com/openisle/service/ReactionService.java @@ -28,8 +28,12 @@ public class ReactionService { .orElseThrow(() -> new IllegalArgumentException("User not found")); Post post = postRepository.findById(postId) .orElseThrow(() -> new IllegalArgumentException("Post not found")); - Reaction reaction = reactionRepository.findByUserAndPost(user, post) - .orElseGet(Reaction::new); + java.util.Optional existing = reactionRepository.findByUserAndPostAndType(user, post, type); + if (existing.isPresent()) { + reactionRepository.delete(existing.get()); + return null; + } + Reaction reaction = new Reaction(); reaction.setUser(user); reaction.setPost(post); reaction.setType(type); @@ -45,8 +49,12 @@ public class ReactionService { .orElseThrow(() -> new IllegalArgumentException("User not found")); Comment comment = commentRepository.findById(commentId) .orElseThrow(() -> new IllegalArgumentException("Comment not found")); - Reaction reaction = reactionRepository.findByUserAndComment(user, comment) - .orElseGet(Reaction::new); + java.util.Optional existing = reactionRepository.findByUserAndCommentAndType(user, comment, type); + if (existing.isPresent()) { + reactionRepository.delete(existing.get()); + return null; + } + Reaction reaction = new Reaction(); reaction.setUser(user); reaction.setComment(comment); reaction.setPost(null);