設定画面 (#34) #397

マージ済み
みてるぞ が 31 個のコミットを feature/034 から main へマージ 2026-07-07 00:48:41 +09:00
3個のファイルの変更94行の追加72行の削除
コミット 4478bc92ed の変更だけを表示してゐます - すべてのコミットを表示
+6 -1
ファイルの表示
@@ -12,7 +12,8 @@ import TopNav from '@/components/TopNav'
import DialogueProvider from '@/components/dialogues/DialogueProvider' import DialogueProvider from '@/components/dialogues/DialogueProvider'
import { Toaster } from '@/components/ui/toaster' import { Toaster } from '@/components/ui/toaster'
import { apiPost, isApiError } from '@/lib/api' import { apiPost, isApiError } from '@/lib/api'
import { applyClientAppearance, import { applyClientAnimationMode,
applyClientAppearance,
fetchUserThemeSlots, fetchUserThemeSlots,
fetchUserSettings, fetchUserSettings,
getClientThemeMode, getClientThemeMode,
@@ -142,6 +143,10 @@ const App: FC = () => {
return () => mediaQuery.removeEventListener ('change', handleThemeChange) return () => mediaQuery.removeEventListener ('change', handleThemeChange)
}, []) }, [])
useEffect (() => {
applyClientAnimationMode (animationMode)
}, [animationMode])
useEffect (() => { useEffect (() => {
const createUser = async () => { const createUser = async () => {
const data = await apiPost<{ code: string; user: User }> ('/users') const data = await apiPost<{ code: string; user: User }> ('/users')
+51 -38
ファイルの表示
@@ -30,9 +30,6 @@ import type { FC, MutableRefObject, ReactNode } from 'react'
import type { Category, Post, TagWithSections } from '@/types' import type { Category, Post, TagWithSections } from '@/types'
type TagByCategory = { [key in Category]: TagWithSections[] } type TagByCategory = { [key in Category]: TagWithSections[] }
type FlatTagRow = {
tag: TagWithSections
parentTagId?: number }
const renderTagTree = ( const renderTagTree = (
@@ -64,34 +61,6 @@ const renderTagTree = (
?? [])] ?? [])]
} }
const flattenTagTree = (
tags: TagWithSections[],
): FlatTagRow[] => {
const seen = new Set<number> ()
const rows: FlatTagRow[] = []
const visit = (
tag: TagWithSections,
parentTagId?: number,
) => {
if (seen.has (tag.id))
return
seen.add (tag.id)
rows.push ({ tag, parentTagId })
for (const child of tag.children ?? [])
visit (child, tag.id)
}
for (const tag of tags)
visit (tag)
return rows.sort ((rowA, rowB) => rowA.tag.name < rowB.tag.name ? -1 : 1)
}
const isDescendant = ( const isDescendant = (
root: TagWithSections, root: TagWithSections,
targetId: number, targetId: number,
@@ -158,6 +127,37 @@ const buildTagByCategory = (post: Post): TagByCategory => {
} }
const buildFlatTagByCategory = (
byCategory: TagByCategory,
): TagByCategory => {
const tagsTmp = { } as TagByCategory
const seen = new Set<number> ()
for (const category of CATEGORIES)
tagsTmp[category] = []
const visit = (tag: TagWithSections) => {
if (seen.has (tag.id))
return
seen.add (tag.id)
tagsTmp[tag.category].push ({ ...tag, children: [] })
for (const child of tag.children ?? [])
visit (child)
}
for (const category of CATEGORIES)
for (const tag of byCategory[category] ?? [])
visit (tag)
for (const category of CATEGORIES)
tagsTmp[category].sort ((tagA, tagB) => tagA.name < tagB.name ? -1 : 1)
return tagsTmp
}
const changeCategory = async ( const changeCategory = async (
tagId: number, tagId: number,
category: Category): Promise<void> => { category: Category): Promise<void> => {
@@ -210,6 +210,10 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
const [dragging, setDragging] = useState (false) const [dragging, setDragging] = useState (false)
const [saving, setSaving] = useState (false) const [saving, setSaving] = useState (false)
const [tags, setTags] = useState (baseTags) const [tags, setTags] = useState (baseTags)
const flatTagsByCategory = useMemo<TagByCategory> (
() => buildFlatTagByCategory (tags),
[tags],
)
const suppressClickRef = useRef (false) const suppressClickRef = useRef (false)
@@ -343,7 +347,16 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
document.body.style.userSelect = '' document.body.style.userSelect = ''
}} }}
modifiers={[restrictToWindowEdges]}> modifiers={[restrictToWindowEdges]}>
{CATEGORIES.map ((cat: Category) => ((tags[cat] ?? []).length > 0 || dragging) && ( {CATEGORIES.map ((cat: Category) => {
const categoryTags =
tagRelationDisplay === 'grouped'
? (tags[cat] ?? [])
: (flatTagsByCategory[cat] ?? [])
if (!(categoryTags.length > 0 || dragging))
return null
return (
<div className="my-3" key={cat}> <div className="my-3" key={cat}>
<SubsectionTitle> <SubsectionTitle>
<motion.div <motion.div
@@ -365,19 +378,19 @@ const TagDetailSidebar: FC<Props> = ({ className, post, sp }) => {
sp, sp,
), ),
) )
: flattenTagTree (tags[cat] ?? []).map (row => ( : (flatTagsByCategory[cat] ?? []).map (tag => (
<li key={`flat-${ cat }-${ row.tag.id }`} className="mb-1"> <li key={`flat-${ cat }-${ tag.id }`} className="mb-1">
<DraggableDroppableTagRow <DraggableDroppableTagRow
tag={row.tag} tag={tag}
nestLevel={0} nestLevel={0}
pathKey={`flat-${ cat }-${ row.tag.id }`} pathKey={`flat-${ cat }-${ tag.id }`}
parentTagId={row.parentTagId}
suppressClickRef={suppressClickRef} suppressClickRef={suppressClickRef}
sp={sp}/> sp={sp}/>
</li>)))} </li>)))}
<DropSlot cat={cat}/> <DropSlot cat={cat}/>
</ul> </ul>
</div>))} </div>)
})}
{post && ( {post && (
<motion.div <motion.div
layoutId={`post-info-${ sp }`} layoutId={`post-info-${ sp }`}
+6 -2
ファイルの表示
@@ -161,11 +161,15 @@ const TopNav: FC<Props> = ({ user }) => {
visibleMenu.findIndex (item => location.pathname.startsWith (item.base || item.to)) visibleMenu.findIndex (item => location.pathname.startsWith (item.base || item.to))
const submenuHeight = moreVsbl ? 40 * moreMenu.length : (activeIdx < 0 ? 0 : 40) const submenuHeight = moreVsbl ? 40 * moreMenu.length : (activeIdx < 0 ? 0 : 40)
const topNavTransition = const topNavTransition =
reducedAnimations animationsOff
? { duration: 0 }
: reducedAnimations
? { duration: .08, ease: 'linear' as const } ? { duration: .08, ease: 'linear' as const }
: { duration: .2, ease: 'easeOut' as const } : { duration: .2, ease: 'easeOut' as const }
const opacityTransition = const opacityTransition =
reducedAnimations animationsOff
? { duration: 0 }
: reducedAnimations
? { duration: .08 } ? { duration: .08 }
: { duration: .12 } : { duration: .12 }
const highlightTransitionClass = const highlightTransitionClass =