新增 /privacy、/terms 页面,提供英文与印尼文版本,覆盖 Google 登录数据、Play 内购与订阅、账号删除及印尼个人数据保护要求;关于页补充法定主体、注册地址与联系方式,页脚下沉法律入口,下载区仅保留 Google Play。
51 lines
2.0 KiB
React
51 lines
2.0 KiB
React
// Author: fruit.com.cn
|
|
import { Suspense, useState } from 'react';
|
|
import { Link, Outlet, useLocation } from 'react-router-dom';
|
|
import { useLocale, useUi } from '../app/providers/AppProviders';
|
|
import { Sidebar } from './components/Sidebar';
|
|
import { TopBar } from './components/TopBar';
|
|
import { UploadVideoDialog } from './components/UploadVideoDialog';
|
|
|
|
const FOOTER_LINKS = {
|
|
id: { privacy: 'Privasi', terms: 'Ketentuan', about: 'Tentang YMB' },
|
|
en: { privacy: 'Privacy', terms: 'Terms', about: 'About YMB' },
|
|
zh: { privacy: '隐私政策', terms: '服务条款', about: '关于 YMB' },
|
|
};
|
|
|
|
export function AppShell() {
|
|
const { locale } = useLocale();
|
|
const { sidebarOpen, setSidebarOpen } = useUi();
|
|
const location = useLocation();
|
|
const [uploadOpen, setUploadOpen] = useState(false);
|
|
const immersive = location.pathname === '/shorts';
|
|
const links = FOOTER_LINKS[locale.code] || FOOTER_LINKS.en;
|
|
|
|
return (
|
|
<>
|
|
<TopBar onToggleSidebar={() => setSidebarOpen(!sidebarOpen)} onUpload={() => setUploadOpen(true)} />
|
|
<Sidebar />
|
|
<main className={`content ${sidebarOpen ? '' : 'content-wide'}`}>
|
|
<Suspense fallback={<div className="route-loading" aria-busy="true" />}>
|
|
<Outlet />
|
|
</Suspense>
|
|
{!immersive && (
|
|
<footer className="site-footer">
|
|
<div>© {new Date().getFullYear()} {locale.copyright}</div>
|
|
<nav className="site-footer-links" aria-label="Legal and company information">
|
|
<Link to="/privacy">{links.privacy}</Link>
|
|
<span aria-hidden="true">·</span>
|
|
<Link to="/terms">{links.terms}</Link>
|
|
<span aria-hidden="true">·</span>
|
|
<Link to="/about">{links.about}</Link>
|
|
</nav>
|
|
</footer>
|
|
)}
|
|
</main>
|
|
{sidebarOpen && (
|
|
<button className="scrim mobile-only" onClick={() => setSidebarOpen(false)} aria-label="Close" type="button" />
|
|
)}
|
|
<UploadVideoDialog open={uploadOpen} onClose={() => setUploadOpen(false)} />
|
|
</>
|
|
);
|
|
}
|