mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-24 04:04:43 +08:00
feat(公告): 添加网站公告功能及弹窗提示
- 从环境变量读取公告内容,提供默认值 - 扩展SiteProvider组件以支持公告功能 - 在首页添加公告弹窗组件,支持本地存储记录用户已读状态 - 弹窗包含关闭功能和完善的样式交互
This commit is contained in:
@@ -29,6 +29,9 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const siteName = process.env.SITE_NAME || 'MoonTV';
|
||||
const announcement =
|
||||
process.env.ANNOUNCEMENT ||
|
||||
'本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。';
|
||||
|
||||
return (
|
||||
<html lang='zh-CN' suppressHydrationWarning>
|
||||
@@ -41,7 +44,7 @@ export default function RootLayout({
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SiteProvider siteName={siteName}>
|
||||
<SiteProvider siteName={siteName} announcement={announcement}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
</SiteProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -13,6 +13,7 @@ import ContinueWatching from '@/components/ContinueWatching';
|
||||
import DemoCard from '@/components/DemoCard';
|
||||
import PageLayout from '@/components/PageLayout';
|
||||
import ScrollableRow from '@/components/ScrollableRow';
|
||||
import { useSite } from '@/components/SiteProvider';
|
||||
import VideoCard from '@/components/VideoCard';
|
||||
|
||||
function HomeClient() {
|
||||
@@ -20,6 +21,16 @@ function HomeClient() {
|
||||
const [hotMovies, setHotMovies] = useState<DoubanItem[]>([]);
|
||||
const [hotTvShows, setHotTvShows] = useState<DoubanItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { announcement } = useSite();
|
||||
|
||||
const [showAnnouncement, setShowAnnouncement] = useState(() => {
|
||||
// 检查本地存储中是否已记录弹窗显示状态
|
||||
const hasSeenAnnouncement = localStorage.getItem('hasSeenAnnouncement');
|
||||
if (hasSeenAnnouncement !== announcement) {
|
||||
return true;
|
||||
}
|
||||
return !hasSeenAnnouncement && announcement; // 未记录且有公告时显示弹窗
|
||||
});
|
||||
|
||||
// 收藏夹数据
|
||||
type FavoriteItem = {
|
||||
@@ -88,6 +99,11 @@ function HomeClient() {
|
||||
})();
|
||||
}, [activeTab]);
|
||||
|
||||
const handleCloseAnnouncement = (announcement: string) => {
|
||||
setShowAnnouncement(false);
|
||||
localStorage.setItem('hasSeenAnnouncement', announcement); // 记录已查看弹窗
|
||||
};
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
<div className='px-2 sm:px-10 py-4 sm:py-8 overflow-visible'>
|
||||
@@ -233,6 +249,40 @@ function HomeClient() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{announcement && showAnnouncement && (
|
||||
<div
|
||||
className={`fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm dark:bg-black/70 p-4 transition-opacity duration-300 ${
|
||||
showAnnouncement ? '' : 'opacity-0 pointer-events-none'
|
||||
}`}
|
||||
>
|
||||
<div className='w-full max-w-md rounded-xl bg-white p-6 shadow-xl dark:bg-gray-900 transform transition-all duration-300 hover:shadow-2xl'>
|
||||
<div className='flex justify-between items-start mb-4'>
|
||||
<h3 className='text-2xl font-bold tracking-tight text-gray-800 dark:text-white border-b border-green-500 pb-1'>
|
||||
提示
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => handleCloseAnnouncement(announcement)}
|
||||
className='text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-white transition-colors'
|
||||
aria-label='关闭'
|
||||
></button>
|
||||
</div>
|
||||
<div className='mb-6'>
|
||||
<div className='relative overflow-hidden rounded-lg mb-4 bg-green-50 dark:bg-green-900/20'>
|
||||
<div className='absolute inset-y-0 left-0 w-1.5 bg-green-500 dark:bg-green-400'></div>
|
||||
<p className='ml-4 text-gray-600 dark:text-gray-300 leading-relaxed'>
|
||||
{announcement}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleCloseAnnouncement(announcement)}
|
||||
className='w-full rounded-lg bg-gradient-to-r from-green-600 to-green-700 px-4 py-3 text-white font-medium shadow-md hover:shadow-lg hover:from-green-700 hover:to-green-800 dark:from-green-600 dark:to-green-700 dark:hover:from-green-700 dark:hover:to-green-800 transition-all duration-300 transform hover:-translate-y-0.5'
|
||||
>
|
||||
我知道了
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
import { createContext, ReactNode, useContext } from 'react';
|
||||
|
||||
const SiteContext = createContext<{ siteName: string }>({
|
||||
siteName: 'MoonTV', // Default value
|
||||
const SiteContext = createContext<{ siteName: string; announcement?: string }>({
|
||||
// 默认值
|
||||
siteName: 'MoonTV',
|
||||
announcement:
|
||||
'本网站仅提供影视信息搜索服务,所有内容均来自第三方网站。本站不存储任何视频资源,不对任何内容的准确性、合法性、完整性负责。',
|
||||
});
|
||||
|
||||
export const useSite = () => useContext(SiteContext);
|
||||
@@ -11,11 +14,15 @@ export const useSite = () => useContext(SiteContext);
|
||||
export function SiteProvider({
|
||||
children,
|
||||
siteName,
|
||||
announcement,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
siteName: string;
|
||||
announcement?: string;
|
||||
}) {
|
||||
return (
|
||||
<SiteContext.Provider value={{ siteName }}>{children}</SiteContext.Provider>
|
||||
<SiteContext.Provider value={{ siteName, announcement }}>
|
||||
{children}
|
||||
</SiteContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user