import React, { useEffect, useState } from 'react'; import DesktopUpdateModal from '@frontend/components/DesktopUpdateModal'; import { checkForUpdates, getAppVersion, type DesktopUpdatePhase, type DesktopUpdateProgress, } from './bridge/updater'; import { scheduleMainWindowTitleSync } from './setMainWindowTitle'; import { PlatformBrandIcon, type PlatformBrandIconId } from '@frontend/components/icons/PlatformBrandIcons'; import { isMacDesktop, isWindowsDesktop } from '@frontend/utils/platform'; 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'; const showBar = false; const panelClass = phase === 'error' ? 'desktop-update-progress--failed' : isActive ? 'desktop-update-progress--active' : ''; const platformBrand: PlatformBrandIconId | null = isMacDesktop() ? 'mac' : isWindowsDesktop() ? 'windows' : null; return (
{platformBrand && (
)}

GoldenChart

{progress?.currentVersion ? `현재 v${progress.currentVersion}` : '시작 중…'}

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

{progress.message}

)}
); } /** 시작 시 버전 확인 후 children(메인 앱) 렌더 — 새 버전 있으면 업데이트 모달 표시 */ export function StartupUpdateGate({ children }: { children: React.ReactNode }) { const [ready, setReady] = useState(false); const [startupUpdateOpen, setStartupUpdateOpen] = useState(false); const [progress, setProgress] = useState({ phase: 'checking', message: '서버에서 새 버전을 확인하는 중…', }); useEffect(() => { let cancelled = false; void (async () => { let currentVersion = '—'; try { currentVersion = await getAppVersion(); } catch { /* ignore */ } if (cancelled) return; setProgress({ phase: 'checking', message: '서버에서 새 버전을 확인하는 중…', currentVersion, }); const result = await checkForUpdates(); if (cancelled) return; if (result.available) { setProgress({ phase: 'available', message: result.message ?? `새 버전 v${result.version}을(를) 사용할 수 있습니다.`, currentVersion, newVersion: result.version, }); } else if (result.message?.includes('실패') || result.message?.includes('오류')) { setProgress({ phase: 'error', message: result.message, currentVersion }); } scheduleMainWindowTitleSync(); setReady(true); if (result.available) { setStartupUpdateOpen(true); } })(); return () => { cancelled = true; }; }, []); if (!ready) { return ; } return ( <> {children} setStartupUpdateOpen(false)} autoStart /> ); }