このコミットが含まれているのは:
@@ -3,10 +3,18 @@ import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
|
||||
import Separator from '@/components/MenuSeparator'
|
||||
import PrefetchLink from '@/components/PrefetchLink'
|
||||
import TopNavUser from '@/components/TopNavUser'
|
||||
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
||||
import { LOCALE_LABELS,
|
||||
SUPPORTED_LOCALES,
|
||||
isLocaleCode,
|
||||
localeFromPath,
|
||||
switchLocalePath,
|
||||
unlocalisedPath } from '@/lib/locales'
|
||||
import { uiMessage, uiMessageLanguage } from '@/lib/messages'
|
||||
import { materialsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys'
|
||||
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
||||
import { canEditContent } from '@/lib/users'
|
||||
@@ -24,13 +32,16 @@ type NavDensity = 'auto' | 'comfortable' | 'compact'
|
||||
|
||||
|
||||
export const menuOutline = (
|
||||
{ tag, material, wikiId, user, pathName }: {
|
||||
{ tag, material, wikiId, user, pathName: fullPathname }: {
|
||||
tag?: Tag | null
|
||||
material?: Material | null
|
||||
wikiId: number | null
|
||||
user: User | null
|
||||
pathName: string },
|
||||
): Menu => {
|
||||
const pathName = unlocalisedPath (fullPathname)
|
||||
const locale = localeFromPath (fullPathname) ?? 'ja'
|
||||
const message = (key: Parameters<typeof uiMessage>[1]) => uiMessage (locale, key)
|
||||
const postCount = tag?.postCount ?? material?.tag?.postCount ?? 0
|
||||
const editable = canEditContent (user)
|
||||
|
||||
@@ -41,71 +52,73 @@ export const menuOutline = (
|
||||
|
||||
const materialFlg = /^\/materials\/\d+/.test (pathName)
|
||||
|
||||
const helpVsbl = /\/help\/((?!changes)[^\/?]+)/.test (pathName)
|
||||
const helpVsbl = /\/help\/((?!changes)[^/?]+)/.test (pathName)
|
||||
const helpIdentifier = pathName.split ('/')[2] ?? ''
|
||||
|
||||
return [
|
||||
{ name: '広場', to: '/posts', subMenu: [
|
||||
{ name: '一覧', to: '/posts' },
|
||||
{ name: '検索', to: '/posts/search' },
|
||||
{ name: '追加', to: '/posts/new', visible: editable },
|
||||
{ name: '全体履歴', to: '/posts/changes' },
|
||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
||||
{ name: 'タグ', to: '/tags', subMenu: [
|
||||
{ name: 'マスタ', to: '/tags' },
|
||||
{ name: 'ニコニコ連携', to: '/tags/nico' },
|
||||
{ name: '全体履歴', to: '/tags/changes' },
|
||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' },
|
||||
{ name: message ('square'), to: '/posts', subMenu: [
|
||||
{ name: message ('list'), to: '/posts' },
|
||||
{ name: message ('search'), to: '/posts/search' },
|
||||
{ name: message ('add'), to: '/posts/new', visible: editable },
|
||||
{ name: message ('allHistory'), to: '/posts/changes' },
|
||||
{ name: message ('help'), to: '/wiki/ヘルプ:広場' }] },
|
||||
{ name: message ('tags'), to: '/tags', subMenu: [
|
||||
{ name: message ('master'), to: '/tags' },
|
||||
{ name: message ('nicoLinks'), to: '/tags/nico' },
|
||||
{ name: message ('allHistory'), to: '/tags/changes' },
|
||||
{ name: message ('help'), to: '/wiki/ヘルプ:タグ' },
|
||||
{ component: <Separator/>, visible: tagFlg },
|
||||
{ name: `広場 (${ postCount || 0 })`,
|
||||
to: `/posts?tags=${ encodeURIComponent (tag?.name ?? '') }`,
|
||||
visible: tagFlg },
|
||||
{ name: '履歴', to: `/tags/changes?id=${ tag?.id }`,
|
||||
{ name: message ('history'), to: `/tags/changes?id=${ tag?.id }`,
|
||||
visible: tagFlg && tag?.category !== 'nico' }] },
|
||||
{ name: '素材', to: '/materials', visible: true, subMenu: [
|
||||
{ name: '一覧', to: '/materials' },
|
||||
{ name: '追加', to: '/materials/new', visible: editable },
|
||||
{ name: '抑止', to: '/materials/suppressions',
|
||||
{ name: message ('materials'), to: '/materials', visible: true, subMenu: [
|
||||
{ name: message ('list'), to: '/materials' },
|
||||
{ name: message ('add'), to: '/materials/new', visible: editable },
|
||||
{ name: message ('suppressions'), to: '/materials/suppressions',
|
||||
visible: user?.role === 'admin' },
|
||||
{ name: '全体履歴', to: '/materials/changes' },
|
||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:素材管理' },
|
||||
{ name: message ('allHistory'), to: '/materials/changes' },
|
||||
{ name: message ('help'), to: '/wiki/ヘルプ:素材管理' },
|
||||
{ component: <Separator/>, visible: materialFlg },
|
||||
{ name: `広場 (${ postCount || 0 })`,
|
||||
to: `/posts?tags=${ encodeURIComponent (material?.tag?.name ?? '') }`,
|
||||
visible: materialFlg && Boolean (material?.tag) },
|
||||
{ name: '履歴', to: `/materials/changes?material_id=${ material?.id }`,
|
||||
{ name: message ('history'), to: `/materials/changes?material_id=${ material?.id }`,
|
||||
visible: materialFlg }] },
|
||||
{ name: 'Wiki', to: '/wiki', subMenu: [
|
||||
{ name: '検索', to: '/wiki' },
|
||||
{ name: '新規', to: '/wiki/new', visible: editable },
|
||||
{ name: '全体履歴', to: '/wiki/changes' },
|
||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' },
|
||||
{ name: message ('wiki'), to: '/wiki', subMenu: [
|
||||
{ name: message ('search'), to: '/wiki' },
|
||||
{ name: message ('create'), to: '/wiki/new', visible: editable },
|
||||
{ name: message ('allHistory'), to: '/wiki/changes' },
|
||||
{ name: message ('help'), to: '/wiki/ヘルプ:Wiki' },
|
||||
{ component: <Separator/>, visible: wikiPageFlg },
|
||||
{ name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`,
|
||||
visible: wikiPageFlg },
|
||||
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
||||
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`,
|
||||
{ name: message ('history'), to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
||||
{ name: message ('edit'), to: `/wiki/${ wikiId || wikiTitle }/edit`,
|
||||
visible: wikiPageFlg && editable }] },
|
||||
{ name: 'ヘルプ', to: '/help/home', base: '/help', subMenu: [
|
||||
{ name: '検索', to: '/help' },
|
||||
{ name: '全体履歴', to: '/help/changes' },
|
||||
{ name: message ('help'), to: '/help/home', base: '/help', subMenu: [
|
||||
{ name: message ('search'), to: '/help' },
|
||||
{ name: message ('allHistory'), to: '/help/changes' },
|
||||
{ component: <Separator/>, visible: helpVsbl },
|
||||
{ name: '履歴',
|
||||
{ name: message ('history'),
|
||||
to: `/help/changes?identifier=${ helpIdentifier }`,
|
||||
visible: helpVsbl }] },
|
||||
{ name: 'おたのしみ', visible: false, subMenu: [
|
||||
{ name: '上映会 (β)', to: '/theatres/1' },
|
||||
{ name: 'グカネータ (β)', to: '/gekanator' }] },
|
||||
{ name: 'ユーザ', to: '/users/settings', visible: false, subMenu: [
|
||||
{ name: '一覧', to: '/users', visible: false },
|
||||
{ name: 'お前', to: `/users/${ user?.id }`, visible: false },
|
||||
{ name: '設定', to: '/users/settings', visible: Boolean (user) }] },
|
||||
{ name: '法規', visible: false, subMenu: [
|
||||
{ name: '利用規約', to: '/tos' }] }]
|
||||
{ name: message ('entertainment'), visible: false, subMenu: [
|
||||
{ name: message ('theatre'), to: '/theatres/1' },
|
||||
{ name: message ('gekanator'), to: '/gekanator' }] },
|
||||
{ name: message ('users'), to: '/users/settings', visible: false, subMenu: [
|
||||
{ name: message ('list'), to: '/users', visible: false },
|
||||
{ name: message ('currentUser'), to: `/users/${ user?.id }`, visible: false },
|
||||
{ name: message ('settings'), to: '/users/settings', visible: Boolean (user) }] },
|
||||
{ name: message ('legal'), visible: false, subMenu: [
|
||||
{ name: message ('terms'), to: '/tos' }] }]
|
||||
}
|
||||
|
||||
|
||||
const TopNav: FC<Props> = ({ user }) => {
|
||||
const locale = useLocale ()
|
||||
const navigate = useLocaleNavigate ()
|
||||
const location = useLocation ()
|
||||
const behaviourSettings = useClientBehaviourSettings ()
|
||||
const animationMode = behaviourSettings.animation ?? 'normal'
|
||||
@@ -151,25 +164,26 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
|
||||
const { data: wikiPage } = useQuery ({
|
||||
enabled: Boolean (wikiIdStr),
|
||||
queryKey: wikiKeys.show (wikiIdStr, { }),
|
||||
queryFn: () => fetchWikiPage (wikiIdStr, { }) })
|
||||
queryKey: wikiKeys.show (wikiIdStr, { }, locale),
|
||||
queryFn: () => fetchWikiPage (wikiIdStr, { }, locale) })
|
||||
|
||||
const tagFlg = /^\/tags\/\d+/.test (location.pathname)
|
||||
const materialFlg = /^\/materials\/\d+/.test (location.pathname)
|
||||
const pathname = unlocalisedPath (location.pathname)
|
||||
const tagFlg = /^\/tags\/\d+/.test (pathname)
|
||||
const materialFlg = /^\/materials\/\d+/.test (pathname)
|
||||
const effectiveTitle = (((tagFlg || materialFlg)
|
||||
? location.pathname.split ('/')[2]
|
||||
? pathname.split ('/')[2]
|
||||
: wikiPage?.title)
|
||||
?? '')
|
||||
|
||||
const { data: tag } = useQuery ({
|
||||
enabled: Boolean (effectiveTitle),
|
||||
queryKey: tagsKeys.show (effectiveTitle),
|
||||
queryFn: () => (tagFlg ? fetchTag : fetchTagByName) (effectiveTitle) })
|
||||
queryKey: tagsKeys.show (effectiveTitle, locale),
|
||||
queryFn: () => (tagFlg ? fetchTag : fetchTagByName) (effectiveTitle, locale) })
|
||||
|
||||
const { data: material } = useQuery ({
|
||||
enabled: Boolean (effectiveTitle),
|
||||
queryKey: materialsKeys.show (effectiveTitle),
|
||||
queryFn: () => fetchMaterial (effectiveTitle) })
|
||||
queryKey: materialsKeys.show (effectiveTitle, locale),
|
||||
queryFn: () => fetchMaterial (effectiveTitle, locale) })
|
||||
|
||||
const menu = menuOutline ({ tag, material, wikiId, user, pathName: location.pathname })
|
||||
const visibleMenu = menu.filter ((item): item is MenuVisibleItem => item.visible ?? true)
|
||||
@@ -177,7 +191,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
!(item.visible ?? true)
|
||||
|| item.subMenu.filter (subItem => subItem.visible ?? true).length > 0)
|
||||
const activeIdx =
|
||||
visibleMenu.findIndex (item => location.pathname.startsWith (item.base || item.to))
|
||||
visibleMenu.findIndex (item => pathname.startsWith (item.base || item.to))
|
||||
const submenuHeight = moreVsbl ? 40 * moreMenu.length : (activeIdx < 0 ? 0 : 40)
|
||||
const topNavTransition =
|
||||
animationsOff
|
||||
@@ -298,7 +312,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
return (
|
||||
<div ref={layoutRef} className="top-nav-layout w-full min-w-0 shrink-0"
|
||||
data-desktop={desktop} data-density={density}>
|
||||
<nav ref={barRef}
|
||||
<nav ref={barRef} lang={uiMessageLanguage (locale, 'square')}
|
||||
className="top-nav-root top-nav-bar flex justify-between items-center w-full">
|
||||
<div className="top-nav-primary flex shrink-0 items-center h-12">
|
||||
<PrefetchLink
|
||||
@@ -373,6 +387,22 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div className="flex min-w-0 justify-end px-2">
|
||||
<select
|
||||
aria-label={LOCALE_LABELS[locale]}
|
||||
value={locale}
|
||||
className="max-w-full rounded border border-gray-300 bg-white text-xs text-gray-900
|
||||
dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100"
|
||||
onChange={event => {
|
||||
const nextLocale = event.target.value
|
||||
if (isLocaleCode (nextLocale))
|
||||
navigate (switchLocalePath (location, nextLocale))
|
||||
}}>
|
||||
{SUPPORTED_LOCALES.map (code => (
|
||||
<option key={code} value={code}>{LOCALE_LABELS[code]}</option>))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
<motion.div
|
||||
key="submenu-shell"
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする