mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-03-06 20:10:46 +08:00
Merge pull request #246 from nagisa77/codex/add-article-pinning-feature
Add post pinning feature
This commit is contained in:
@@ -58,6 +58,7 @@
|
|||||||
<div class="article-item" v-for="article in articles" :key="article.id">
|
<div class="article-item" v-for="article in articles" :key="article.id">
|
||||||
<div class="article-main-container">
|
<div class="article-main-container">
|
||||||
<router-link class="article-item-title main-item" :to="`/posts/${article.id}`">
|
<router-link class="article-item-title main-item" :to="`/posts/${article.id}`">
|
||||||
|
<i v-if="article.pinned" class="fas fa-thumbtack pinned-icon"></i>
|
||||||
{{ article.title }}
|
{{ article.title }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="article-item-description main-item">{{ sanitizeDescription(article.description) }}</div>
|
<div class="article-item-description main-item">{{ sanitizeDescription(article.description) }}</div>
|
||||||
@@ -236,7 +237,8 @@ export default {
|
|||||||
members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })),
|
members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })),
|
||||||
comments: (p.comments || []).length,
|
comments: (p.comments || []).length,
|
||||||
views: p.views,
|
views: p.views,
|
||||||
time: TimeManager.format(p.createdAt)
|
time: TimeManager.format(p.createdAt),
|
||||||
|
pinned: !!p.pinnedAt
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
if (data.length < pageSize) {
|
if (data.length < pageSize) {
|
||||||
@@ -272,7 +274,8 @@ export default {
|
|||||||
members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })),
|
members: (p.participants || []).map(m => ({ id: m.id, avatar: m.avatar })),
|
||||||
comments: (p.comments || []).length,
|
comments: (p.comments || []).length,
|
||||||
views: p.views,
|
views: p.views,
|
||||||
time: TimeManager.format(p.createdAt)
|
time: TimeManager.format(p.createdAt),
|
||||||
|
pinned: !!p.pinnedAt
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
if (data.length < pageSize) {
|
if (data.length < pageSize) {
|
||||||
@@ -502,6 +505,11 @@ export default {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pinned-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
.article-item-description {
|
.article-item-description {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export default {
|
|||||||
const postReactions = ref([])
|
const postReactions = ref([])
|
||||||
const comments = ref([])
|
const comments = ref([])
|
||||||
const status = ref('PUBLISHED')
|
const status = ref('PUBLISHED')
|
||||||
|
const pinnedAt = ref(null)
|
||||||
const isWaitingFetchingPost = ref(false);
|
const isWaitingFetchingPost = ref(false);
|
||||||
const isWaitingPostingComment = ref(false);
|
const isWaitingPostingComment = ref(false);
|
||||||
const postTime = ref('')
|
const postTime = ref('')
|
||||||
@@ -158,6 +159,13 @@ export default {
|
|||||||
items.push({ text: '编辑文章', onClick: () => editPost() })
|
items.push({ text: '编辑文章', onClick: () => editPost() })
|
||||||
items.push({ text: '删除文章', color: 'red', onClick: () => deletePost() })
|
items.push({ text: '删除文章', color: 'red', onClick: () => deletePost() })
|
||||||
}
|
}
|
||||||
|
if (isAdmin.value) {
|
||||||
|
if (pinnedAt.value) {
|
||||||
|
items.push({ text: '取消置顶', onClick: () => unpinPost() })
|
||||||
|
} else {
|
||||||
|
items.push({ text: '置顶', onClick: () => pinPost() })
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isAdmin.value && status.value === 'PENDING') {
|
if (isAdmin.value && status.value === 'PENDING') {
|
||||||
items.push({ text: '通过审核', onClick: () => approvePost() })
|
items.push({ text: '通过审核', onClick: () => approvePost() })
|
||||||
items.push({ text: '驳回', color: 'red', onClick: () => rejectPost() })
|
items.push({ text: '驳回', color: 'red', onClick: () => rejectPost() })
|
||||||
@@ -277,6 +285,7 @@ export default {
|
|||||||
comments.value = (data.comments || []).map(mapComment)
|
comments.value = (data.comments || []).map(mapComment)
|
||||||
subscribed.value = !!data.subscribed
|
subscribed.value = !!data.subscribed
|
||||||
status.value = data.status
|
status.value = data.status
|
||||||
|
pinnedAt.value = data.pinnedAt
|
||||||
postTime.value = TimeManager.format(data.createdAt)
|
postTime.value = TimeManager.format(data.createdAt)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
gatherPostItems()
|
gatherPostItems()
|
||||||
@@ -395,6 +404,36 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pinPost = async () => {
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) return
|
||||||
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/pin`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
pinnedAt.value = new Date().toISOString()
|
||||||
|
toast.success('已置顶')
|
||||||
|
} else {
|
||||||
|
toast.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unpinPost = async () => {
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) return
|
||||||
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/unpin`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
pinnedAt.value = null
|
||||||
|
toast.success('已取消置顶')
|
||||||
|
} else {
|
||||||
|
toast.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const editPost = () => {
|
const editPost = () => {
|
||||||
router.push(`/posts/${postId}/edit`)
|
router.push(`/posts/${postId}/edit`)
|
||||||
}
|
}
|
||||||
@@ -514,12 +553,15 @@ export default {
|
|||||||
editPost,
|
editPost,
|
||||||
onCommentDeleted,
|
onCommentDeleted,
|
||||||
deletePost,
|
deletePost,
|
||||||
|
pinPost,
|
||||||
|
unpinPost,
|
||||||
rejectPost,
|
rejectPost,
|
||||||
lightboxVisible,
|
lightboxVisible,
|
||||||
lightboxIndex,
|
lightboxIndex,
|
||||||
lightboxImgs,
|
lightboxImgs,
|
||||||
handleContentClick,
|
handleContentClick,
|
||||||
isMobile
|
isMobile,
|
||||||
|
pinnedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ public class AdminPostController {
|
|||||||
return toDto(postService.rejectPost(id));
|
return toDto(postService.rejectPost(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/pin")
|
||||||
|
public PostDto pin(@PathVariable Long id) {
|
||||||
|
return toDto(postService.pinPost(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/unpin")
|
||||||
|
public PostDto unpin(@PathVariable Long id) {
|
||||||
|
return toDto(postService.unpinPost(id));
|
||||||
|
}
|
||||||
|
|
||||||
private PostDto toDto(Post post) {
|
private PostDto toDto(Post post) {
|
||||||
PostDto dto = new PostDto();
|
PostDto dto = new PostDto();
|
||||||
dto.setId(post.getId());
|
dto.setId(post.getId());
|
||||||
@@ -46,6 +56,7 @@ public class AdminPostController {
|
|||||||
dto.setCategory(toCategoryDto(post.getCategory()));
|
dto.setCategory(toCategoryDto(post.getCategory()));
|
||||||
dto.setViews(post.getViews());
|
dto.setViews(post.getViews());
|
||||||
dto.setStatus(post.getStatus());
|
dto.setStatus(post.getStatus());
|
||||||
|
dto.setPinnedAt(post.getPinnedAt());
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +80,7 @@ public class AdminPostController {
|
|||||||
private CategoryDto category;
|
private CategoryDto category;
|
||||||
private long views;
|
private long views;
|
||||||
private com.openisle.model.PostStatus status;
|
private com.openisle.model.PostStatus status;
|
||||||
|
private LocalDateTime pinnedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ public class PostController {
|
|||||||
dto.setTags(post.getTags().stream().map(this::toTagDto).collect(Collectors.toList()));
|
dto.setTags(post.getTags().stream().map(this::toTagDto).collect(Collectors.toList()));
|
||||||
dto.setViews(post.getViews());
|
dto.setViews(post.getViews());
|
||||||
dto.setStatus(post.getStatus());
|
dto.setStatus(post.getStatus());
|
||||||
|
dto.setPinnedAt(post.getPinnedAt());
|
||||||
|
|
||||||
List<ReactionDto> reactions = reactionService.getReactionsForPost(post.getId())
|
List<ReactionDto> reactions = reactionService.getReactionsForPost(post.getId())
|
||||||
.stream()
|
.stream()
|
||||||
@@ -259,6 +260,7 @@ public class PostController {
|
|||||||
private java.util.List<TagDto> tags;
|
private java.util.List<TagDto> tags;
|
||||||
private long views;
|
private long views;
|
||||||
private com.openisle.model.PostStatus status;
|
private com.openisle.model.PostStatus status;
|
||||||
|
private LocalDateTime pinnedAt;
|
||||||
private List<CommentDto> comments;
|
private List<CommentDto> comments;
|
||||||
private List<ReactionDto> reactions;
|
private List<ReactionDto> reactions;
|
||||||
private java.util.List<AuthorDto> participants;
|
private java.util.List<AuthorDto> participants;
|
||||||
|
|||||||
@@ -59,4 +59,7 @@ public class Post {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private PostStatus status = PostStatus.PUBLISHED;
|
private PostStatus status = PostStatus.PUBLISHED;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private LocalDateTime pinnedAt;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,22 +173,14 @@ public class PostService {
|
|||||||
java.util.List<Long> tagIds,
|
java.util.List<Long> tagIds,
|
||||||
Integer page,
|
Integer page,
|
||||||
Integer pageSize) {
|
Integer pageSize) {
|
||||||
Pageable pageable = null;
|
|
||||||
if (page != null && pageSize != null) {
|
|
||||||
pageable = PageRequest.of(page, pageSize, Sort.Direction.DESC, "createdAt");
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasCategories = categoryIds != null && !categoryIds.isEmpty();
|
boolean hasCategories = categoryIds != null && !categoryIds.isEmpty();
|
||||||
boolean hasTags = tagIds != null && !tagIds.isEmpty();
|
boolean hasTags = tagIds != null && !tagIds.isEmpty();
|
||||||
|
|
||||||
if (!hasCategories && !hasTags) {
|
java.util.List<Post> posts;
|
||||||
if (pageable != null) {
|
|
||||||
return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED, pageable);
|
|
||||||
}
|
|
||||||
return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasCategories) {
|
if (!hasCategories && !hasTags) {
|
||||||
|
posts = postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED);
|
||||||
|
} else if (hasCategories) {
|
||||||
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
|
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
|
||||||
if (categories.isEmpty()) {
|
if (categories.isEmpty()) {
|
||||||
return java.util.List.of();
|
return java.util.List.of();
|
||||||
@@ -198,49 +190,33 @@ public class PostService {
|
|||||||
if (tags.isEmpty()) {
|
if (tags.isEmpty()) {
|
||||||
return java.util.List.of();
|
return java.util.List.of();
|
||||||
}
|
}
|
||||||
if (pageable != null) {
|
posts = postRepository.findByCategoriesAndAllTagsOrderByViewsDesc(
|
||||||
return postRepository.findByCategoriesAndAllTagsOrderByViewsDesc(
|
|
||||||
categories, tags, PostStatus.PUBLISHED, tags.size(), pageable);
|
|
||||||
}
|
|
||||||
return postRepository.findByCategoriesAndAllTagsOrderByViewsDesc(
|
|
||||||
categories, tags, PostStatus.PUBLISHED, tags.size());
|
categories, tags, PostStatus.PUBLISHED, tags.size());
|
||||||
|
} else {
|
||||||
|
posts = postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED);
|
||||||
}
|
}
|
||||||
if (pageable != null) {
|
} else {
|
||||||
return postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED, pageable);
|
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
|
||||||
|
if (tags.isEmpty()) {
|
||||||
|
return java.util.List.of();
|
||||||
}
|
}
|
||||||
return postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED);
|
posts = postRepository.findByAllTagsOrderByViewsDesc(tags, PostStatus.PUBLISHED, tags.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
|
return paginate(sortByPinnedAndViews(posts), page, pageSize);
|
||||||
if (tags.isEmpty()) {
|
|
||||||
return java.util.List.of();
|
|
||||||
}
|
|
||||||
if (pageable != null) {
|
|
||||||
return postRepository.findByAllTagsOrderByViewsDesc(tags, PostStatus.PUBLISHED, tags.size(), pageable);
|
|
||||||
}
|
|
||||||
return postRepository.findByAllTagsOrderByViewsDesc(tags, PostStatus.PUBLISHED, tags.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Post> listPostsByCategories(java.util.List<Long> categoryIds,
|
public List<Post> listPostsByCategories(java.util.List<Long> categoryIds,
|
||||||
Integer page,
|
Integer page,
|
||||||
Integer pageSize) {
|
Integer pageSize) {
|
||||||
Pageable pageable = null;
|
|
||||||
if (page != null && pageSize != null) {
|
|
||||||
pageable = PageRequest.of(page, pageSize, Sort.Direction.DESC, "createdAt");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (categoryIds == null || categoryIds.isEmpty()) {
|
if (categoryIds == null || categoryIds.isEmpty()) {
|
||||||
if (pageable != null) {
|
java.util.List<Post> posts = postRepository.findByStatusOrderByCreatedAtDesc(PostStatus.PUBLISHED);
|
||||||
return postRepository.findByStatusOrderByCreatedAtDesc(PostStatus.PUBLISHED, pageable);
|
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
|
||||||
}
|
|
||||||
return postRepository.findByStatusOrderByCreatedAtDesc(PostStatus.PUBLISHED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
|
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
|
||||||
if (pageable != null) {
|
java.util.List<Post> posts = postRepository.findByCategoryInAndStatusOrderByCreatedAtDesc(categories, PostStatus.PUBLISHED);
|
||||||
return postRepository.findByCategoryInAndStatusOrderByCreatedAtDesc(categories, PostStatus.PUBLISHED, pageable);
|
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
|
||||||
}
|
|
||||||
return postRepository.findByCategoryInAndStatusOrderByCreatedAtDesc(categories, PostStatus.PUBLISHED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Post> getRecentPostsByUser(String username, int limit) {
|
public List<Post> getRecentPostsByUser(String username, int limit) {
|
||||||
@@ -266,20 +242,13 @@ public class PostService {
|
|||||||
return java.util.List.of();
|
return java.util.List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pageable pageable = null;
|
|
||||||
if (page != null && pageSize != null) {
|
|
||||||
pageable = PageRequest.of(page, pageSize, Sort.Direction.DESC, "createdAt");
|
|
||||||
}
|
|
||||||
|
|
||||||
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
|
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
|
||||||
if (tags.isEmpty()) {
|
if (tags.isEmpty()) {
|
||||||
return java.util.List.of();
|
return java.util.List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pageable != null) {
|
java.util.List<Post> posts = postRepository.findByAllTagsOrderByCreatedAtDesc(tags, PostStatus.PUBLISHED, tags.size());
|
||||||
return postRepository.findByAllTagsOrderByCreatedAtDesc(tags, PostStatus.PUBLISHED, tags.size(), pageable);
|
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
|
||||||
}
|
|
||||||
return postRepository.findByAllTagsOrderByCreatedAtDesc(tags, PostStatus.PUBLISHED, tags.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Post> listPostsByCategoriesAndTags(java.util.List<Long> categoryIds,
|
public List<Post> listPostsByCategoriesAndTags(java.util.List<Long> categoryIds,
|
||||||
@@ -290,21 +259,14 @@ public class PostService {
|
|||||||
return java.util.List.of();
|
return java.util.List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pageable pageable = null;
|
|
||||||
if (page != null && pageSize != null) {
|
|
||||||
pageable = PageRequest.of(page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
|
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
|
||||||
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
|
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
|
||||||
if (categories.isEmpty() || tags.isEmpty()) {
|
if (categories.isEmpty() || tags.isEmpty()) {
|
||||||
return java.util.List.of();
|
return java.util.List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pageable != null) {
|
java.util.List<Post> posts = postRepository.findByCategoriesAndAllTagsOrderByCreatedAtDesc(categories, tags, PostStatus.PUBLISHED, tags.size());
|
||||||
return postRepository.findByCategoriesAndAllTagsOrderByCreatedAtDesc(categories, tags, PostStatus.PUBLISHED, tags.size(), pageable);
|
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
|
||||||
}
|
|
||||||
return postRepository.findByCategoriesAndAllTagsOrderByCreatedAtDesc(categories, tags, PostStatus.PUBLISHED, tags.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Post> listPendingPosts() {
|
public List<Post> listPendingPosts() {
|
||||||
@@ -347,6 +309,20 @@ public class PostService {
|
|||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Post pinPost(Long id) {
|
||||||
|
Post post = postRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
|
||||||
|
post.setPinnedAt(java.time.LocalDateTime.now());
|
||||||
|
return postRepository.save(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Post unpinPost(Long id) {
|
||||||
|
Post post = postRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
|
||||||
|
post.setPinnedAt(null);
|
||||||
|
return postRepository.save(post);
|
||||||
|
}
|
||||||
|
|
||||||
@org.springframework.transaction.annotation.Transactional
|
@org.springframework.transaction.annotation.Transactional
|
||||||
public Post updatePost(Long id,
|
public Post updatePost(Long id,
|
||||||
String username,
|
String username,
|
||||||
@@ -410,4 +386,32 @@ public class PostService {
|
|||||||
public long countPostsByTag(Long tagId) {
|
public long countPostsByTag(Long tagId) {
|
||||||
return postRepository.countDistinctByTags_Id(tagId);
|
return postRepository.countDistinctByTags_Id(tagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private java.util.List<Post> sortByPinnedAndCreated(java.util.List<Post> posts) {
|
||||||
|
return posts.stream()
|
||||||
|
.sorted(java.util.Comparator
|
||||||
|
.comparing(Post::getPinnedAt, java.util.Comparator.nullsLast(java.util.Comparator.reverseOrder()))
|
||||||
|
.thenComparing(Post::getCreatedAt, java.util.Comparator.reverseOrder()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private java.util.List<Post> sortByPinnedAndViews(java.util.List<Post> posts) {
|
||||||
|
return posts.stream()
|
||||||
|
.sorted(java.util.Comparator
|
||||||
|
.comparing(Post::getPinnedAt, java.util.Comparator.nullsLast(java.util.Comparator.reverseOrder()))
|
||||||
|
.thenComparing(Post::getViews, java.util.Comparator.reverseOrder()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private java.util.List<Post> paginate(java.util.List<Post> posts, Integer page, Integer pageSize) {
|
||||||
|
if (page == null || pageSize == null) {
|
||||||
|
return posts;
|
||||||
|
}
|
||||||
|
int from = page * pageSize;
|
||||||
|
if (from >= posts.size()) {
|
||||||
|
return java.util.List.of();
|
||||||
|
}
|
||||||
|
int to = Math.min(from + pageSize, posts.size());
|
||||||
|
return posts.subList(from, to);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user