show progress for imports and deletions
This commit is contained in:
parent
38eea05789
commit
5e342b6fe5
13 changed files with 343 additions and 115 deletions
|
|
@ -628,7 +628,11 @@ function renderLibrary() {
|
|||
: (inPrefix ? t('library.removePrefixNote') : '')
|
||||
const ok = await confirmModal(t('library.removeConfirm', game.name, note), t('library.remove'))
|
||||
if (!ok) return
|
||||
await window.plwl.gamesRemove(game.id)
|
||||
// portable apps delete their folder too - show how far that got
|
||||
await runWithProgress({
|
||||
title: t('library.removing', game.name),
|
||||
action: () => window.plwl.gamesRemove(game.id)
|
||||
})
|
||||
toast(t('toast.gameRemoved', game.name), 'success')
|
||||
state.games = await window.plwl.gamesList()
|
||||
renderLibrary()
|
||||
|
|
@ -755,14 +759,22 @@ async function doImport(srcDir, mode) {
|
|||
const name = srcDir.split('/').filter(Boolean).pop()
|
||||
const modal = showModal(`
|
||||
<h2>${t('import.progress', name)}</h2>
|
||||
<div class="progress-row"><progress id="import-progress"></progress></div>
|
||||
<p id="import-status"></p>
|
||||
<div class="progress-row"><progress id="import-progress"></progress><span class="progress-pct"></span></div>
|
||||
<p class="subtitle" id="import-status"></p>
|
||||
`)
|
||||
let count = 0
|
||||
window.plwl.onImportProgress(() => {
|
||||
window.plwl.onImportProgress(data => {
|
||||
count++
|
||||
const bar = modal.querySelector('#import-progress')
|
||||
const pct = modal.querySelector('.progress-pct')
|
||||
const status = modal.querySelector('#import-status')
|
||||
if (status) status.textContent = t('import.fileDone', count)
|
||||
// byte totals drive the bar when the copy knows them; a rename or a
|
||||
// symlink-only run sends plain path events and only bumps the count
|
||||
if (bar && data && data.totalBytes) {
|
||||
renderProgressBar(bar, data.bytes, data.totalBytes)
|
||||
if (pct) pct.textContent = Math.round(((data.bytes || 0) / data.totalBytes) * 100) + '%'
|
||||
}
|
||||
if (status) status.textContent = t('import.fileDone', (data && data.files) || count)
|
||||
})
|
||||
try {
|
||||
const game = await window.plwl.gamesImport(srcDir, mode)
|
||||
|
|
@ -895,9 +907,11 @@ function wireOpenWith() {
|
|||
// copied, paths stay prefix-relative.
|
||||
async function doPrefixImport(prefix, srcDir) {
|
||||
const name = srcDir.split('/').filter(Boolean).pop()
|
||||
const modal = showModal(`
|
||||
<h2>${t('import.progress', name)}</h2>
|
||||
<div class="progress-row"><progress></progress></div>
|
||||
// nothing is copied here - the app is just registered in place, so there is
|
||||
// no progress to show (no bar that would sit at zero)
|
||||
showModal(`
|
||||
<h2>${t('import.adding', name)}</h2>
|
||||
<p class="subtitle">${t('import.prefix.registering')}</p>
|
||||
`)
|
||||
try {
|
||||
const game = await window.plwl.gamesImportFromPrefix(prefix, srcDir)
|
||||
|
|
@ -1008,7 +1022,11 @@ function renderPrefixes() {
|
|||
if (users.all.length) return toast(t('prefixes.hasApps', p.name, users.all.map(g => g.name).join(', ')), 'error')
|
||||
const ok = await confirmModal(t('prefixes.deleteConfirm', p.name), t('prefixes.delete'))
|
||||
if (!ok) return
|
||||
const res = await window.plwl.prefixesDelete(p.name)
|
||||
// removing a whole windows drive takes a while: measured progress modal
|
||||
const res = await runWithProgress({
|
||||
title: t('prefixes.deleting', p.name),
|
||||
action: () => window.plwl.prefixesDelete(p.name)
|
||||
})
|
||||
if (res.ok) toast(t('toast.prefixDeleted', p.name), 'success')
|
||||
else if (res.error === 'isMain') toast(t('prefixes.mainLocked'), 'error')
|
||||
else if (res.error === 'inUse' || res.error === 'hasApps') toast(t('prefixes.hasApps', p.name, (res.apps || []).join(', ')), 'error')
|
||||
|
|
@ -1091,7 +1109,10 @@ function renderWines() {
|
|||
delBtn.onclick = async () => {
|
||||
const ok = await confirmModal(t('wines.deleteConfirm', w.label), t('wines.delete'))
|
||||
if (!ok) return
|
||||
const res = await window.plwl.winesDelete(w.id)
|
||||
const res = await runWithProgress({
|
||||
title: t('wines.deleting', w.label),
|
||||
action: () => window.plwl.winesDelete(w.id)
|
||||
})
|
||||
if (res.ok) {
|
||||
toast(t('wines.deleted', w.label), 'success')
|
||||
state.wines = await window.plwl.winesList()
|
||||
|
|
@ -1398,6 +1419,37 @@ function barValues(info) {
|
|||
return [bytes, total]
|
||||
}
|
||||
|
||||
// One modal for operations with a measurable size (import, delete, long
|
||||
// copies): the bar follows BYTES for the same reason the sync bar does, and a
|
||||
// percent label sits next to it. Main-process work streams through the single
|
||||
// import:progress slot (preload keeps only the newest listener), so the modal
|
||||
// is the only thing showing progress at a time. `action` is the ipc call;
|
||||
// the modal closes itself when it settles.
|
||||
async function runWithProgress({ title, action }) {
|
||||
const modal = showModal(`
|
||||
<h2>${title}</h2>
|
||||
<div class="progress-row"><progress></progress><span class="progress-pct"></span></div>
|
||||
<p class="subtitle" id="op-status">${t('import.phase.count')}</p>
|
||||
`)
|
||||
const bar = modal.querySelector('progress')
|
||||
const pct = modal.querySelector('.progress-pct')
|
||||
const status = modal.querySelector('#op-status')
|
||||
window.plwl.onImportProgress(data => {
|
||||
if (!data || !data.phase) return
|
||||
// the counting pass reports its own phase with the totals; the bar starts
|
||||
// moving once the real units arrive
|
||||
if (data.phase === 'count') return
|
||||
renderProgressBar(bar, data.bytes, data.totalBytes)
|
||||
pct.textContent = data.totalBytes ? Math.round(((data.bytes || 0) / data.totalBytes) * 100) + '%' : ''
|
||||
if (data.totalFiles) status.textContent = t('import.phase.' + data.phase, data.files || 0, data.totalFiles)
|
||||
})
|
||||
try {
|
||||
return await action()
|
||||
} finally {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
|
||||
// "340 MB / 1.2 GB" once the total is known (the sending side learns it as it
|
||||
// plans the units, the receiving side as the manifests arrive), otherwise
|
||||
// just what has moved so far - so the line is never a lying zero.
|
||||
|
|
@ -1848,12 +1900,10 @@ function wireEvents() {
|
|||
qs('#btn-wines-import').addEventListener('click', async () => {
|
||||
const srcDir = await window.plwl.dialogPickFile({ properties: ['openDirectory'], title: t('wines.importPick') })
|
||||
if (!srcDir) return
|
||||
const modal = showModal(`
|
||||
<h2>${t('wines.importing')}</h2>
|
||||
<div class="progress-row"><progress></progress></div>
|
||||
`)
|
||||
const res = await window.plwl.winesImport(srcDir)
|
||||
closeModal()
|
||||
const res = await runWithProgress({
|
||||
title: t('wines.importing'),
|
||||
action: () => window.plwl.winesImport(srcDir)
|
||||
})
|
||||
if (res.ok) {
|
||||
toast(t('wines.imported', res.path), 'success')
|
||||
state.wines = await window.plwl.winesList()
|
||||
|
|
|
|||
|
|
@ -797,5 +797,10 @@ progress {
|
|||
background: rgba(255, 255, 255, 0.08);
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.progress-pct {
|
||||
min-width: 2.6rem;
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
progress::-webkit-progress-bar { background: rgba(255, 255, 255, 0.08); border-radius: 0.4rem; }
|
||||
progress::-webkit-progress-value { background: var(--text-color); border-radius: 0.4rem; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue