This commit is contained in:
2026-05-23 03:50:54 +09:00
parent 7b6b24b9c5
commit 7fcfc8b8aa
10 changed files with 55 additions and 23 deletions
+2 -2
View File
@@ -392,7 +392,7 @@ class PostsController < ApplicationController
tag = tags_by_id[tag_id] tag = tags_by_id[tag_id]
return nil unless tag return nil unless tag
sections = PostTagSection.where(post_id: post.id, tag_id: tag.id) sections = PostTagSection.where(post_id: post.id, tag_id:)
.as_json(only: [:begin_ms, :end_ms]) .as_json(only: [:begin_ms, :end_ms])
if path.include?(tag_id) if path.include?(tag_id)
@@ -419,7 +419,7 @@ class PostsController < ApplicationController
params[:parent_post_ids].to_s.split.map { |token| params[:parent_post_ids].to_s.split.map { |token|
id = Integer(token, exception: false) id = Integer(token, exception: false)
raise ArgumentError, "親投稿 Id. が不正です: #{ token }" if id.nil? || id <= 0 raise ArgumentError, "親投稿 Id. が不正です: #{ token }" if !(id) || id <= 0
id id
}.uniq }.uniq
+7 -2
View File
@@ -102,7 +102,7 @@ class Tag < ApplicationRecord
tags = tag_names.map do |name| tags = tag_names.map do |name|
pf, cat = CATEGORY_PREFIXES.find { |p, _| name.downcase.start_with?(p) } || ['', nil] pf, cat = CATEGORY_PREFIXES.find { |p, _| name.downcase.start_with?(p) } || ['', nil]
name = TagName.canonicalise(name.sub(/\A#{ pf }/i, '')).first name = name.sub(/\A#{ pf }/i, '')
sections_by_tag = [] sections_by_tag = []
while n = name.sub!(/^(\S*?)\[([0-9:.]*?)-([0-9:.]*?)\](\S*?)$/, '\1\4 \2 \3') while n = name.sub!(/^(\S*?)\[([0-9:.]*?)-([0-9:.]*?)\](\S*?)$/, '\1\4 \2 \3')
@@ -116,9 +116,14 @@ class Tag < ApplicationRecord
sections_by_tag << [begin_ms, end_ms] sections_by_tag << [begin_ms, end_ms]
end end
name = TagName.canonicalise(name).first
find_or_create_by_tag_name!(name, category: (cat || :general)).tap do |tag| find_or_create_by_tag_name!(name, category: (cat || :general)).tap do |tag|
tag.update!(category: cat) if cat && tag.category != cat tag.update!(category: cat) if cat && tag.category != cat
sections[tag.id] = sections_by_tag if sections_by_tag.present? next if sections_by_tag.blank?
sections[tag.id] ||= []
sections[tag.id].concat(sections_by_tag)
end end
end end
@@ -0,0 +1,8 @@
FactoryBot.define do
factory :post_tag_section do
association :post
association :tag
begin_ms { 1_000 }
end_ms { 2_000 }
end
end
+6
View File
@@ -0,0 +1,6 @@
FactoryBot.define do
factory :post_tag do
association :post
association :tag
end
end
+8
View File
@@ -0,0 +1,8 @@
FactoryBot.define do
factory :post do
sequence(:url) { |n| "https://example.com/factory-post-#{ n }" }
title { 'factory post' }
thumbnail_base { nil }
uploaded_user { nil }
end
end
+9 -6
View File
@@ -12,21 +12,24 @@ import { msToTime } from '@/lib/utils'
import type { FC, FormEvent } from 'react' import type { FC, FormEvent } from 'react'
import type { Post, Tag } from '@/types' import type { Post, TagWithSections } from '@/types'
const tagsToStr = (tags: Tag[]): string => { const tagsToStr = (tags: TagWithSections[]): string => {
const result: Tag[] = [] const result: Omit<TagWithSections, 'children'>[] = []
const walk = (tag: Tag) => { const walk = (tag: TagWithSections) => {
const { children, ...rest } = tag const { children, ...rest } = tag
result.push (rest) result.push (rest)
children?.forEach (walk) children.forEach (walk)
} }
tags.filter (t => t.category !== 'nico').forEach (walk) tags.filter (t => t.category !== 'nico').forEach (walk)
return [...(new Set (result.map (t => `${ t.name }${ t.sections.map (s => `[${ msToTime (s.beginMs) }-${ msToTime (s.endMs) }]`).join ('') }`)))].join (' ') return [...(new Set (result.map (t =>
`${ t.name }${ t.sections
.map (s => `[${ msToTime (s.beginMs) }-${ msToTime (s.endMs) }]`)
.join ('') }`)))].join (' ')
} }
+7 -7
View File
@@ -26,13 +26,13 @@ import { dateString, originalCreatedAtString } from '@/lib/utils'
import type { DragEndEvent } from '@dnd-kit/core' import type { DragEndEvent } from '@dnd-kit/core'
import type { FC, MutableRefObject, ReactNode } from 'react' import type { FC, MutableRefObject, ReactNode } from 'react'
import type { Category, Post, Tag } from '@/types' import type { Category, Post, TagWithSections } from '@/types'
type TagByCategory = { [key in Category]: Tag[] } type TagByCategory = { [key in Category]: TagWithSections[] }
const renderTagTree = ( const renderTagTree = (
tag: Tag, tag: TagWithSections,
nestLevel: number, nestLevel: number,
path: string, path: string,
suppressClickRef: MutableRefObject<boolean>, suppressClickRef: MutableRefObject<boolean>,
@@ -63,7 +63,7 @@ const renderTagTree = (
const isDescendant = ( const isDescendant = (
root: Tag, root: TagWithSections,
targetId: number, targetId: number,
): boolean => { ): boolean => {
if (!(root.children)) if (!(root.children))
@@ -84,8 +84,8 @@ const isDescendant = (
const findTag = ( const findTag = (
byCat: TagByCategory, byCat: TagByCategory,
id: number, id: number,
): Tag | undefined => { ): TagWithSections | undefined => {
const walk = (nodes: Tag[]): Tag | undefined => { const walk = (nodes: TagWithSections[]): TagWithSections | undefined => {
for (const t of nodes) for (const t of nodes)
{ {
if (t.id === id) if (t.id === id)
@@ -167,7 +167,7 @@ const TagDetailSidebar: FC<Props> = ({ post, sp }) => {
} }
for (const cat of Object.keys (tagsTmp) as (keyof typeof tagsTmp)[]) for (const cat of Object.keys (tagsTmp) as (keyof typeof tagsTmp)[])
tagsTmp[cat].sort ((tagA: Tag, tagB: Tag) => tagA.name < tagB.name ? -1 : 1) tagsTmp[cat].sort ((tagA: TagWithSections, tagB: TagWithSections) => tagA.name < tagB.name ? -1 : 1)
return tagsTmp return tagsTmp
}, [post]) }, [post])
+1 -1
View File
@@ -77,7 +77,7 @@ export const msToTime = (ms: number): string => {
const totalS = Math.trunc (ms / 1_000) const totalS = Math.trunc (ms / 1_000)
const s = String (totalS % 60) const s = String (totalS % 60)
const min = String (Math.trunc (totalS / 60) % 60) const min = String (Math.trunc (totalS / 60) % 60)
const h = String (Math.trunc (totalS / 3_600)) const h = Math.trunc (totalS / 3_600)
return (h > 0 return (h > 0
? `${ h }:${ min.padStart (2, '0') }:${ s.padStart (2, '0') }` ? `${ h }:${ min.padStart (2, '0') }:${ s.padStart (2, '0') }`
+4 -2
View File
@@ -1,6 +1,6 @@
import type { Material, Post, Tag, User, WikiPage } from '@/types' import type { Material, Post, TagWithSections, User, WikiPage } from '@/types'
export const buildTag = (overrides: Partial<Tag> = {}): Tag => ({ export const buildTag = (overrides: Partial<TagWithSections> = {}): TagWithSections => ({
id: 1, id: 1,
name: 'テストタグ', name: 'テストタグ',
category: 'general', category: 'general',
@@ -13,6 +13,8 @@ export const buildTag = (overrides: Partial<Tag> = {}): Tag => ({
materialId: null, materialId: null,
hasDeerjikists: false, hasDeerjikists: false,
matchedAlias: null, matchedAlias: null,
sections: [],
children: [],
...overrides, ...overrides,
}) })