diff --git a/classes/plugin/SafeUpgradeManager.php b/classes/plugin/SafeUpgradeManager.php index 5131618a..1cb3f373 100644 --- a/classes/plugin/SafeUpgradeManager.php +++ b/classes/plugin/SafeUpgradeManager.php @@ -606,9 +606,8 @@ class SafeUpgradeManager try { $file = $this->download($package, $timeout); - $this->setProgress('installing', 'Installing update...', 80); $this->performInstall($file); - $this->setProgress('installing', 'Preparing promotion...', 92); + $this->setProgress('installing', 'Preparing promotion...', null); } catch (Throwable $e) { $this->setProgress('error', $e->getMessage(), null); @@ -620,14 +619,14 @@ class SafeUpgradeManager $this->tmp = null; } - $this->setProgress('finalizing', 'Finalizing upgrade...', 95); + $this->setProgress('finalizing', 'Finalizing upgrade...', null); $safeUpgrade->clearRecoveryFlag(); if ($this->recovery && method_exists($this->recovery, 'closeUpgradeWindow')) { $this->recovery->closeUpgradeWindow(); } $this->ensureExecutablePermissions(); - $this->setProgress('finalizing', 'Finalizing upgrade...', 98); + $this->setProgress('finalizing', 'Finalizing upgrade...', null); $manifest = $this->resolveLatestManifest(); @@ -853,7 +852,7 @@ class SafeUpgradeManager */ protected function performInstall(string $zip): void { - $this->setProgress('installing', 'Unpacking archive...', 82); + $this->setProgress('installing', 'Unpacking update...', null); $folder = Installer::unZip($zip, $this->tmp . '/zip'); if ($folder === false) { throw new RuntimeException(Installer::lastErrorMsg()); @@ -870,9 +869,9 @@ class SafeUpgradeManager } try { - $this->setProgress('installing', 'Running installer...', 85); + $this->setProgress('installing', 'Running installer...', null); $install($zip); - $this->setProgress('installing', 'Verifying files...', 88); + $this->setProgress('installing', 'Verifying files...', null); } catch (Throwable $e) { throw new RuntimeException($e->getMessage(), 0, $e); } diff --git a/themes/grav/app/updates/safe-upgrade.js b/themes/grav/app/updates/safe-upgrade.js index 646e6eb0..4aa01544 100644 --- a/themes/grav/app/updates/safe-upgrade.js +++ b/themes/grav/app/updates/safe-upgrade.js @@ -56,6 +56,8 @@ export default class SafeUpgrade { this.statusFailures = 0; this.statusContext = null; this.statusIdleCount = 0; + this.currentStage = null; + this.stageEnteredAt = 0; this.directStatusUrl = this.resolveDirectStatusUrl(); this.preferDirectStatus = !!this.directStatusUrl; @@ -124,6 +126,8 @@ export default class SafeUpgrade { this.preferDirectStatus = !!this.directStatusUrl; this.statusContext = null; this.statusIdleCount = 0; + this.currentStage = null; + this.stageEnteredAt = 0; this.renderLoading(); this.modal.open(); this.fetchPreflight(); @@ -639,6 +643,11 @@ export default class SafeUpgrade { } const stage = data.stage || 'initializing'; + if (stage !== this.currentStage) { + this.currentStage = stage; + this.stageEnteredAt = Date.now(); + } + const titleResolver = STAGE_TITLES[stage] || STAGE_TITLES.initializing; const title = titleResolver(); let percent = typeof data.percent === 'number' ? data.percent : null; @@ -648,12 +657,16 @@ export default class SafeUpgrade { if (stage === 'initializing') { return percent !== null ? Math.min(percent, 5) : 5; } if (stage === 'downloading') { if (percent !== null) { - return Math.min(60, Math.round(10 + (percent * 0.5))); + return Math.min(20, Math.max(5, Math.round(percent * 0.2))); } - return 25; + return 12; + } + if (stage === 'installing') { + return this.computeSmoothPercent(20, 90, 28, percent); + } + if (stage === 'finalizing') { + return this.computeSmoothPercent(90, 99, 6, percent); } - if (stage === 'installing') { return percent !== null ? Math.min(Math.max(percent, 80), 94) : 80; } - if (stage === 'finalizing') { return percent !== null ? Math.min(Math.max(percent, 95), 99) : 95; } if (stage === 'complete') { return 100; } if (stage === 'error') { return null; } return percent; @@ -665,11 +678,14 @@ export default class SafeUpgrade { const statusLine = job && job.status ? `
${t('SAFE_UPGRADE_JOB_STATUS', 'Status')}: ${job.status.toUpperCase()}${job.error ? ` — ${job.error}` : ''}
` : ''; const animateBar = stage !== 'complete' && stage !== 'error' && percent !== null; const barClass = `safe-upgrade-progress-bar${animateBar ? ' is-active' : ''}`; + const detailMessage = stage === 'error' + ? `${data.message || ''}
` + : (data.message && stage !== 'installing' && stage !== 'finalizing' ? `${data.message}
` : ''); this.steps.progress.html(`${data.message || ''}
+ ${detailMessage} ${statusLine} ${percentLabel ? `".concat(t('SAFE_UPGRADE_JOB_STATUS', 'Status'), ": ").concat(job.status.toUpperCase(), "").concat(job.error ? " — ".concat(job.error) : '', "
") : ''; var animateBar = stage !== 'complete' && stage !== 'error' && percent !== null; var barClass = "safe-upgrade-progress-bar".concat(animateBar ? ' is-active' : ''); - this.steps.progress.html("\n").concat(data.message || '', "
\n ").concat(statusLine, "\n ").concat(percentLabel ? "".concat(data.message || '', "
") : data.message && stage !== 'installing' && stage !== 'finalizing' ? "".concat(data.message, "
") : ''; + this.steps.progress.html("\n