Files
new-api/web/default/src/features/about/index.tsx
T

172 lines
5.3 KiB
TypeScript
Vendored

/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useQuery } from '@tanstack/react-query'
import { Construction } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { PublicLayout } from '@/components/layout'
import { RichContent } from '@/components/rich-content'
import { Skeleton } from '@/components/ui/skeleton'
import { isHttpUrl, isLikelyHtml } from '@/lib/content-format'
import { getAboutContent } from './api'
function EmptyAboutState() {
const { t } = useTranslation()
const currentYear = new Date().getFullYear()
return (
<div className='flex min-h-[60vh] items-center justify-center p-8'>
<div className='max-w-2xl space-y-6 text-center'>
<div className='flex justify-center'>
<Construction className='text-muted-foreground h-24 w-24' />
</div>
<div className='space-y-2'>
<h2 className='text-2xl font-bold'>{t('No About Content Set')}</h2>
<p className='text-muted-foreground'>
{t(
'The administrator has not configured any about content yet. You can set it in the settings page, supporting HTML or URL.'
)}
</p>
</div>
<div className='space-y-4 text-sm'>
<p>
{t('New API Project Repository:')}{' '}
<a
href='https://github.com/QuantumNous/new-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('https://github.com/QuantumNous/new-api')}
</a>
</p>
<p className='text-muted-foreground'>
<a
href='https://github.com/QuantumNous/new-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('NewAPI')}
</a>{' '}
© {currentYear}{' '}
<a
href='https://github.com/QuantumNous'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('QuantumNous')}
</a>{' '}
{t('| Based on')}{' '}
<a
href='https://github.com/songquanpeng/one-api'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('One API')}
</a>{' '}
© 2023{' '}
<a
href='https://github.com/songquanpeng'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('JustSong')}
</a>
</p>
<p className='text-muted-foreground'>
{t('This project must be used in compliance with the')}{' '}
<a
href='https://github.com/QuantumNous/new-api/blob/main/LICENSE'
target='_blank'
rel='noopener noreferrer'
className='text-primary hover:underline'
>
{t('AGPL v3.0 License')}
</a>
.
</p>
</div>
</div>
</div>
)
}
export function About() {
const { t } = useTranslation()
const { data, isLoading } = useQuery({
queryKey: ['about-content'],
queryFn: getAboutContent,
})
const rawContent = data?.data?.trim() ?? ''
const hasContent = rawContent.length > 0
const isUrl = hasContent && isHttpUrl(rawContent)
if (isLoading) {
return (
<PublicLayout>
<div className='mx-auto flex max-w-4xl flex-col gap-4 py-12'>
<Skeleton className='h-8 w-[45%]' />
<Skeleton className='h-4 w-full' />
<Skeleton className='h-4 w-[90%]' />
<Skeleton className='h-4 w-[80%]' />
</div>
</PublicLayout>
)
}
if (!hasContent) {
return (
<PublicLayout>
<EmptyAboutState />
</PublicLayout>
)
}
if (isUrl) {
return (
<PublicLayout showMainContainer={false}>
<iframe
src={rawContent}
className='h-[calc(100vh-3.5rem)] w-full border-0'
title={t('About')}
sandbox='allow-forms allow-popups allow-popups-to-escape-sandbox allow-scripts'
/>
</PublicLayout>
)
}
return (
<PublicLayout>
<div className='mx-auto max-w-6xl px-4 py-8'>
<RichContent
mode={isLikelyHtml(rawContent) ? 'html' : 'markdown'}
content={rawContent}
className='prose-neutral dark:prose-invert max-w-none'
/>
</div>
</PublicLayout>
)
}