feat: オリジナルの作成日時追加( #101#117

Merged
みてるぞ merged 5 commits from '#101' into main 2025-09-22 05:05:31 +09:00
6 changed files with 142 additions and 21 deletions
Showing only changes of commit cafd55bf22 - Show all commits
+7 -2
View File
@@ -69,8 +69,11 @@ class PostsController < ApplicationController
url = params[:url]
thumbnail = params[:thumbnail]
tag_names = params[:tags].to_s.split(' ')
original_created_from = params[:original_created_from]
original_created_before = params[:original_created_before]
post = Post.new(title:, url:, thumbnail_base: '', uploaded_user: current_user)
post = Post.new(title:, url:, thumbnail_base: '', uploaded_user: current_user,
original_created_from:, original_created_before:)
post.thumbnail.attach(thumbnail)
if post.save
post.resized_thumbnail!
@@ -103,10 +106,12 @@ class PostsController < ApplicationController
title = params[:title]
tag_names = params[:tags].to_s.split(' ')
original_created_from = params[:original_created_from]
original_created_before = params[:original_created_before]
post = Post.find(params[:id].to_i)
tags = post.tags.where(category: 'nico').to_a + Tag.normalise_tags(tag_names)
if post.update(title:, tags:)
if post.update(title:, tags:, original_created_from:, original_created_before:)
render json: post.as_json(include: { tags: { only: [:id, :name, :category, :post_count] } }),
status: :ok
else
+18
View File
@@ -15,6 +15,8 @@ class Post < ApplicationRecord
foreign_key: :target_post_id
has_one_attached :thumbnail
validate :validate_original_created_range
def as_json options = { }
super(options).merge({ thumbnail: thumbnail.attached? ?
Rails.application.routes.url_helpers.rails_blob_url(
@@ -49,4 +51,20 @@ class Post < ApplicationRecord
filename: 'resized_thumbnail.jpg',
content_type: 'image/jpeg')
end
private
def validate_original_created_range
f = original_created_from
b = original_created_before
return if f.blank? || b.blank?
f = Time.zone.parse(f) if String === f
b = Time.zone.parse(b) if String === b
return if !(f) || !(b)
if f >= b
errors.add :original_created_before, 'オリジナルの作成日時の順番がをかしぃです.'
end
end
end
+33 -5
View File
@@ -3,6 +3,7 @@ import toCamel from 'camelcase-keys'
import { useState } from 'react'
import PostFormTagsArea from '@/components/PostFormTagsArea'
import DateTimeField from '@/components/common/DateTimeField'
import Label from '@/components/common/Label'
import { Button } from '@/components/ui/button'
import { API_BASE_URL } from '@/config'
@@ -16,6 +17,10 @@ type Props = { post: Post
export default (({ post, onSave }: Props) => {
const [originalCreatedBefore, setOriginalCreatedBefore] =
useState<string | null> (post.originalCreatedBefore)
const [originalCreatedFrom, setOriginalCreatedFrom] =
useState<string | null> (post.originalCreatedFrom)
const [title, setTitle] = useState (post.title)
const [tags, setTags] = useState<string> (post.tags
.filter (t => t.category !== 'nico')
@@ -23,13 +28,19 @@ export default (({ post, onSave }: Props) => {
.join (' '))
const handleSubmit = async () => {
const res = await axios.put (`${ API_BASE_URL }/posts/${ post.id }`, { title, tags },
const res = await axios.put (
`${ API_BASE_URL }/posts/${ post.id }`,
{ title, tags,
original_created_from: originalCreatedFrom,
original_created_before: originalCreatedBefore },
{ headers: { 'Content-Type': 'multipart/form-data',
'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } } )
'X-Transfer-Code': localStorage.getItem ('user_code') ?? '' } })
const data = toCamel (res.data as any, { deep: true }) as Post
onSave ({ ...post,
title: data.title,
tags: data.tags } as Post)
title: data.title,
tags: data.tags,
originalCreatedFrom: data.originalCreatedFrom,
originalCreatedBefore: data.originalCreatedBefore } as Post)
}
return (
@@ -40,12 +51,29 @@ export default (({ post, onSave }: Props) => {
<input type="text"
className="w-full border rounded p-2"
value={title}
onChange={e => setTitle (e.target.value)}/>
onChange={ev => setTitle (ev.target.value)}/>
</div>
{/* タグ */}
<PostFormTagsArea tags={tags} setTags={setTags}/>
{/* オリジナルの作成日時 */}
<div>
<Label></Label>
<div className="my-1">
<DateTimeField
value={originalCreatedFrom ?? undefined}
onChange={setOriginalCreatedFrom}/>
miteruzo marked this conversation as resolved Outdated
Outdated
Review

オリジナルの作成日時(以降),変更時にオリジナルの作成日時(より前)が自動設定されるやぅにしたぃ.
基本は +1 秒,秒が 0 なら +1 分,分、秒が 0 なら +1 日.

オリジナルの作成日時(以降),変更時にオリジナルの作成日時(より前)が自動設定されるやぅにしたぃ. 基本は +1 秒,秒が 0 なら +1 分,分、秒が 0 なら +1 日.
</div>
<div className="my-1">
<DateTimeField
value={originalCreatedBefore ?? undefined}
onChange={setOriginalCreatedBefore}/>
</div>
</div>
{/* 送信 */}
<Button onClick={handleSubmit}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400">
@@ -0,0 +1,43 @@
import { useEffect, useState } from 'react'
import type { FC } from 'react'
const pad = (n: number) => n.toString ().padStart (2, '0')
const toDateTimeLocalValue = (d: Date) => {
const y = d.getFullYear ()
const m = pad (d.getMonth () + 1)
const day = pad (d.getDate ())
const h = pad (d.getHours ())
const min = pad (d.getMinutes ())
const s = pad (d.getSeconds ())
return `${ y }-${ m }-${ day }T${ h }:${ min }:${ s }`
}
type Props = {
value?: string
onChange?: (isoUTC: string | null) => void }
export default (({ value, onChange }: Props) => {
const [local, setLocal] = useState ('')
useEffect (() => {
setLocal (value ? toDateTimeLocalValue (new Date (value)) : '')
}, [value])
return (
<input
className="border rounded p-2"
miteruzo marked this conversation as resolved
Review

“以降”、“より前” までの距離が窮屈なので mr-1 欲しぃかも.

“以降”、“より前” までの距離が窮屈なので `mr-1` 欲しぃかも.
type="datetime-local"
step={1}
value={local}
onChange={ev => {
const v = ev.target.value
setLocal (v)
onChange?.(v ? (new Date (v)).toISOString () : null)
}}/>)
}) satisfies FC<Props>
+30 -6
View File
@@ -4,6 +4,7 @@ import { Helmet } from 'react-helmet-async'
import { useNavigate } from 'react-router-dom'
import PostFormTagsArea from '@/components/PostFormTagsArea'
import DateTimeField from '@/components/common/DateTimeField'
import Form from '@/components/common/Form'
import Label from '@/components/common/Label'
import PageTitle from '@/components/common/PageTitle'
@@ -26,15 +27,17 @@ export default (({ user }: Props) => {
const navigate = useNavigate ()
const [originalCreatedBefore, setOriginalCreatedBefore] = useState<string | null> (null)
const [originalCreatedFrom, setOriginalCreatedFrom] = useState<string | null> (null)
const [tags, setTags] = useState ('')
const [thumbnailAutoFlg, setThumbnailAutoFlg] = useState (true)
const [thumbnailFile, setThumbnailFile] = useState<File | null> (null)
const [thumbnailLoading, setThumbnailLoading] = useState (false)
const [thumbnailPreview, setThumbnailPreview] = useState<string> ('')
const [title, setTitle] = useState ('')
const [titleAutoFlg, setTitleAutoFlg] = useState (true)
const [titleLoading, setTitleLoading] = useState (false)
const [url, setURL] = useState ('')
const [thumbnailFile, setThumbnailFile] = useState<File | null> (null)
const [thumbnailPreview, setThumbnailPreview] = useState<string> ('')
const [thumbnailAutoFlg, setThumbnailAutoFlg] = useState (true)
const [thumbnailLoading, setThumbnailLoading] = useState (false)
const [tags, setTags] = useState ('')
const previousURLRef = useRef ('')
@@ -45,6 +48,10 @@ export default (({ user }: Props) => {
formData.append ('tags', tags)
if (thumbnailFile)
formData.append ('thumbnail', thumbnailFile)
if (originalCreatedFrom)
formData.append ('original_created_from', originalCreatedFrom)
if (originalCreatedBefore)
formData.append ('original_created_before', originalCreatedBefore)
try
{
@@ -122,7 +129,7 @@ export default (({ user }: Props) => {
{/* URL */}
<div>
<Label>URL</Label>
<input type="text"
<input type="url"
placeholder="例:https://www.nicovideo.jp/watch/..."
value={url}
onChange={e => setURL (e.target.value)}
@@ -181,6 +188,23 @@ export default (({ user }: Props) => {
{/* タグ */}
<PostFormTagsArea tags={tags} setTags={setTags}/>
{/* オリジナルの作成日時 */}
<div>
<Label></Label>
<div className="my-1">
<DateTimeField
value={originalCreatedFrom ?? undefined}
onChange={setOriginalCreatedFrom}/>
miteruzo marked this conversation as resolved Outdated
Outdated
Review

上に同じ.

上に同じ.
</div>
<div className="my-1">
<DateTimeField
value={originalCreatedBefore ?? undefined}
onChange={setOriginalCreatedBefore}/>
</div>
</div>
{/* 送信 */}
<Button onClick={handleSubmit}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400"
+11 -8
View File
@@ -17,14 +17,17 @@ export type NicoTag = Tag & {
linkedTags: Tag[] }
export type Post = {
id: number
url: string
title: string
thumbnail: string
thumbnailBase: string
tags: Tag[]
viewed: boolean
related: Post[] }
id: number
url: string
title: string
thumbnail: string
thumbnailBase: string
tags: Tag[]
viewed: boolean
related: Post[]
createdAt: string
originalCreatedFrom: string | null
originalCreatedBefore: string | null }
export type SubMenuItem = {
component: React.ReactNode