增加更明显的更新提示

This commit is contained in:
mtvpls
2025-12-05 21:12:15 +08:00
parent f8a2964264
commit cc99f4c945
5 changed files with 138 additions and 59 deletions

View File

@@ -5,6 +5,7 @@ import Link from 'next/link';
import { BackButton } from './BackButton';
import { useSite } from './SiteProvider';
import { ThemeToggle } from './ThemeToggle';
import { UpdateNotification } from './UpdateNotification';
import { UserMenu } from './UserMenu';
interface MobileHeaderProps {
@@ -45,6 +46,7 @@ const MobileHeader = ({ showBackButton = false }: MobileHeaderProps) => {
<div className='flex items-center gap-2'>
<ThemeToggle />
<UserMenu />
<UpdateNotification />
</div>
</div>

View File

@@ -3,7 +3,9 @@ import MobileBottomNav from './MobileBottomNav';
import MobileHeader from './MobileHeader';
import Sidebar from './Sidebar';
import { ThemeToggle } from './ThemeToggle';
import { UpdateNotification } from './UpdateNotification';
import { UserMenu } from './UserMenu';
import { VersionCheckProvider } from './VersionCheckProvider';
interface PageLayoutProps {
children: React.ReactNode;
@@ -12,49 +14,52 @@ interface PageLayoutProps {
const PageLayout = ({ children, activePath = '/' }: PageLayoutProps) => {
return (
<div className='w-full min-h-screen'>
{/* 移动端头部 */}
<MobileHeader showBackButton={['/play', '/live'].includes(activePath)} />
<VersionCheckProvider>
<div className='w-full min-h-screen'>
{/* 移动端头部 */}
<MobileHeader showBackButton={['/play', '/live'].includes(activePath)} />
{/* 主要布局容器 */}
<div className='flex md:grid md:grid-cols-[auto_1fr] w-full min-h-screen md:min-h-auto'>
{/* 侧边栏 - 桌面端显示,移动端隐藏 */}
<div className='hidden md:block'>
<Sidebar activePath={activePath} />
</div>
{/* 主内容区域 */}
<div className='relative min-w-0 flex-1 transition-all duration-300'>
{/* 桌面端左上角返回按钮 */}
{['/play', '/live'].includes(activePath) && (
<div className='absolute top-3 left-1 z-20 hidden md:flex'>
<BackButton />
</div>
)}
{/* 桌面端顶部按钮 */}
<div className='absolute top-2 right-4 z-20 hidden md:flex items-center gap-2'>
<ThemeToggle />
<UserMenu />
{/* 主要布局容器 */}
<div className='flex md:grid md:grid-cols-[auto_1fr] w-full min-h-screen md:min-h-auto'>
{/* 侧边栏 - 桌面端显示,移动端隐藏 */}
<div className='hidden md:block'>
<Sidebar activePath={activePath} />
</div>
{/* 主内容 */}
<main
className='flex-1 md:min-h-0 mb-14 md:mb-0 md:mt-0 mt-12'
style={{
paddingBottom: 'calc(3.5rem + env(safe-area-inset-bottom))',
}}
>
{children}
</main>
{/* 主内容区域 */}
<div className='relative min-w-0 flex-1 transition-all duration-300'>
{/* 桌面端左上角返回按钮 */}
{['/play', '/live'].includes(activePath) && (
<div className='absolute top-3 left-1 z-20 hidden md:flex'>
<BackButton />
</div>
)}
{/* 桌面端顶部按钮 */}
<div className='absolute top-2 right-4 z-20 hidden md:flex items-center gap-2'>
<ThemeToggle />
<UserMenu />
<UpdateNotification />
</div>
{/* 主内容 */}
<main
className='flex-1 md:min-h-0 mb-14 md:mb-0 md:mt-0 mt-12'
style={{
paddingBottom: 'calc(3.5rem + env(safe-area-inset-bottom))',
}}
>
{children}
</main>
</div>
</div>
{/* 移动端底部导航 */}
<div className='md:hidden'>
<MobileBottomNav activePath={activePath} />
</div>
</div>
{/* 移动端底部导航 */}
<div className='md:hidden'>
<MobileBottomNav activePath={activePath} />
</div>
</div>
</VersionCheckProvider>
);
};

View File

@@ -0,0 +1,45 @@
'use client';
import { useEffect, useState } from 'react';
import { getAuthInfoFromBrowserCookie } from '@/lib/auth';
import { UpdateStatus } from '@/lib/version_check';
import { useVersionCheck } from './VersionCheckProvider';
import { VersionPanel } from './VersionPanel';
export const UpdateNotification: React.FC = () => {
const { updateStatus, isChecking } = useVersionCheck();
const [isOwner, setIsOwner] = useState(false);
const [isVersionPanelOpen, setIsVersionPanelOpen] = useState(false);
useEffect(() => {
// 检查认证信息
const authInfo = getAuthInfoFromBrowserCookie();
setIsOwner(authInfo?.role === 'owner');
}, []);
// 检查中、不是站长或没有更新时不渲染任何内容
if (isChecking || !isOwner || updateStatus !== UpdateStatus.HAS_UPDATE) {
return null;
}
return (
<>
<button
onClick={() => setIsVersionPanelOpen(true)}
className='flex items-center px-3 py-1.5 bg-yellow-100 dark:bg-yellow-900/30 border border-yellow-300 dark:border-yellow-700 rounded-full hover:bg-yellow-200 dark:hover:bg-yellow-900/50 transition-colors cursor-pointer'
>
<span className='text-xs font-medium text-yellow-800 dark:text-yellow-300'>
</span>
</button>
{/* 版本面板 */}
<VersionPanel
isOpen={isVersionPanelOpen}
onClose={() => setIsVersionPanelOpen(false)}
/>
</>
);
};

View File

@@ -20,8 +20,9 @@ import { createPortal } from 'react-dom';
import { getAuthInfoFromBrowserCookie } from '@/lib/auth';
import { clearAllDanmakuCache } from '@/lib/danmaku/api';
import { CURRENT_VERSION } from '@/lib/version';
import { checkForUpdates, UpdateStatus } from '@/lib/version_check';
import { UpdateStatus } from '@/lib/version_check';
import { useVersionCheck } from './VersionCheckProvider';
import { VersionPanel } from './VersionPanel';
interface AuthInfo {
@@ -31,6 +32,7 @@ interface AuthInfo {
export const UserMenu: React.FC = () => {
const router = useRouter();
const { updateStatus, isChecking } = useVersionCheck();
const [isOpen, setIsOpen] = useState(false);
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false);
@@ -106,10 +108,6 @@ export const UserMenu: React.FC = () => {
const [passwordLoading, setPasswordLoading] = useState(false);
const [passwordError, setPasswordError] = useState('');
// 版本检查相关状态
const [updateStatus, setUpdateStatus] = useState<UpdateStatus | null>(null);
const [isChecking, setIsChecking] = useState(true);
// 清除弹幕缓存相关状态
const [isClearingCache, setIsClearingCache] = useState(false);
const [clearCacheMessage, setClearCacheMessage] = useState<string | null>(null);
@@ -203,22 +201,6 @@ export const UserMenu: React.FC = () => {
}
}, []);
// 版本检查
useEffect(() => {
const checkUpdate = async () => {
try {
const status = await checkForUpdates();
setUpdateStatus(status);
} catch (error) {
console.warn('版本检查失败:', error);
} finally {
setIsChecking(false);
}
};
checkUpdate();
}, []);
// 点击外部区域关闭下拉框
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {

View File

@@ -0,0 +1,45 @@
'use client';
import { createContext, useContext, useEffect, useState } from 'react';
import { checkForUpdates, UpdateStatus } from '@/lib/version_check';
interface VersionCheckContextType {
updateStatus: UpdateStatus | null;
isChecking: boolean;
}
const VersionCheckContext = createContext<VersionCheckContextType>({
updateStatus: null,
isChecking: true,
});
export const useVersionCheck = () => useContext(VersionCheckContext);
export const VersionCheckProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [updateStatus, setUpdateStatus] = useState<UpdateStatus | null>(null);
const [isChecking, setIsChecking] = useState(true);
useEffect(() => {
const checkUpdate = async () => {
try {
const status = await checkForUpdates();
setUpdateStatus(status);
} catch (error) {
console.warn('版本检查失败:', error);
} finally {
setIsChecking(false);
}
};
checkUpdate();
}, []);
return (
<VersionCheckContext.Provider value={{ updateStatus, isChecking }}>
{children}
</VersionCheckContext.Provider>
);
};