From 281c85f2f683e56e5d7e8447d82e6d260931176a Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 29 Jun 2025 15:44:40 +0900 Subject: [PATCH] #69 --- backend/app/controllers/posts_controller.rb | 44 ++++++++++++++------- backend/config/routes.rb | 1 + frontend/src/components/TagSidebar.tsx | 20 +++++++--- frontend/src/pages/wiki/WikiDetailPage.tsx | 2 +- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/backend/app/controllers/posts_controller.rb b/backend/app/controllers/posts_controller.rb index 3522b01..9116f9b 100644 --- a/backend/app/controllers/posts_controller.rb +++ b/backend/app/controllers/posts_controller.rb @@ -5,24 +5,18 @@ require 'nokogiri' class PostsController < ApplicationController # GET /posts def index - if params[:tags].present? - tag_names = params[:tags].split(',') - match_type = params[:match] - if match_type == 'any' - posts = Post.joins(:tags).where(tags: { name: tag_names }).distinct - else - posts = Post.joins(:tags) - tag_names.each do |tag| - posts = posts.where(id: Post.joins(:tags).where(tags: { name: tag })) - end - posts = posts.distinct - end - else - posts = Post.all - end + posts = filtered_posts render json: posts.as_json(include: { tags: { only: [:id, :name, :category] } }) end + def random + post = filtered_posts.order('RAND()').first + viewed = current_user&.viewed?(post) + render json: (post + .as_json(include: { tags: { only: [:id, :name, :category] } }) + .merge(viewed: viewed)) + end + # GET /posts/1 def show post = Post.includes(:tags).find(params[:id]) @@ -86,4 +80,24 @@ class PostsController < ApplicationController # DELETE /posts/1 def destroy end + + private + + def filtered_posts + tag_names = params[:tags]&.split(',') + match_type = params[:match] + tag_names.present? ? filter_posts_by_tags(tag_names, match_type) : Post.all + end + + def filter_posts_by_tags tag_names, match_type + posts = Post.joins(:tags) + if match_type == 'any' + posts = posts.where(tags: { name: tag_names }).distinct + else + tag_names.each do |tag| + posts = posts.where(id: Post.joins(:tags).where(tags: { name: tag })) + end + end + posts.distinct + end end diff --git a/backend/config/routes.rb b/backend/config/routes.rb index a153130..859eccf 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do get 'tags/autocomplete', to: 'tags#autocomplete' + get 'posts/random', to: 'posts#random' post 'posts/:id/viewed', to: 'posts#viewed' delete 'posts/:id/viewed', to: 'posts#unviewed' get 'preview/title', to: 'preview#title' diff --git a/frontend/src/components/TagSidebar.tsx b/frontend/src/components/TagSidebar.tsx index c2f05fa..52038c3 100644 --- a/frontend/src/components/TagSidebar.tsx +++ b/frontend/src/components/TagSidebar.tsx @@ -1,10 +1,11 @@ -import React, { useEffect, useState } from 'react' import axios from 'axios' -import { Link, useParams } from 'react-router-dom' -import { API_BASE_URL } from '@/config' +import React, { useEffect, useState } from 'react' +import { Link, useNavigate, useParams } from 'react-router-dom' + import TagSearch from '@/components/TagSearch' -import SidebarComponent from '@/components/layout/SidebarComponent' import SectionTitle from '@/components/common/SectionTitle' +import SidebarComponent from '@/components/layout/SidebarComponent' +import { API_BASE_URL } from '@/config' import type { Post, Tag } from '@/types' @@ -14,6 +15,8 @@ type Props = { posts: Post[] } export default ({ posts }: Props) => { + const navigate = useNavigate () + const [tags, setTags] = useState ({ }) const [tagsCounts, setTagsCounts] = useState<{ [key: number]: number }> ({ }) @@ -56,6 +59,13 @@ export default ({ posts }: Props) => { ))} 関聯 - ランダム + { + ev.preventDefault () + void (axios.get (`${ API_BASE_URL }/posts/random`) + .then (res => navigate (`/posts/${ res.data.id }`))) + }}> + ランダム + ) } diff --git a/frontend/src/pages/wiki/WikiDetailPage.tsx b/frontend/src/pages/wiki/WikiDetailPage.tsx index c60b8a3..fef2f1b 100644 --- a/frontend/src/pages/wiki/WikiDetailPage.tsx +++ b/frontend/src/pages/wiki/WikiDetailPage.tsx @@ -2,7 +2,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import { useEffect, useState } from 'react' import { Helmet } from 'react-helmet' -import { Link, useLocation, useParams, useNavigate } from 'react-router-dom' +import { Link, useLocation, useNavigate, useParams } from 'react-router-dom' import WikiBody from '@/components/WikiBody' import PageTitle from '@/components/common/PageTitle'