위젯 추가 팝업 수정

This commit is contained in:
Macbook
2026-06-15 17:17:06 +09:00
parent 271392bc49
commit 51fdcf40e5
16 changed files with 435 additions and 238 deletions
+55 -57
View File
@@ -1,6 +1,8 @@
import React, { useEffect, useState } from 'react';
import DesktopUpdateModal from '@frontend/components/DesktopUpdateModal';
import {
runStartupAutoUpdate,
checkForUpdates,
getAppVersion,
type DesktopUpdatePhase,
type DesktopUpdateProgress,
} from './bridge/updater';
@@ -27,9 +29,8 @@ function formatBytes(n?: number): string {
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 isActive = phase === 'checking';
const showBar = false;
const panelClass = phase === 'error'
? 'desktop-update-progress--failed'
@@ -54,9 +55,6 @@ function StartupUpdateSplash({ progress }: { progress: DesktopUpdateProgress | n
<h1 className="desktop-startup-update-title">GoldenChart</h1>
<p className="desktop-startup-update-sub">
{progress?.currentVersion ? `현재 v${progress.currentVersion}` : '시작 중…'}
{progress?.newVersion && progress.newVersion !== progress.currentVersion && (
<> v{progress.newVersion}</>
)}
</p>
<div className={`desktop-update-progress ${panelClass}`.trim()}>
@@ -65,27 +63,11 @@ function StartupUpdateSplash({ progress }: { progress: DesktopUpdateProgress | n
{isActive && <span className="desktop-update-spinner" aria-hidden />}
{PHASE_LABEL[phase]}
</span>
{phase === 'downloading' && progress?.downloadPercent != null && (
<span className="desktop-update-pct">{progress.downloadPercent}%</span>
)}
</div>
{showBar && (
<div
className="desktop-update-bar"
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={barPct ?? undefined}
>
{barPct != null ? (
<div
className="desktop-update-bar-fill desktop-update-bar-fill--determinate"
style={{ width: `${barPct}%` }}
/>
) : (
<div className="desktop-update-bar-fill desktop-update-bar-fill--indeterminate" />
)}
<div className="desktop-update-bar" role="progressbar" aria-valuemin={0} aria-valuemax={100}>
<div className="desktop-update-bar-fill desktop-update-bar-fill--indeterminate" />
</div>
)}
@@ -94,10 +76,7 @@ function StartupUpdateSplash({ progress }: { progress: DesktopUpdateProgress | n
<span className="desktop-update-step-dot" aria-hidden />
</li>
<li className={`desktop-update-step${
phase === 'available' || phase === 'downloading' ? ' active'
: ['installing', 'relaunching', 'uptodate'].includes(phase) ? ' done' : ''
}`}>
<li className="desktop-update-step">
<span className="desktop-update-step-dot" aria-hidden />
{progress?.downloadedBytes && progress.totalBytes ? (
@@ -106,9 +85,7 @@ function StartupUpdateSplash({ progress }: { progress: DesktopUpdateProgress | n
</span>
) : null}
</li>
<li className={`desktop-update-step${
phase === 'installing' ? ' active' : phase === 'relaunching' || phase === 'uptodate' ? ' done' : ''
}`}>
<li className="desktop-update-step">
<span className="desktop-update-step-dot" aria-hidden />
</li>
@@ -125,44 +102,56 @@ function StartupUpdateSplash({ progress }: { progress: DesktopUpdateProgress | n
);
}
/** StrictMode 이중 실행 방지 — 시작 업데이트는 한 번만 */
let startupUpdateTask: ReturnType<typeof runStartupAutoUpdate> | 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(메인 앱) 렌더 */
/** 시작 시 버전 확인 후 children(메인 앱) 렌더 — 새 버전 있으면 업데이트 모달 표시 */
export function StartupUpdateGate({ children }: { children: React.ReactNode }) {
const [ready, setReady] = useState(false);
const [startupUpdateOpen, setStartupUpdateOpen] = useState(false);
const [progress, setProgress] = useState<DesktopUpdateProgress | null>({
phase: 'checking',
message: '서버에서 새 버전을 확인하는 중…',
});
useEffect(() => {
const listener = (p: DesktopUpdateProgress) => setProgress(p);
startupProgressListeners.add(listener);
let cancelled = false;
void getStartupUpdateTask().then(({ shouldContinue, result }) => {
if (!shouldContinue) return;
if (result.phase === 'error') {
console.warn('[startup-update]', result.message);
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 () => {
startupProgressListeners.delete(listener);
cancelled = true;
};
}, []);
@@ -170,5 +159,14 @@ export function StartupUpdateGate({ children }: { children: React.ReactNode }) {
return <StartupUpdateSplash progress={progress} />;
}
return <>{children}</>;
return (
<>
{children}
<DesktopUpdateModal
open={startupUpdateOpen}
onClose={() => setStartupUpdateOpen(false)}
autoStart
/>
</>
);
}