/* eslint-disable no-console,react-hooks/exhaustive-deps */ 'use client'; import { Bug, CheckCircle, ChevronDown, ChevronUp, Download, Plus, RefreshCw, X, } from 'lucide-react'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import { changelog, ChangelogEntry } from '@/lib/changelog'; import { compareVersions, CURRENT_VERSION, UpdateStatus } from '@/lib/version'; interface VersionPanelProps { isOpen: boolean; onClose: () => void; } interface RemoteChangelogEntry { version: string; date: string; added: string[]; changed: string[]; fixed: string[]; } export const VersionPanel: React.FC = ({ isOpen, onClose, }) => { const [mounted, setMounted] = useState(false); const [remoteChangelog, setRemoteChangelog] = useState([]); const [hasUpdate, setIsHasUpdate] = useState(false); const [latestVersion, setLatestVersion] = useState(''); const [showRemoteContent, setShowRemoteContent] = useState(false); // 确保组件已挂载 useEffect(() => { setMounted(true); return () => setMounted(false); }, []); // 获取远程变更日志 useEffect(() => { if (isOpen) { fetchRemoteChangelog(); } }, [isOpen]); // 获取远程变更日志 const fetchRemoteChangelog = async () => { try { const response = await fetch( 'https://codeberg.org/LunaTechLab/MoonTV/raw/branch/main/CHANGELOG' ); if (response.ok) { const content = await response.text(); const parsed = parseChangelog(content); setRemoteChangelog(parsed); // 检查是否有更新 if (parsed.length > 0) { const latest = parsed[0]; setLatestVersion(latest.version); setIsHasUpdate( compareVersions(latest.version) === UpdateStatus.HAS_UPDATE ); } } else { console.error( '获取远程变更日志失败:', response.status, response.statusText ); } } catch (error) { console.error('获取远程变更日志失败:', error); } }; // 解析变更日志格式 const parseChangelog = (content: string): RemoteChangelogEntry[] => { const lines = content.split('\n'); const versions: RemoteChangelogEntry[] = []; let currentVersion: RemoteChangelogEntry | null = null; let currentSection: string | null = null; let inVersionContent = false; for (const line of lines) { const trimmedLine = line.trim(); // 匹配版本行: ## [X.Y.Z] - YYYY-MM-DD const versionMatch = trimmedLine.match( /^## \[([\d.]+)\] - (\d{4}-\d{2}-\d{2})$/ ); if (versionMatch) { if (currentVersion) { versions.push(currentVersion); } currentVersion = { version: versionMatch[1], date: versionMatch[2], added: [], changed: [], fixed: [], }; currentSection = null; inVersionContent = true; continue; } // 如果遇到下一个版本或到达文件末尾,停止处理当前版本 if (inVersionContent && currentVersion) { // 匹配章节标题 if (trimmedLine === '### Added') { currentSection = 'added'; continue; } else if (trimmedLine === '### Changed') { currentSection = 'changed'; continue; } else if (trimmedLine === '### Fixed') { currentSection = 'fixed'; continue; } // 匹配条目: - 内容 if (trimmedLine.startsWith('- ') && currentSection) { const entry = trimmedLine.substring(2); if (currentSection === 'added') { currentVersion.added.push(entry); } else if (currentSection === 'changed') { currentVersion.changed.push(entry); } else if (currentSection === 'fixed') { currentVersion.fixed.push(entry); } } } } // 添加最后一个版本 if (currentVersion) { versions.push(currentVersion); } return versions; }; // 渲染变更日志条目 const renderChangelogEntry = ( entry: ChangelogEntry | RemoteChangelogEntry, isCurrentVersion = false, isRemote = false ) => { const isUpdate = isRemote && hasUpdate && entry.version === latestVersion; return (
{/* 版本标题 */}

v{entry.version}

{isCurrentVersion && ( 当前版本 )} {isUpdate && ( 可更新 )}
{entry.date}
{/* 变更内容 */}
{entry.added.length > 0 && (
新增功能
    {entry.added.map((item, index) => (
  • {item}
  • ))}
)} {entry.changed.length > 0 && (
功能改进
    {entry.changed.map((item, index) => (
  • {item}
  • ))}
)} {entry.fixed.length > 0 && (
问题修复
    {entry.fixed.map((item, index) => (
  • {item}
  • ))}
)}
); }; // 版本面板内容 const versionPanelContent = ( <> {/* 背景遮罩 */}
{/* 版本面板 */}
{/* 标题栏 */}

版本信息

v{CURRENT_VERSION} {hasUpdate && ( 有新版本可用 可更新 )}
{/* 内容区域 */}
{/* 远程更新信息 */} {hasUpdate && (

发现新版本

v{CURRENT_VERSION} → v{latestVersion}

前往仓库
)} {/* 当前为最新版本信息 */} {!hasUpdate && (

当前为最新版本

已是最新版本 v{CURRENT_VERSION}

前往仓库
)} {/* 远程可更新内容 */} {hasUpdate && (

远程更新内容

{showRemoteContent && remoteChangelog.length > 0 && (
{remoteChangelog .filter((entry) => { // 找到第一个本地版本,过滤掉本地已有的版本 const localVersions = changelog.map( (local) => local.version ); return !localVersions.includes(entry.version); }) .map((entry, index) => (

v{entry.version}

{entry.version === latestVersion && ( 远程最新 )}
{entry.date}
{entry.added && entry.added.length > 0 && (
新增功能
    {entry.added.map((item, itemIndex) => (
  • {item}
  • ))}
)} {entry.changed && entry.changed.length > 0 && (
功能改进
    {entry.changed.map((item, itemIndex) => (
  • {item}
  • ))}
)} {entry.fixed && entry.fixed.length > 0 && (
问题修复
    {entry.fixed.map((item, itemIndex) => (
  • {item}
  • ))}
)}
))}
)}
)} {/* 变更日志标题 */}

变更日志

{/* 本地变更日志 */} {changelog.map((entry) => renderChangelogEntry( entry, entry.version === CURRENT_VERSION, false ) )}
); // 使用 Portal 渲染到 document.body if (!mounted || !isOpen) return null; return createPortal(versionPanelContent, document.body); };