new-api / web /src /components /layout /PageLayout.jsx
liuzhao521
Deploy New API v0.9.25+ (commit b47cf4ef) to HuggingFace Spaces
4674012
/*
Copyright (C) 2025 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 HeaderBar from './headerbar';
import { Layout } from '@douyinfe/semi-ui';
import SiderBar from './SiderBar';
import App from '../../App';
import FooterBar from './Footer';
import { ToastContainer } from 'react-toastify';
import React, { useContext, useEffect, useState } from 'react';
import { useIsMobile } from '../../hooks/common/useIsMobile';
import { useSidebarCollapsed } from '../../hooks/common/useSidebarCollapsed';
import { useTranslation } from 'react-i18next';
import {
API,
getLogo,
getSystemName,
showError,
setStatusData,
} from '../../helpers';
import { UserContext } from '../../context/User';
import { StatusContext } from '../../context/Status';
import { useLocation } from 'react-router-dom';
const { Sider, Content, Header } = Layout;
const PageLayout = () => {
const [, userDispatch] = useContext(UserContext);
const [, statusDispatch] = useContext(StatusContext);
const isMobile = useIsMobile();
const [collapsed, , setCollapsed] = useSidebarCollapsed();
const [drawerOpen, setDrawerOpen] = useState(false);
const { i18n } = useTranslation();
const location = useLocation();
const cardProPages = [
'/console/channel',
'/console/log',
'/console/redemption',
'/console/user',
'/console/token',
'/console/midjourney',
'/console/task',
'/console/models',
'/pricing',
];
const shouldHideFooter = cardProPages.includes(location.pathname);
const shouldInnerPadding =
location.pathname.includes('/console') &&
!location.pathname.startsWith('/console/chat') &&
location.pathname !== '/console/playground';
const isConsoleRoute = location.pathname.startsWith('/console');
const showSider = isConsoleRoute && (!isMobile || drawerOpen);
useEffect(() => {
if (isMobile && drawerOpen && collapsed) {
setCollapsed(false);
}
}, [isMobile, drawerOpen, collapsed, setCollapsed]);
const loadUser = () => {
let user = localStorage.getItem('user');
if (user) {
let data = JSON.parse(user);
userDispatch({ type: 'login', payload: data });
}
};
const loadStatus = async () => {
try {
const res = await API.get('/api/status');
const { success, data } = res.data;
if (success) {
statusDispatch({ type: 'set', payload: data });
setStatusData(data);
} else {
showError('Unable to connect to server');
}
} catch (error) {
showError('Failed to load status');
}
};
useEffect(() => {
loadUser();
loadStatus().catch(console.error);
let systemName = getSystemName();
if (systemName) {
document.title = systemName;
}
let logo = getLogo();
if (logo) {
let linkElement = document.querySelector("link[rel~='icon']");
if (linkElement) {
linkElement.href = logo;
}
}
const savedLang = localStorage.getItem('i18nextLng');
if (savedLang) {
i18n.changeLanguage(savedLang);
}
}, [i18n]);
return (
<Layout
style={{
height: '100vh',
display: 'flex',
flexDirection: 'column',
overflow: isMobile ? 'visible' : 'hidden',
}}
>
<Header
style={{
padding: 0,
height: 'auto',
lineHeight: 'normal',
position: 'fixed',
width: '100%',
top: 0,
zIndex: 100,
}}
>
<HeaderBar
onMobileMenuToggle={() => setDrawerOpen((prev) => !prev)}
drawerOpen={drawerOpen}
/>
</Header>
<Layout
style={{
overflow: isMobile ? 'visible' : 'auto',
display: 'flex',
flexDirection: 'column',
}}
>
{showSider && (
<Sider
style={{
position: 'fixed',
left: 0,
top: '64px',
zIndex: 99,
border: 'none',
paddingRight: '0',
height: 'calc(100vh - 64px)',
width: 'var(--sidebar-current-width)',
}}
>
<SiderBar
onNavigate={() => {
if (isMobile) setDrawerOpen(false);
}}
/>
</Sider>
)}
<Layout
style={{
marginLeft: isMobile
? '0'
: showSider
? 'var(--sidebar-current-width)'
: '0',
flex: '1 1 auto',
display: 'flex',
flexDirection: 'column',
}}
>
<Content
style={{
flex: '1 0 auto',
overflowY: isMobile ? 'visible' : 'hidden',
WebkitOverflowScrolling: 'touch',
padding: shouldInnerPadding ? (isMobile ? '5px' : '24px') : '0',
position: 'relative',
}}
>
<App />
</Content>
{!shouldHideFooter && (
<Layout.Footer
style={{
flex: '0 0 auto',
width: '100%',
}}
>
<FooterBar />
</Layout.Footer>
)}
</Layout>
</Layout>
<ToastContainer />
</Layout>
);
};
export default PageLayout;