diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue
index eaf2e551c..b6e313861 100644
--- a/open-isle-cli/src/views/PostPageView.vue
+++ b/open-isle-cli/src/views/PostPageView.vue
@@ -69,7 +69,7 @@
@@ -154,6 +154,7 @@ export default {
const mainContainer = ref(null)
const currentIndex = ref(1)
const subscribed = ref(false)
+ const commentSort = ref('NEWEST')
// record default metadata from the main document
const defaultTitle = document.title
@@ -287,6 +288,7 @@ export default {
const onCommentDeleted = (id) => {
removeCommentFromList(Number(id), comments.value)
+ fetchComments()
}
const fetchPost = async () => {
@@ -310,13 +312,12 @@ export default {
category.value = data.category
tags.value = data.tags || []
postReactions.value = data.reactions || []
- comments.value = (data.comments || []).map(mapComment)
+ await fetchComments()
subscribed.value = !!data.subscribed
status.value = data.status
pinnedAt.value = data.pinnedAt
postTime.value = TimeManager.format(data.createdAt)
await nextTick()
- gatherPostItems()
} catch (e) {
console.error(e)
}
@@ -379,9 +380,7 @@ export default {
})
if (res.ok) {
const data = await res.json()
- comments.value.push(mapComment(data))
- await nextTick()
- gatherPostItems()
+ await fetchComments()
if (data.reward && data.reward > 0) {
toast.success(`评论成功,获得 ${data.reward} 经验值`)
} else {
@@ -524,6 +523,33 @@ export default {
}
}
+ const fetchCommentSorts = () => {
+ return Promise.resolve([
+ { id: 'NEWEST', name: '最新', icon: 'fas fa-clock' },
+ { id: 'OLDEST', name: '最旧', icon: 'fas fa-hourglass-start' },
+ { id: 'MOST_INTERACTIONS', name: '最多互动', icon: 'fas fa-fire' }
+ ])
+ }
+
+ const fetchComments = async () => {
+ try {
+ const token = getToken()
+ const res = await fetch(`${API_BASE_URL}/api/posts/${postId}/comments?sort=${commentSort.value}`, {
+ headers: { Authorization: token ? `Bearer ${token}` : '' }
+ })
+ if (res.ok) {
+ const data = await res.json()
+ comments.value = data.map(mapComment)
+ await nextTick()
+ gatherPostItems()
+ }
+ } catch {
+ // ignore
+ }
+ }
+
+ watch(commentSort, fetchComments)
+
const jumpToHashComment = async () => {
const hash = location.hash
if (hash.startsWith('#comment-')) {
@@ -546,7 +572,6 @@ export default {
const hash = location.hash
const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null
await fetchPost()
- gatherPostItems()
if (id) expandCommentPath(id)
updateCurrentIndex()
await jumpToHashComment()
@@ -595,7 +620,9 @@ export default {
lightboxImgs,
handleContentClick,
isMobile,
- pinnedAt
+ pinnedAt,
+ commentSort,
+ fetchCommentSorts
}
}
}
diff --git a/src/main/java/com/openisle/controller/CommentController.java b/src/main/java/com/openisle/controller/CommentController.java
index c3aab14d1..b6dccfdfd 100644
--- a/src/main/java/com/openisle/controller/CommentController.java
+++ b/src/main/java/com/openisle/controller/CommentController.java
@@ -4,6 +4,7 @@ import com.openisle.model.Comment;
import com.openisle.service.CommentService;
import com.openisle.service.CaptchaService;
import com.openisle.service.LevelService;
+import com.openisle.model.CommentSort;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
@@ -56,8 +57,9 @@ public class CommentController {
}
@GetMapping("/posts/{postId}/comments")
- public List listComments(@PathVariable Long postId) {
- return commentService.getCommentsForPost(postId).stream()
+ public List listComments(@PathVariable Long postId,
+ @RequestParam(value = "sort", required = false, defaultValue = "OLDEST") CommentSort sort) {
+ return commentService.getCommentsForPost(postId, sort).stream()
.map(this::toDtoWithReplies)
.collect(Collectors.toList());
}
diff --git a/src/main/java/com/openisle/controller/PostController.java b/src/main/java/com/openisle/controller/PostController.java
index c839c2173..9b49ddc5c 100644
--- a/src/main/java/com/openisle/controller/PostController.java
+++ b/src/main/java/com/openisle/controller/PostController.java
@@ -4,6 +4,7 @@ import com.openisle.model.Comment;
import com.openisle.model.Post;
import com.openisle.model.Reaction;
import com.openisle.service.CommentService;
+import com.openisle.model.CommentSort;
import com.openisle.service.PostService;
import com.openisle.service.ReactionService;
import com.openisle.service.CaptchaService;
@@ -180,7 +181,7 @@ public class PostController {
.collect(Collectors.toList());
dto.setReactions(reactions);
- List comments = commentService.getCommentsForPost(post.getId())
+ List comments = commentService.getCommentsForPost(post.getId(), CommentSort.OLDEST)
.stream()
.map(this::toCommentDtoWithReplies)
.collect(Collectors.toList());
diff --git a/src/main/java/com/openisle/model/CommentSort.java b/src/main/java/com/openisle/model/CommentSort.java
new file mode 100644
index 000000000..515a5465e
--- /dev/null
+++ b/src/main/java/com/openisle/model/CommentSort.java
@@ -0,0 +1,10 @@
+package com.openisle.model;
+
+/**
+ * Sort options for comments.
+ */
+public enum CommentSort {
+ NEWEST,
+ OLDEST,
+ MOST_INTERACTIONS
+}
diff --git a/src/main/java/com/openisle/service/CommentService.java b/src/main/java/com/openisle/service/CommentService.java
index a2cca58ff..49d11c037 100644
--- a/src/main/java/com/openisle/service/CommentService.java
+++ b/src/main/java/com/openisle/service/CommentService.java
@@ -4,6 +4,7 @@ import com.openisle.model.Comment;
import com.openisle.model.Post;
import com.openisle.model.User;
import com.openisle.model.NotificationType;
+import com.openisle.model.CommentSort;
import com.openisle.repository.CommentRepository;
import com.openisle.repository.PostRepository;
import com.openisle.repository.UserRepository;
@@ -104,10 +105,16 @@ public class CommentService {
return comment;
}
- public List getCommentsForPost(Long postId) {
+ public List getCommentsForPost(Long postId, CommentSort sort) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
- return commentRepository.findByPostAndParentIsNullOrderByCreatedAtAsc(post);
+ List list = commentRepository.findByPostAndParentIsNullOrderByCreatedAtAsc(post);
+ if (sort == CommentSort.NEWEST) {
+ list.sort(java.util.Comparator.comparing(Comment::getCreatedAt).reversed());
+ } else if (sort == CommentSort.MOST_INTERACTIONS) {
+ list.sort((a, b) -> Integer.compare(interactionCount(b), interactionCount(a)));
+ }
+ return list;
}
public List getReplies(Long parentId) {
@@ -167,4 +174,10 @@ public class CommentService {
imageUploader.removeReferences(imageUploader.extractUrls(comment.getContent()));
commentRepository.delete(comment);
}
+
+ private int interactionCount(Comment comment) {
+ int reactions = reactionRepository.findByComment(comment).size();
+ int replies = commentRepository.findByParentOrderByCreatedAtAsc(comment).size();
+ return reactions + replies;
+ }
}
diff --git a/src/test/java/com/openisle/controller/CommentControllerTest.java b/src/test/java/com/openisle/controller/CommentControllerTest.java
index 959e8b90e..8eeabbef9 100644
--- a/src/test/java/com/openisle/controller/CommentControllerTest.java
+++ b/src/test/java/com/openisle/controller/CommentControllerTest.java
@@ -19,6 +19,7 @@ import java.time.LocalDateTime;
import java.util.List;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@@ -53,7 +54,7 @@ class CommentControllerTest {
void createAndListComments() throws Exception {
Comment comment = createComment(1L, "hi", "bob");
Mockito.when(commentService.addComment(eq("bob"), eq(1L), eq("hi"))).thenReturn(comment);
- Mockito.when(commentService.getCommentsForPost(1L)).thenReturn(List.of(comment));
+ Mockito.when(commentService.getCommentsForPost(eq(1L), any())).thenReturn(List.of(comment));
Mockito.when(commentService.getReplies(1L)).thenReturn(List.of());
mockMvc.perform(post("/api/posts/1/comments")
diff --git a/src/test/java/com/openisle/controller/PostControllerTest.java b/src/test/java/com/openisle/controller/PostControllerTest.java
index 6a46af268..7da8cdb25 100644
--- a/src/test/java/com/openisle/controller/PostControllerTest.java
+++ b/src/test/java/com/openisle/controller/PostControllerTest.java
@@ -184,7 +184,7 @@ class PostControllerTest {
cr.setType(com.openisle.model.ReactionType.LIKE);
Mockito.when(postService.viewPost(eq(1L), Mockito.isNull())).thenReturn(post);
- Mockito.when(commentService.getCommentsForPost(1L)).thenReturn(List.of(comment));
+ Mockito.when(commentService.getCommentsForPost(eq(1L), any())).thenReturn(List.of(comment));
Mockito.when(commentService.getReplies(2L)).thenReturn(List.of(reply));
Mockito.when(commentService.getReplies(3L)).thenReturn(List.of());
Mockito.when(commentService.getParticipants(Mockito.anyLong(), Mockito.anyInt())).thenReturn(java.util.List.of());