@@ -1,2 +1,16 @@ | |||||
class ApplicationController < ActionController::API | class ApplicationController < ActionController::API | ||||
before_action :authenticate_user | |||||
def current_user | |||||
@current_user | |||||
end | |||||
private | |||||
def authenticate_user | |||||
code = request.headers['X-Transfer-Code'] || request.headers['HTTP_X_TRANSFER_CODE'] | |||||
@current_user = User.find_by inheritance_code: code | |||||
Rails.logger.info("X-Transfer-Code: #{request.headers['X-Transfer-Code']}") | |||||
Rails.logger.info("current_user: #{@current_user&.id}") | |||||
end | |||||
end | end |
@@ -24,7 +24,10 @@ class PostsController < ApplicationController | |||||
# GET /posts/1 | # GET /posts/1 | ||||
def show | def show | ||||
@post = Post.includes(:tags).find(params[:id]) | @post = Post.includes(:tags).find(params[:id]) | ||||
render json: @post.as_json(include: { tags: { only: [:id, :name, :category] } }) | |||||
viewed = current_user&.viewed?(@post) | |||||
render json: (@post | |||||
.as_json(include: { tags: { only: [:id, :name, :category] } }) | |||||
.merge(viewed: viewed)) | |||||
end | end | ||||
# POST /posts | # POST /posts | ||||
@@ -38,6 +41,20 @@ class PostsController < ApplicationController | |||||
end | end | ||||
end | end | ||||
def viewed | |||||
return head :unauthorized unless current_user | |||||
current_user.viewed_posts << Post.find(params[:id]) | |||||
head :no_content | |||||
end | |||||
def unviewed | |||||
return head :unauthorized unless current_user | |||||
current_user.viewed_posts.delete(Post.find(params[:id])) | |||||
head :no_content | |||||
end | |||||
# PATCH/PUT /posts/1 | # PATCH/PUT /posts/1 | ||||
def update | def update | ||||
if @post.update(post_params) | if @post.update(post_params) | ||||
@@ -17,6 +17,6 @@ class User < ApplicationRecord | |||||
has_many :updated_wiki_pages, class_name: 'WikiPage', foreign_key: 'updated_user_id', dependent: :nullify | has_many :updated_wiki_pages, class_name: 'WikiPage', foreign_key: 'updated_user_id', dependent: :nullify | ||||
def viewed? post | def viewed? post | ||||
user_post_views.exists? post_id: post.id, viewed: true | |||||
user_post_views.exists? post_id: post.id | |||||
end | end | ||||
end | end |
@@ -40,6 +40,8 @@ Rails.application.routes.draw do | |||||
get "post_tags/create" | get "post_tags/create" | ||||
get "post_tags/update" | get "post_tags/update" | ||||
get "post_tags/destroy" | get "post_tags/destroy" | ||||
post 'posts/:id/viewed', to: 'posts#viewed' | |||||
delete 'posts/:id/viewed', to: 'posts#unviewed' | |||||
get "nico_tag_relation/index" | get "nico_tag_relation/index" | ||||
get "nico_tag_relation/show" | get "nico_tag_relation/show" | ||||
get "nico_tag_relation/create" | get "nico_tag_relation/create" | ||||
@@ -0,0 +1,5 @@ | |||||
class RemoveColumnFromUserPostView < ActiveRecord::Migration[8.0] | |||||
def change | |||||
remove_column :user_post_views, :viewed | |||||
end | |||||
end |
@@ -19,7 +19,8 @@ type Post = { id: number | |||||
url: string | url: string | ||||
title: string | title: string | ||||
thumbnail: string | thumbnail: string | ||||
tags: Tag[] } | |||||
tags: Tag[] | |||||
viewed: boolean } | |||||
type User = { id: number | type User = { id: number | ||||
name: string | null | name: string | null | ||||
@@ -3,6 +3,9 @@ import { Link, useLocation, useParams } from 'react-router-dom' | |||||
import axios from 'axios' | import axios from 'axios' | ||||
import { API_BASE_URL, SITE_TITLE } from '../config' | import { API_BASE_URL, SITE_TITLE } from '../config' | ||||
import NicoViewer from '../components/NicoViewer' | import NicoViewer from '../components/NicoViewer' | ||||
import { Button } from '@/components/ui/button' | |||||
import { toast } from '@/components/ui/use-toast' | |||||
import { cn } from '@/lib/utils' | |||||
type Tag = { id: number | type Tag = { id: number | ||||
name: string | name: string | ||||
@@ -12,22 +15,51 @@ type Post = { id: number | |||||
url: string | url: string | ||||
title: string | title: string | ||||
thumbnail: string | thumbnail: string | ||||
tags: Tag[] } | |||||
tags: Tag[] | |||||
viewed: boolean } | |||||
type Props = { posts: Post[] | type Props = { posts: Post[] | ||||
setPosts: (posts: Post[]) => void } | setPosts: (posts: Post[]) => void } | ||||
const PostDetailPage = (props: Props) => { | |||||
const { posts, setPosts } = props | |||||
const PostDetailPage = ({ posts, setPosts }: Props) => { | |||||
const { id } = useParams () | const { id } = useParams () | ||||
const location = useLocation () | const location = useLocation () | ||||
const changeViewedFlg = () => { | |||||
if (posts[0]?.viewed) | |||||
{ | |||||
void (axios.delete ( | |||||
`${ API_BASE_URL }/posts/${ id }/viewed`, | |||||
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') || '' } }) | |||||
.then (res => setPosts (([post]) => { | |||||
post.viewed = false | |||||
return [post] | |||||
})) | |||||
.catch (err => toast ({ title: '失敗……', | |||||
description: '通信に失敗しました……' }))) | |||||
} | |||||
else | |||||
{ | |||||
void (axios.post ( | |||||
`${ API_BASE_URL }/posts/${ id }/viewed`, | |||||
{ }, | |||||
{ headers: { 'X-Transfer-Code': localStorage.getItem ('user_code') || '' } }) | |||||
.then (res => setPosts (([post]) => { | |||||
post.viewed = true | |||||
return [post] | |||||
})) | |||||
.catch (err => toast ({ title: '失敗……', | |||||
description: '通信に失敗しました……' }))) | |||||
} | |||||
} | |||||
useEffect (() => { | useEffect (() => { | ||||
if (!(id)) | if (!(id)) | ||||
return | return | ||||
void (axios.get (`/api/posts/${ id }`) | |||||
void (axios.get (`${ API_BASE_URL }/posts/${ id }`, { headers: { | |||||
'X-Transfer-Code': localStorage.getItem ('user_code') || '' } }) | |||||
.then (res => setPosts ([res.data])) | .then (res => setPosts ([res.data])) | ||||
.catch (err => console.error ('うんち!', err))) | .catch (err => console.error ('うんち!', err))) | ||||
}, [id]) | }, [id]) | ||||
@@ -56,6 +88,11 @@ const PostDetailPage = (props: Props) => { | |||||
else | else | ||||
return <img src={post.thumbnail} alt={post.url} className="mb-4 w-full" /> | return <img src={post.thumbnail} alt={post.url} className="mb-4 w-full" /> | ||||
}) ()} | }) ()} | ||||
<Button onClick={changeViewedFlg} | |||||
className={cn ('text-white', | |||||
posts[0]?.viewed ? 'bg-blue-600 hover:bg-blue-700' : 'bg-gray-500 hover:bg-gray-600')}> | |||||
{posts[0]?.viewed ? '閲覧済' : '未閲覧'} | |||||
</Button> | |||||
</div>) | </div>) | ||||
} | } | ||||
@@ -11,7 +11,8 @@ type Post = { id: number | |||||
url: string | url: string | ||||
title: string | title: string | ||||
thumbnail: string | thumbnail: string | ||||
tags: Tag[] } | |||||
tags: Tag[] | |||||
viewed: boolean } | |||||
type Props = { posts: Post[] | type Props = { posts: Post[] | ||||
setPosts: (posts: Post[]) => void } | setPosts: (posts: Post[]) => void } | ||||