diff --git a/backend/app/controllers/application_controller.rb b/backend/app/controllers/application_controller.rb index 4ac8823..dbe334b 100644 --- a/backend/app/controllers/application_controller.rb +++ b/backend/app/controllers/application_controller.rb @@ -1,2 +1,16 @@ 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 diff --git a/backend/app/controllers/posts_controller.rb b/backend/app/controllers/posts_controller.rb index 39d2ee1..cfd2100 100644 --- a/backend/app/controllers/posts_controller.rb +++ b/backend/app/controllers/posts_controller.rb @@ -24,7 +24,10 @@ class PostsController < ApplicationController # GET /posts/1 def show @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 # POST /posts @@ -38,6 +41,20 @@ class PostsController < ApplicationController 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 def update if @post.update(post_params) diff --git a/backend/app/models/user.rb b/backend/app/models/user.rb index 38f4f82..51e3d3d 100644 --- a/backend/app/models/user.rb +++ b/backend/app/models/user.rb @@ -17,6 +17,6 @@ class User < ApplicationRecord has_many :updated_wiki_pages, class_name: 'WikiPage', foreign_key: 'updated_user_id', dependent: :nullify def viewed? post - user_post_views.exists? post_id: post.id, viewed: true + user_post_views.exists? post_id: post.id end end diff --git a/backend/config/routes.rb b/backend/config/routes.rb index 882a5f8..a689b21 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -40,6 +40,8 @@ Rails.application.routes.draw do get "post_tags/create" get "post_tags/update" 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/show" get "nico_tag_relation/create" diff --git a/backend/db/migrate/20250529135850_remove_column_from_user_post_view.rb b/backend/db/migrate/20250529135850_remove_column_from_user_post_view.rb new file mode 100644 index 0000000..de6161e --- /dev/null +++ b/backend/db/migrate/20250529135850_remove_column_from_user_post_view.rb @@ -0,0 +1,5 @@ +class RemoveColumnFromUserPostView < ActiveRecord::Migration[8.0] + def change + remove_column :user_post_views, :viewed + end +end diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 541b85e..610a8d5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -19,7 +19,8 @@ type Post = { id: number url: string title: string thumbnail: string - tags: Tag[] } + tags: Tag[] + viewed: boolean } type User = { id: number name: string | null diff --git a/frontend/src/pages/PostDetailPage.tsx b/frontend/src/pages/PostDetailPage.tsx index 039d97f..46546ec 100644 --- a/frontend/src/pages/PostDetailPage.tsx +++ b/frontend/src/pages/PostDetailPage.tsx @@ -3,6 +3,9 @@ import { Link, useLocation, useParams } from 'react-router-dom' import axios from 'axios' import { API_BASE_URL, SITE_TITLE } from '../config' 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 name: string @@ -12,22 +15,51 @@ type Post = { id: number url: string title: string thumbnail: string - tags: Tag[] } + tags: Tag[] + viewed: boolean } type Props = { posts: Post[] setPosts: (posts: Post[]) => void } -const PostDetailPage = (props: Props) => { - const { posts, setPosts } = props +const PostDetailPage = ({ posts, setPosts }: Props) => { const { id } = useParams () 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 (() => { if (!(id)) 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])) .catch (err => console.error ('うんち!', err))) }, [id]) @@ -56,6 +88,11 @@ const PostDetailPage = (props: Props) => { else return {post.url} }) ()} + ) } diff --git a/frontend/src/pages/PostPage.tsx b/frontend/src/pages/PostPage.tsx index 605e7b6..50539f0 100644 --- a/frontend/src/pages/PostPage.tsx +++ b/frontend/src/pages/PostPage.tsx @@ -11,7 +11,8 @@ type Post = { id: number url: string title: string thumbnail: string - tags: Tag[] } + tags: Tag[] + viewed: boolean } type Props = { posts: Post[] setPosts: (posts: Post[]) => void }