import React, { useEffect, useState } from 'react'; import { runStartupAutoUpdate, type DesktopUpdatePhase, type DesktopUpdateProgress, } from './bridge/updater'; import '@frontend/App.css'; const PHASE_LABEL: Record = { checking: '업데이트 확인 중', uptodate: '최신 버전', available: '새 버전 발견', downloading: '다운로드 중', installing: '설치 중', relaunching: '재시작 중', error: '업데이트 실패', }; function formatBytes(n?: number): string { if (n == null || !Number.isFinite(n) || n <= 0) return ''; if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`; return `${(n / (1024 * 1024)).toFixed(1)} MB`; } function StartupUpdateSplash({ progress }: { progress: DesktopUpdateProgress | null }) { const phase = progress?.phase ?? 'checking'; const isActive = phase === 'checking' || phase === 'downloading' || phase === 'installing' || phase === 'relaunching'; const showBar = phase === 'available' || phase === 'downloading' || phase === 'installing'; const barPct = phase === 'installing' ? 100 : progress?.downloadPercent; const panelClass = phase === 'error' ? 'desktop-update-progress--failed' : isActive ? 'desktop-update-progress--active' : ''; return (

GoldenChart

{progress?.currentVersion ? `현재 v${progress.currentVersion}` : '시작 중…'} {progress?.newVersion && progress.newVersion !== progress.currentVersion && ( <> → v{progress.newVersion} )}

{isActive && } {PHASE_LABEL[phase]} {phase === 'downloading' && progress?.downloadPercent != null && ( {progress.downloadPercent}% )}
{showBar && (
{barPct != null ? (
) : (
)}
)}
  • 서버에서 버전 확인
  • 업데이트 다운로드 {progress?.downloadedBytes && progress.totalBytes ? ( {formatBytes(progress.downloadedBytes)} / {formatBytes(progress.totalBytes)} ) : null}
  • 설치 및 재시작
{progress?.message && (

{progress.message}

)}
); } /** StrictMode 이중 실행 방지 — 시작 업데이트는 한 번만 */ let startupUpdateTask: ReturnType | null = null; const startupProgressListeners = new Set<(p: DesktopUpdateProgress) => void>(); function emitStartupProgress(p: DesktopUpdateProgress) { for (const listener of startupProgressListeners) listener(p); } function getStartupUpdateTask() { if (!startupUpdateTask) { startupUpdateTask = runStartupAutoUpdate(emitStartupProgress); } return startupUpdateTask; } /** 시작 시 자동 업데이트 후 children(메인 앱) 렌더 */ export function StartupUpdateGate({ children }: { children: React.ReactNode }) { const [ready, setReady] = useState(false); const [progress, setProgress] = useState({ phase: 'checking', message: '서버에서 새 버전을 확인하는 중…', }); useEffect(() => { const listener = (p: DesktopUpdateProgress) => setProgress(p); startupProgressListeners.add(listener); void getStartupUpdateTask().then(({ shouldContinue, result }) => { if (!shouldContinue) return; if (result.phase === 'error') { console.warn('[startup-update]', result.message); } setReady(true); }); return () => { startupProgressListeners.delete(listener); }; }, []); if (!ready) { return ; } return <>{children}; }