@@ -5,24 +5,18 @@ require 'nokogiri' | |||||
class PostsController < ApplicationController | class PostsController < ApplicationController | ||||
# GET /posts | # GET /posts | ||||
def index | 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] } }) | render json: posts.as_json(include: { tags: { only: [:id, :name, :category] } }) | ||||
end | 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 | # GET /posts/1 | ||||
def show | def show | ||||
post = Post.includes(:tags).find(params[:id]) | post = Post.includes(:tags).find(params[:id]) | ||||
@@ -86,4 +80,24 @@ class PostsController < ApplicationController | |||||
# DELETE /posts/1 | # DELETE /posts/1 | ||||
def destroy | def destroy | ||||
end | 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 | end |
@@ -1,5 +1,6 @@ | |||||
Rails.application.routes.draw do | Rails.application.routes.draw do | ||||
get 'tags/autocomplete', to: 'tags#autocomplete' | get 'tags/autocomplete', to: 'tags#autocomplete' | ||||
get 'posts/random', to: 'posts#random' | |||||
post 'posts/:id/viewed', to: 'posts#viewed' | post 'posts/:id/viewed', to: 'posts#viewed' | ||||
delete 'posts/:id/viewed', to: 'posts#unviewed' | delete 'posts/:id/viewed', to: 'posts#unviewed' | ||||
get 'preview/title', to: 'preview#title' | get 'preview/title', to: 'preview#title' | ||||
@@ -1,10 +1,11 @@ | |||||
import React, { useEffect, useState } from 'react' | |||||
import axios from 'axios' | 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 TagSearch from '@/components/TagSearch' | ||||
import SidebarComponent from '@/components/layout/SidebarComponent' | |||||
import SectionTitle from '@/components/common/SectionTitle' | import SectionTitle from '@/components/common/SectionTitle' | ||||
import SidebarComponent from '@/components/layout/SidebarComponent' | |||||
import { API_BASE_URL } from '@/config' | |||||
import type { Post, Tag } from '@/types' | import type { Post, Tag } from '@/types' | ||||
@@ -14,6 +15,8 @@ type Props = { posts: Post[] } | |||||
export default ({ posts }: Props) => { | export default ({ posts }: Props) => { | ||||
const navigate = useNavigate () | |||||
const [tags, setTags] = useState<TagByCategory> ({ }) | const [tags, setTags] = useState<TagByCategory> ({ }) | ||||
const [tagsCounts, setTagsCounts] = useState<{ [key: number]: number }> ({ }) | const [tagsCounts, setTagsCounts] = useState<{ [key: number]: number }> ({ }) | ||||
@@ -56,6 +59,13 @@ export default ({ posts }: Props) => { | |||||
</>))} | </>))} | ||||
</ul> | </ul> | ||||
<SectionTitle>関聯</SectionTitle> | <SectionTitle>関聯</SectionTitle> | ||||
<Link>ランダム</Link> | |||||
<a href="#" | |||||
onClick={ev => { | |||||
ev.preventDefault () | |||||
void (axios.get (`${ API_BASE_URL }/posts/random`) | |||||
.then (res => navigate (`/posts/${ res.data.id }`))) | |||||
}}> | |||||
ランダム | |||||
</a> | |||||
</SidebarComponent>) | </SidebarComponent>) | ||||
} | } |
@@ -2,7 +2,7 @@ import axios from 'axios' | |||||
import toCamel from 'camelcase-keys' | import toCamel from 'camelcase-keys' | ||||
import { useEffect, useState } from 'react' | import { useEffect, useState } from 'react' | ||||
import { Helmet } from 'react-helmet' | 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 WikiBody from '@/components/WikiBody' | ||||
import PageTitle from '@/components/common/PageTitle' | import PageTitle from '@/components/common/PageTitle' | ||||