このコミットが含まれているのは:
@@ -8,6 +8,8 @@ const COLUMN_COUNT = 12
|
||||
|
||||
|
||||
const DevModeWatermark: FC = () => {
|
||||
const { t } = useTranslation ('shared')
|
||||
const text = t ('labels.development', { returnDetails: true })
|
||||
return (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
@@ -29,13 +31,14 @@ const DevModeWatermark: FC = () => {
|
||||
{Array.from ({ length: COLUMN_COUNT }, (_, columnIndex) => (
|
||||
<span
|
||||
key={columnIndex}
|
||||
lang={text.usedLng}
|
||||
className={
|
||||
'whitespace-nowrap text-3xl font-bold '
|
||||
+ 'tracking-[0.3em] text-neutral-950/5 '
|
||||
+ 'dark:text-white/10'
|
||||
}
|
||||
style={{ fontFamily: 'Nikumaru' }}>
|
||||
開発モード
|
||||
{text.res}
|
||||
</span>))}
|
||||
</div>))}
|
||||
</div>
|
||||
@@ -44,3 +47,5 @@ const DevModeWatermark: FC = () => {
|
||||
|
||||
|
||||
export default DevModeWatermark
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import nikumaru from '@/assets/fonts/nikumaru.otf'
|
||||
import errorImg from '@/assets/images/not-found.gif'
|
||||
import MainArea from '@/components/layout/MainArea'
|
||||
import { SITE_TITLE } from '@/config'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -11,17 +12,16 @@ type Props = { status: number }
|
||||
|
||||
|
||||
const ErrorScreen: FC<Props> = ({ status }) => {
|
||||
const [message, rightMsg, leftMsg]: [string, string, string] = (() => {
|
||||
const { message: translate, label } = useUITranslation ('errors')
|
||||
const [message]: [string, string, string] = (() => {
|
||||
switch (status)
|
||||
{
|
||||
case 403:
|
||||
return ['権限ないよ(笑)', '帰れ!', '帰れ!']
|
||||
case 404:
|
||||
return ['ページないよ(笑)', '帰れ!', '帰れ!']
|
||||
case 500:
|
||||
return ['鯖でエラー出たって(嘲笑)', '管理人に', '聯絡を!']
|
||||
case 503:
|
||||
return ['鯖死んでるよ(泣)', '管理人に', '聯絡を!']
|
||||
return [translate (`${ status }.message`).text,
|
||||
translate (`${ status }.right`).text, translate (`${ status }.left`).text]
|
||||
default:
|
||||
throw new Error
|
||||
}
|
||||
@@ -51,13 +51,13 @@ const ErrorScreen: FC<Props> = ({ status }) => {
|
||||
style={{ fontFamily: 'Nikumaru' }}>
|
||||
<p>{status}</p>
|
||||
<div className="flex flex-row space-x-1 sm:space-x-2 md:space-x-4">
|
||||
<p style={{ writingMode: 'vertical-rl' }}>{leftMsg}</p>
|
||||
<img className="max-w-[70vw]" src={errorImg} alt="逃げたギター"/>
|
||||
<p style={{ writingMode: 'vertical-rl' }}>{rightMsg}</p>
|
||||
<p style={{ writingMode: 'vertical-rl' }}>{label (`${ status }.left`)}</p>
|
||||
<img className="max-w-[70vw]" src={errorImg} alt={translate ('imageAlt').text}/>
|
||||
<p style={{ writingMode: 'vertical-rl' }}>{label (`${ status }.right`)}</p>
|
||||
</div>
|
||||
<p className="mr-[-.5em]">{message}</p>
|
||||
<p className="mr-[-.5em]">{label (`${ status }.message`)}</p>
|
||||
</div>
|
||||
</MainArea>)
|
||||
}
|
||||
|
||||
export default ErrorScreen
|
||||
export default ErrorScreen
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
import { useLocale } from '@/lib/useLocale'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import TagSearchBox from '@/components/TagSearchBox'
|
||||
import FormField from '@/components/common/FormField'
|
||||
import TextArea from '@/components/common/TextArea'
|
||||
@@ -39,6 +40,7 @@ type Props = Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'onChange' | '
|
||||
|
||||
|
||||
const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
|
||||
const { label } = useUITranslation ('navigation')
|
||||
const locale = useLocale ()
|
||||
const ref = useRef<HTMLTextAreaElement> (null)
|
||||
|
||||
@@ -79,7 +81,7 @@ const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<FormField className="relative w-full" label="タグ" messages={errors}>
|
||||
<FormField className="relative w-full" label={label ('tags')} messages={errors}>
|
||||
{({ describedBy, invalid }) => (
|
||||
<>
|
||||
<TextArea
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRef } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import PrefetchLink from '@/components/PrefetchLink'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSharedTransitionStore } from '@/stores/sharedTransitionStore'
|
||||
@@ -16,6 +17,7 @@ type Props = { posts: Post[]
|
||||
|
||||
|
||||
const PostList: FC<Props> = ({ posts, onClick }) => {
|
||||
const { label } = useUITranslation ('shared')
|
||||
const location = useLocation ()
|
||||
const behaviourSettings = useClientBehaviourSettings ()
|
||||
const thumbnailMode = behaviourSettings.thumbnailMode ?? 'normal'
|
||||
@@ -75,7 +77,7 @@ const PostList: FC<Props> = ({ posts, onClick }) => {
|
||||
? (
|
||||
<div className="flex h-full w-full items-center justify-center
|
||||
bg-muted text-center text-xs text-muted-foreground">
|
||||
サムネイルなし
|
||||
{label ('labels.noThumbnail')}
|
||||
</div>)
|
||||
: (
|
||||
<img
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import DateTimeField, { toMinutePrecisionIsoUtc } from '@/components/common/DateTimeField'
|
||||
import FormField from '@/components/common/FormField'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -19,9 +20,11 @@ const PostOriginalCreatedTimeField: FC<Props> = (
|
||||
setOriginalCreatedFrom,
|
||||
originalCreatedBefore,
|
||||
setOriginalCreatedBefore,
|
||||
errors }: Props) => {
|
||||
errors }: Props,
|
||||
) => {
|
||||
const { label } = useUITranslation ('shared')
|
||||
return (
|
||||
<FormField label="オリジナルの作成日時" messages={errors}>
|
||||
<FormField label={label ('labels.originalCreatedAt')} messages={errors}>
|
||||
{({ describedBy, invalid }) => (
|
||||
<>
|
||||
<div className="my-1 flex">
|
||||
@@ -46,7 +49,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
|
||||
d.setMinutes (d.getMinutes () + 1)
|
||||
setOriginalCreatedBefore (toMinutePrecisionIsoUtc (d.toISOString ()))
|
||||
}}/>
|
||||
以降
|
||||
{label ('labels.since')}
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
@@ -56,7 +59,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
|
||||
onClick={() => {
|
||||
setOriginalCreatedFrom (null)
|
||||
}}>
|
||||
リセット
|
||||
{label ('actions.reset')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,7 +74,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
|
||||
invalid={invalid}
|
||||
value={originalCreatedBefore ?? undefined}
|
||||
onChange={setOriginalCreatedBefore}/>
|
||||
より前
|
||||
{label ('labels.before')}
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
@@ -81,7 +84,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
|
||||
onClick={() => {
|
||||
setOriginalCreatedBefore (null)
|
||||
}}>
|
||||
リセット
|
||||
{label ('actions.reset')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import { apiGet } from '@/lib/api'
|
||||
import { inputClass } from '@/lib/utils'
|
||||
|
||||
@@ -15,6 +16,7 @@ import type { Tag } from '@/types'
|
||||
|
||||
|
||||
const TagSearch: FC = () => {
|
||||
const { message } = useUITranslation ('shared')
|
||||
const locale = useLocale ()
|
||||
const location = useLocation ()
|
||||
const navigate = useLocaleNavigate ()
|
||||
@@ -108,7 +110,7 @@ const TagSearch: FC = () => {
|
||||
<div className="relative w-full">
|
||||
<input type="text"
|
||||
data-shortcut-focus-search="true"
|
||||
placeholder="タグ検索..."
|
||||
placeholder={message ('labels.tagSearch').text}
|
||||
value={search}
|
||||
onChange={whenChanged}
|
||||
onFocus={() => setSuggestionsVsbl (true)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import TagLink from '@/components/TagLink'
|
||||
import TagSearch from '@/components/TagSearch'
|
||||
import SectionTitle from '@/components/common/SectionTitle'
|
||||
@@ -23,6 +24,7 @@ type Props = { posts: Post[]
|
||||
|
||||
|
||||
const TagSidebar: FC<Props> = ({ posts, onClick }) => {
|
||||
const { label } = useUITranslation ('shared')
|
||||
const locale = useLocale ()
|
||||
const navigate = useLocaleNavigate ()
|
||||
const behaviourSettings = useClientBehaviourSettings ()
|
||||
@@ -71,7 +73,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
|
||||
|
||||
const TagBlock = (
|
||||
<>
|
||||
<SectionTitle>タグ</SectionTitle>
|
||||
<SectionTitle>{label ('navigation:tags')}</SectionTitle>
|
||||
<ul>
|
||||
{CATEGORIES.flatMap (cat => cat in tags ? (
|
||||
tags[cat].map (tag => (
|
||||
@@ -90,7 +92,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
|
||||
</motion.div>)}
|
||||
</li>))) : [])}
|
||||
</ul>
|
||||
<SectionTitle>関聯</SectionTitle>
|
||||
<SectionTitle>{label ('labels.relations')}</SectionTitle>
|
||||
<a href="#"
|
||||
onClick={ev => {
|
||||
ev.preventDefault ()
|
||||
@@ -108,7 +110,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
|
||||
}
|
||||
}) ())
|
||||
}}>
|
||||
ランダム
|
||||
{label ('actions.random')}
|
||||
</a>
|
||||
</>)
|
||||
|
||||
@@ -148,7 +150,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
|
||||
ev.preventDefault ()
|
||||
setTagsVsbl (v => !(v))
|
||||
}}>
|
||||
{tagsVsbl ? '▲▲▲ タグ一覧を閉じる ▲▲▲' : '▼▼▼ タグ一覧を表示 ▼▼▼'}
|
||||
{tagsVsbl ? label ('labels.hideTags') : label ('labels.showTags')}
|
||||
</a>
|
||||
</SidebarComponent>)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { isLocaleCode,
|
||||
switchLocalePath,
|
||||
unlocalisedPath } from '@/lib/locales'
|
||||
import { uiMessage, uiMessageLanguage } from '@/lib/messages'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import { materialsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys'
|
||||
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
||||
import { canEditContent } from '@/lib/users'
|
||||
@@ -125,6 +126,7 @@ export const menuOutline = (
|
||||
|
||||
|
||||
const TopNav: FC<Props> = ({ user, locales }) => {
|
||||
const { label } = useUITranslation ('shared')
|
||||
const locale = useLocale ()
|
||||
const localeName = locales.find (entry => entry.code === locale)?.name
|
||||
const navigate = useLocaleNavigate ()
|
||||
@@ -206,21 +208,21 @@ const TopNav: FC<Props> = ({ user, locales }) => {
|
||||
const topNavTransition =
|
||||
animationsOff
|
||||
? { duration: 0 }
|
||||
: reducedAnimations
|
||||
? { duration: .08, ease: 'linear' as const }
|
||||
: { duration: .2, ease: 'easeOut' as const }
|
||||
: (reducedAnimations
|
||||
? { duration: .08, ease: 'linear' as const }
|
||||
: { duration: .2, ease: 'easeOut' as const })
|
||||
const opacityTransition =
|
||||
animationsOff
|
||||
? { duration: 0 }
|
||||
: reducedAnimations
|
||||
? { duration: .08 }
|
||||
: { duration: .12 }
|
||||
: (reducedAnimations
|
||||
? { duration: .08 }
|
||||
: { duration: .12 })
|
||||
const highlightTransitionClass =
|
||||
animationsOff
|
||||
? undefined
|
||||
: reducedAnimations
|
||||
? 'transition-[transform,width] duration-75 ease-linear'
|
||||
: 'transition-[transform,width] duration-200 ease-out'
|
||||
: (reducedAnimations
|
||||
? 'transition-[transform,width] duration-75 ease-linear'
|
||||
: 'transition-[transform,width] duration-200 ease-out')
|
||||
|
||||
const prevActiveIdxRef = useRef<number> (activeIdx)
|
||||
|
||||
@@ -382,8 +384,7 @@ const TopNav: FC<Props> = ({ user, locales }) => {
|
||||
onClick={() => {
|
||||
scroll (0, 0)
|
||||
}}>
|
||||
{/* TODO: ここも外出ししろ. */}
|
||||
ぼざクリ タグ広場
|
||||
{label ('labels.brand')}
|
||||
</PrefetchLink>
|
||||
|
||||
<div ref={navRef}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Separator from '@/components/MenuSeparator'
|
||||
import PrefetchLink from '@/components/PrefetchLink'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -11,6 +12,7 @@ type Props = { user: User | null,
|
||||
|
||||
|
||||
const TopNavUser: FC<Props> = ({ user, sp }) => {
|
||||
const { label } = useUITranslation ('shared')
|
||||
if (!(user))
|
||||
return
|
||||
|
||||
@@ -24,7 +26,7 @@ const TopNavUser: FC<Props> = ({ user, sp }) => {
|
||||
<>
|
||||
{sp && <Separator/>}
|
||||
<PrefetchLink to="/users/settings" className={className}>
|
||||
{user.name || '名もなきニジラー'}
|
||||
{user.name || label ('labels.anonymous')}
|
||||
</PrefetchLink>
|
||||
</>)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown'
|
||||
import remarkGFM from 'remark-gfm'
|
||||
|
||||
import { useLocale } from '@/lib/useLocale'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
import PrefetchLink from '@/components/PrefetchLink'
|
||||
import { wikiKeys } from '@/lib/queryKeys'
|
||||
import remarkWikiAutoLink from '@/lib/remark-wiki-autolink'
|
||||
@@ -27,6 +28,7 @@ const mdComponents = { a: (({ href, children }) => (
|
||||
|
||||
|
||||
const WikiBody: FC<Props> = ({ title, body }) => {
|
||||
const { message } = useUITranslation ('wiki')
|
||||
const locale = useLocale ()
|
||||
const { data } = useQuery ({
|
||||
enabled: Boolean (body),
|
||||
@@ -39,8 +41,9 @@ const WikiBody: FC<Props> = ({ title, body }) => {
|
||||
|
||||
return (
|
||||
<ReactMarkdown components={mdComponents} remarkPlugins={remarkPlugins}>
|
||||
{body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`}
|
||||
{body || message ('missingPage', {
|
||||
url: `/wiki/new?title=${ encodeURIComponent (title) }` }).text}
|
||||
</ReactMarkdown>)
|
||||
}
|
||||
|
||||
export default WikiBody
|
||||
export default WikiBody
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
|
||||
import PostTagsField from '@/components/posts/PostTagsField'
|
||||
import PostTextField from '@/components/posts/PostTextField'
|
||||
import TranslatedText from '@/components/TranslatedText'
|
||||
|
||||
import type { FC, ReactNode } from 'react'
|
||||
|
||||
@@ -54,7 +55,7 @@ const PostCoreDataFields: FC<PostCoreDataFieldsProps> = (
|
||||
) => (
|
||||
<>
|
||||
<PostTextField
|
||||
label="タイトル"
|
||||
label={<TranslatedText namespace="shared" messageKey="labels.title"/>}
|
||||
value={title.value}
|
||||
disabled={title.disabled}
|
||||
warnings={title.warnings}
|
||||
@@ -82,7 +83,7 @@ const PostCoreDataFields: FC<PostCoreDataFieldsProps> = (
|
||||
rows={tags.rows}/>
|
||||
|
||||
<PostTextField
|
||||
label="親投稿"
|
||||
label={<TranslatedText namespace="shared" messageKey="labels.parentPosts"/>}
|
||||
value={parentPostIds.value}
|
||||
disabled={parentPostIds.disabled}
|
||||
warnings={parentPostIds.warnings}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import PostTextField from '@/components/posts/PostTextField'
|
||||
import TranslatedText from '@/components/TranslatedText'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -16,12 +17,11 @@ const PostDurationField: FC<Props> = (
|
||||
disabled },
|
||||
) => (
|
||||
<PostTextField
|
||||
label="動画時間"
|
||||
label={<TranslatedText namespace="shared" messageKey="labels.duration"/>}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
errors={errors}
|
||||
disabled={disabled}
|
||||
type="text"/>
|
||||
)
|
||||
type="text"/>)
|
||||
|
||||
export default PostDurationField
|
||||
|
||||
@@ -5,7 +5,7 @@ import { inputClass } from '@/lib/utils'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
|
||||
type Props = {
|
||||
label: string
|
||||
label: ReactNode
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
warnings?: string[]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -15,10 +16,11 @@ type Props = {
|
||||
const PostThumbnailPreview: FC<Props> = (
|
||||
{ url,
|
||||
file,
|
||||
alt = 'サムネール',
|
||||
alt,
|
||||
className = 'h-16 w-16',
|
||||
referrerPolicy },
|
||||
) => {
|
||||
const { message } = useUITranslation ('shared')
|
||||
const [failed, setFailed] = useState (false)
|
||||
const [fileUrl, setFileUrl] = useState<string | null> (null)
|
||||
|
||||
@@ -55,7 +57,7 @@ const PostThumbnailPreview: FC<Props> = (
|
||||
return (
|
||||
<img
|
||||
src={resolvedUrl}
|
||||
alt={alt}
|
||||
alt={alt ?? message ('labels.thumbnail').text}
|
||||
referrerPolicy={referrerPolicy}
|
||||
className={cn (className, 'rounded border border-border object-cover')}
|
||||
onError={() => setFailed (true)}/>)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import StatusBadge from '@/components/common/StatusBadge'
|
||||
import TranslatedText from '@/components/TranslatedText'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -8,14 +9,6 @@ import type { PostImportBadgeValue } from '@/components/posts/import/postImportR
|
||||
type Props = {
|
||||
value: PostImportBadgeValue }
|
||||
|
||||
const LABELS: Record<PostImportBadgeValue, string> = {
|
||||
ready: '登録可能',
|
||||
error: '登録不可',
|
||||
warning: '警告',
|
||||
skipped: 'スキップ',
|
||||
created: '登録済み',
|
||||
failed: '登録失敗' }
|
||||
|
||||
const TONES: Record<PostImportBadgeValue, StatusBadgeTone> = {
|
||||
ready: 'success',
|
||||
error: 'warning',
|
||||
@@ -27,7 +20,7 @@ const TONES: Record<PostImportBadgeValue, StatusBadgeTone> = {
|
||||
|
||||
const PostImportStatusBadge: FC<Props> = ({ value }) => (
|
||||
<StatusBadge tone={TONES[value]}>
|
||||
{LABELS[value]}
|
||||
<TranslatedText namespace="imports" messageKey={`status.${ value }`}/>
|
||||
</StatusBadge>)
|
||||
|
||||
export default PostImportStatusBadge
|
||||
|
||||
@@ -12,7 +12,7 @@ type Props = {
|
||||
const PostImportThumbnailPreview: FC<Props> = (
|
||||
{ url,
|
||||
file,
|
||||
alt = 'サムネール',
|
||||
alt,
|
||||
className = 'h-16 w-16' },
|
||||
) => (
|
||||
<PostThumbnailPreview
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする