diff --git a/backend/app/controllers/posts_controller.rb b/backend/app/controllers/posts_controller.rb index bbbce70..73f3995 100644 --- a/backend/app/controllers/posts_controller.rb +++ b/backend/app/controllers/posts_controller.rb @@ -5,16 +5,16 @@ require 'nokogiri' class PostsController < ApplicationController # GET /posts def index - limit = (params[:limit] || 20).to_i + limit = params[:limit].presence&.to_i cursor = params[:cursor].presence q = filtered_posts.order(created_at: :desc) q = q.where('posts.created_at < ?', Time.iso8601(cursor)) if cursor - posts = q.limit(limit + 1) + posts = limit ? q.limit(limit + 1) : q next_cursor = nil - if posts.size > limit + if limit && posts.size > limit next_cursor = posts.last.created_at.iso8601(6) posts = posts.first(limit) end @@ -51,7 +51,7 @@ class PostsController < ApplicationController render json: (post .as_json(include: { tags: { only: [:id, :name, :category, :post_count] } }) - .merge(viewed: viewed)) + .merge(related: post.related(limit: 20), viewed:)) end # POST /posts diff --git a/backend/app/models/post.rb b/backend/app/models/post.rb index 6934025..6399042 100644 --- a/backend/app/models/post.rb +++ b/backend/app/models/post.rb @@ -7,6 +7,12 @@ class Post < ApplicationRecord has_many :post_tags, dependent: :destroy has_many :tags, through: :post_tags has_many :user_post_views, dependent: :destroy + has_many :post_similarities_as_post, + class_name: 'PostSimilarity', + foreign_key: :post_id + has_many :post_similarities_as_target_post, + class_name: 'PostSimilarity', + foreign_key: :target_post_id has_one_attached :thumbnail def as_json options = { } @@ -18,6 +24,21 @@ class Post < ApplicationRecord super(options).merge(thumbnail: nil) end + def related(limit: nil) + ids_with_cos = + post_similarities_as_post.select(:target_post_id, :cos) + .map { |ps| [ps.target_post_id, ps.cos] } + + post_similarities_as_target_post.select(:post_id, :cos) + .map { |ps| [ps.post_id, ps.cos] } + + sorted = ids_with_cos.sort_by { |_, cos| -cos } + + ids = sorted.map(&:first) + ids = ids.first(limit) if limit + + Post.where(id: ids) + end + def resized_thumbnail! return unless thumbnail.attached? diff --git a/frontend/src/pages/posts/PostDetailPage.tsx b/frontend/src/pages/posts/PostDetailPage.tsx index 74fcb5b..942d148 100644 --- a/frontend/src/pages/posts/PostDetailPage.tsx +++ b/frontend/src/pages/posts/PostDetailPage.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react' import { Helmet } from 'react-helmet-async' import { useParams } from 'react-router-dom' +import PostList from '@/components/PostList' import TagDetailSidebar from '@/components/TagDetailSidebar' import NicoViewer from '@/components/NicoViewer' import PostEditForm from '@/components/PostEditForm' @@ -117,6 +118,9 @@ export default ({ user }: Props) => { {post.viewed ? '閲覧済' : '未閲覧'} + + + {(['admin', 'member'].some (r => user?.role === r) && editing) && (