diff --git a/backend/app/controllers/posts_controller.rb b/backend/app/controllers/posts_controller.rb index 70eb22f..00618a1 100644 --- a/backend/app/controllers/posts_controller.rb +++ b/backend/app/controllers/posts_controller.rb @@ -5,16 +5,26 @@ require 'nokogiri' class PostsController < ApplicationController # GET /posts def index - latest_id = params[:latest].to_i - offset = params[:offset].to_i - - posts = filtered_posts.order(created_at: :desc) - posts = if posts.first&.id == latest_id - posts.limit(20).offset(offset) - else - posts.limit(offset + 20) - end - render json: posts.as_json(include: { tags: { only: [:id, :name, :category, :post_count] } }) + limit = (params[:limit] || 20).to_i + cursor = params[:cursor] + + q = filtered_posts.order(created_at: :desc) + + next_cursor = nil + if cursor.present? + q = q.where('posts.created_at < ?', Time.iso8601(cursor)) + end + + posts = q.limit(limit + 1) + + next_cursor = nil + if posts.size > limit + next_cursor = posts.last.created_at.iso8601(6) + posts = posts.first(limit) + end + + render json: { posts: posts.as_json(include: { tags: { only: [:id, :name, :category, :post_count] } }), + next_cursor: } end def random @@ -31,7 +41,10 @@ class PostsController < ApplicationController # GET /posts/1 def show post = Post.includes(:tags).find(params[:id]) - viewed = current_user&.viewed?(post) + return head :not_found unless post + + viewed = current_user&.viewed?(post) || false + render json: (post .as_json(include: { tags: { only: [:id, :name, :category] } }) .merge(viewed: viewed)) @@ -95,7 +108,7 @@ class PostsController < ApplicationController private def filtered_posts - tag_names = params[:tags]&.split(',') + tag_names = params[:tags]&.split(?\ ) match_type = params[:match] tag_names.present? ? filter_posts_by_tags(tag_names, match_type) : Post.all end diff --git a/frontend/src/components/TagSidebar.tsx b/frontend/src/components/TagSidebar.tsx index aaf9aea..e0c2f63 100644 --- a/frontend/src/components/TagSidebar.tsx +++ b/frontend/src/components/TagSidebar.tsx @@ -7,6 +7,7 @@ import TagSearch from '@/components/TagSearch' import SectionTitle from '@/components/common/SectionTitle' import SidebarComponent from '@/components/layout/SidebarComponent' import { API_BASE_URL } from '@/config' +import { CATEGORIES } from '@/consts' import type { Post, Tag } from '@/types' @@ -27,6 +28,8 @@ export default ({ posts }: Props) => { useEffect (() => { const tagsTmp: TagByCategory = { } + let cnt = 0 + loop: for (const post of posts) { for (const tag of post.tags) @@ -34,7 +37,12 @@ export default ({ posts }: Props) => { if (!(tag.category in tagsTmp)) tagsTmp[tag.category] = [] if (!(tagsTmp[tag.category].map (t => t.id).includes (tag.id))) - tagsTmp[tag.category].push (tag) + { + tagsTmp[tag.category].push (tag) + ++cnt + if (cnt >= 25) + break loop + } } } for (const cat of Object.keys (tagsTmp)) @@ -47,7 +55,7 @@ export default ({ posts }: Props) => { タグ