| @@ -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 } | ||||