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
}) ()}
+
)
}
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 }