mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-18 21:10:57 +08:00
feat: add comment sorting
This commit is contained in:
@@ -69,7 +69,7 @@
|
||||
<div class="comment-config-container">
|
||||
<div class="comment-sort-container">
|
||||
<div class="comment-sort-title">Sort by: </div>
|
||||
<Dropdown></Dropdown>
|
||||
<Dropdown v-model="commentSort" :fetch-options="fetchCommentSorts" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CommentDto> listComments(@PathVariable Long postId) {
|
||||
return commentService.getCommentsForPost(postId).stream()
|
||||
public List<CommentDto> 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());
|
||||
}
|
||||
|
||||
@@ -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<CommentDto> comments = commentService.getCommentsForPost(post.getId())
|
||||
List<CommentDto> comments = commentService.getCommentsForPost(post.getId(), CommentSort.OLDEST)
|
||||
.stream()
|
||||
.map(this::toCommentDtoWithReplies)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
10
src/main/java/com/openisle/model/CommentSort.java
Normal file
10
src/main/java/com/openisle/model/CommentSort.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.openisle.model;
|
||||
|
||||
/**
|
||||
* Sort options for comments.
|
||||
*/
|
||||
public enum CommentSort {
|
||||
NEWEST,
|
||||
OLDEST,
|
||||
MOST_INTERACTIONS
|
||||
}
|
||||
@@ -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<Comment> getCommentsForPost(Long postId) {
|
||||
public List<Comment> getCommentsForPost(Long postId, CommentSort sort) {
|
||||
Post post = postRepository.findById(postId)
|
||||
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
|
||||
return commentRepository.findByPostAndParentIsNullOrderByCreatedAtAsc(post);
|
||||
List<Comment> 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<Comment> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user