guard prefix and wine edits while their apps run

This commit is contained in:
olcxja 2026-10-02 14:46:21 +02:00
commit 38eea05789
9 changed files with 170 additions and 39 deletions

View file

@ -921,6 +921,16 @@ async function doPrefixImport(prefix, srcDir) {
}
}
// Apps the launcher would name when it refuses a prefix rename/delete: a
// running app (blocking immediately) or a registered one (orphaned by a
// delete). Fetched fresh - the Prefixes view does not keep the library hot.
async function prefixUsers(name) {
const [games, running] = await Promise.all([window.plwl.gamesList(), window.plwl.gamesListRunning()])
const runningSet = new Set(running || [])
const users = (games || []).filter(g => (g.prefix || state.prefixes.default || 'Main') === name)
return { all: users, running: users.filter(g => runningSet.has(g.id)) }
}
function renderPrefixes() {
const list = qs('#prefix-list')
list.innerHTML = ''
@ -952,10 +962,11 @@ function renderPrefixes() {
if (renBtn) {
renBtn.textContent = t('prefixes.rename')
renBtn.onclick = async () => {
const users = await prefixUsers(p.name)
const name = await promptModal({
title: t('prefixes.renameTitle'),
label: t('prefixes.namePlaceholder'),
hint: t('prefixes.renameHint'),
hint: users.running.length ? t('prefixes.inUseHint') : t('prefixes.renameHint'),
value: p.name,
confirm: t('prefixes.rename')
})
@ -963,8 +974,12 @@ function renderPrefixes() {
const res = await window.plwl.prefixesRename(p.name, name)
if (!res || !res.ok) {
const err = (res && res.error) || ''
const known = { alreadyExists: 'prefixes.exists', invalidName: 'prefixes.invalidName', isMain: 'prefixes.mainLocked' }
toast(known[err] ? t(known[err]) : t('prefixes.renameFailed', err), 'error')
if (err === 'inUse') toast(t('prefixes.inUse', p.name, (res.apps || []).join(', ')), 'error')
else if (err === 'hasApps') toast(t('prefixes.hasApps', p.name, (res.apps || []).join(', ')), 'error')
else {
const known = { alreadyExists: 'prefixes.exists', invalidName: 'prefixes.invalidName', isMain: 'prefixes.mainLocked' }
toast(known[err] ? t(known[err]) : t('prefixes.renameFailed', err), 'error')
}
return
}
toast(t('prefixes.renamed', p.name, res.name), 'success')
@ -979,17 +994,24 @@ function renderPrefixes() {
defBtn.onclick = async () => {
const res = await window.plwl.prefixesSetDefault(p.name)
if (res && res.ok !== false) toast(t('toast.prefixDefault', p.name), 'success')
else if (res && res.error === 'inUse') toast(t('prefixes.inUse', p.name, (res.apps || []).join(', ')), 'error')
else if (res && res.error === 'notFound') toast(t('toast.prefixDeleteFailed', p.name), 'error')
state.prefixes = await window.plwl.prefixesList()
renderPrefixes()
}
const delBtn = row.querySelector('[data-act=delete]')
delBtn.innerHTML = `${ICONS.trash()}<span>${t('prefixes.delete')}</span>`
delBtn.onclick = async () => {
// refused before the confirm prompt, with the reason: the folder is
// either held by a running app or still referenced by registered ones
const users = await prefixUsers(p.name)
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)
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')
else toast(t('toast.prefixDeleteFailed', p.name), 'error')
state.prefixes = await window.plwl.prefixesList()
renderPrefixes()
@ -1076,6 +1098,8 @@ function renderWines() {
state.settings = await window.plwl.settingsGet()
} else if (res.error === 'isSystem') {
toast(t('wines.systemLocked'), 'error')
} else if (res.error === 'inUse') {
toast(t('wines.inUse', (res.apps || []).join(', ')), 'error')
}
renderWines()
}