'use client'; import { marked } from 'marked'; import { useEffect, useState } from 'react'; import { EmptyState } from '@/components/feedback/empty-state'; import { ErrorState } from '@/components/feedback/error-state'; import { LoadingState } from '@/components/feedback/loading-state'; import { AppCard } from '@/components/ui/app-card'; import { AppModal } from '@/components/ui/app-modal'; import { StatusBadge } from '@/components/ui/status-badge'; import type { LatestReleaseInfo, ReleaseChannel, UploadedServerBinaryInfo, } from '@/features/update/types'; import { PrimaryButton, ResourceField, ResourceInput, SecondaryButton, } from '@/features/shared/components/resource-primitives'; import { formatDateTime, formatRelativeTime } from '@/lib/utils/date'; interface VersionUpgradeModalProps { isOpen: boolean; onClose: () => void; currentVersion: string; release: LatestReleaseInfo | null | undefined; selectedChannel: ReleaseChannel; uploadedBinary: UploadedServerBinaryInfo | null; isLoading: boolean; releaseErrorMessage?: string; manualStatusMessage?: string; manualErrorMessage?: string; canUpgrade: boolean; isChecking: boolean; isUpgrading: boolean; isUploadingBinary: boolean; uploadProgress: number; isConfirmingManualUpgrade: boolean; onChannelChange: (channel: ReleaseChannel) => void; onCheck: () => void; onUpgrade: () => void; onUploadBinary: (file: File) => void; onConfirmManualUpgrade: () => void; } function getUpgradeBadge(release: LatestReleaseInfo | null | undefined) { if (!release) { return { label: '未检查', variant: 'info' as const }; } if (release.in_progress) { return { label: '升级中', variant: 'warning' as const }; } if (release.has_update) { return { label: '可升级', variant: 'warning' as const }; } return { label: '最新', variant: 'success' as const }; } function formatLogTimestamp(value: string) { const date = new Date(value); if (Number.isNaN(date.getTime())) { return '0000-00-00 00:00:00.000'; } const year = date.getFullYear(); const month = `${date.getMonth() + 1}`.padStart(2, '0'); const day = `${date.getDate()}`.padStart(2, '0'); const hour = `${date.getHours()}`.padStart(2, '0'); const minute = `${date.getMinutes()}`.padStart(2, '0'); const second = `${date.getSeconds()}`.padStart(2, '0'); const millisecond = `${date.getMilliseconds()}`.padStart(3, '0'); return `${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}`; } function formatLogLevel(level: string) { const normalized = (level || 'info').toUpperCase(); return normalized.padEnd(8, ' '); } export function VersionUpgradeModal({ isOpen, onClose, currentVersion, release, selectedChannel, uploadedBinary, isLoading, releaseErrorMessage, manualStatusMessage, manualErrorMessage, canUpgrade, isChecking, isUpgrading, isUploadingBinary, uploadProgress, isConfirmingManualUpgrade, onChannelChange, onCheck, onUpgrade, onUploadBinary, onConfirmManualUpgrade, }: VersionUpgradeModalProps) { const upgradeBadge = getUpgradeBadge(release); const [selectedBinary, setSelectedBinary] = useState(null); const uploadPhaseLabel = uploadProgress >= 100 ? '已上传,正在服务端校验版本...' : '上传中...'; const selectedChannelLabel = selectedChannel === 'preview' ? '预览版' : '正式版'; const toggleChannel = () => { onChannelChange(selectedChannel === 'preview' ? 'stable' : 'preview'); }; const canConfirmManualUpgrade = Boolean( uploadedBinary?.ready_to_upgrade && uploadedBinary.upload_token, ); const showConfirmManualUpgradeAction = canConfirmManualUpgrade; const upgradeLogs = release?.upgrade_logs ?? []; useEffect(() => { if (!isOpen) { setSelectedBinary(null); } }, [isOpen]); return (

{currentVersion || 'unknown'}

{release?.tag_name || '未检查'}

{canUpgrade ? (
) : null}
{isChecking ? '检查中...' : '检查'}
{isLoading ? : null} {!isLoading && releaseErrorMessage ? ( ) : null} {!isLoading && !releaseErrorMessage && !release ? ( ) : null} {!isLoading && !releaseErrorMessage && release ? (
{release.prerelease ? ( ) : ( )} {!release.upgrade_supported ? ( ) : null} {release.in_progress ? ( ) : null}
查看发布详情 {canUpgrade ? (
{isUpgrading ? '升级中...' : release.in_progress ? '升级中...' : selectedChannel === 'preview' ? '升级预览版' : '升级正式版'}
) : null}
) : null} {release ? ( {upgradeLogs.length > 0 ? (
{upgradeLogs.map((log, index) => (
                      
                        {formatLogTimestamp(log.created_at)}
                      
                       | 
                      
                        {formatLogLevel(log.level)}
                      
                       | 
                      
                        {log.message}
                      
                    
))}
) : ( )}
) : null} {canUpgrade ? (
{ const file = event.target.files?.[0] ?? null; setSelectedBinary(file); }} disabled={isUploadingBinary || isConfirmingManualUpgrade} />
{showConfirmManualUpgradeAction ? ( {isConfirmingManualUpgrade ? '升级中...' : '升级'} ) : ( { if (selectedBinary) { onUploadBinary(selectedBinary); } }} disabled={ !selectedBinary || isUploadingBinary || isConfirmingManualUpgrade } > {isUploadingBinary ? uploadProgress >= 100 ? '服务端校验中...' : '上传中...' : '上传并检查'} )}
{isUploadingBinary ? (
{uploadPhaseLabel} {uploadProgress}%
) : null} {manualErrorMessage ? ( ) : null} {!manualErrorMessage && manualStatusMessage ? (
{manualStatusMessage}
) : null} {uploadedBinary ? (
{!uploadedBinary.upgrade_supported ? ( ) : null}

文件名

{uploadedBinary.file_name}

上传时间

{uploadedBinary.uploaded_at ? formatDateTime(uploadedBinary.uploaded_at) : '未知'}

当前版本

{uploadedBinary.current_version}

上传版本

{uploadedBinary.detected_version}

{uploadedBinary.comparison_message}

) : ( )}
) : null}
); }