fixes:
- fixed icon saving - slightly improved library layout
This commit is contained in:
parent
096e36a9fa
commit
f8ce9a9945
4 changed files with 35 additions and 13 deletions
|
|
@ -1678,7 +1678,10 @@ function wireEvents() {
|
||||||
if (res.ok) refreshConfigIcon()
|
if (res.ok) refreshConfigIcon()
|
||||||
}
|
}
|
||||||
qs('#gc-icon-auto').addEventListener('click', async () => {
|
qs('#gc-icon-auto').addEventListener('click', async () => {
|
||||||
applyIconResult(await window.plwl.gamesAutoIcon(state.currentGame.id))
|
// extract from the exe the field shows, not the saved one - the user may
|
||||||
|
// have just pointed the app at another exe and expects its icon
|
||||||
|
const exe = absGamePathInput(qs('#gc-exe').value.trim(), state.currentGame)
|
||||||
|
applyIconResult(await window.plwl.gamesAutoIcon(state.currentGame.id, exe))
|
||||||
})
|
})
|
||||||
qs('#gc-icon-browse').addEventListener('click', async () => {
|
qs('#gc-icon-browse').addEventListener('click', async () => {
|
||||||
// start in the app's own folder (prefix apps included) - that is where
|
// start in the app's own folder (prefix apps included) - that is where
|
||||||
|
|
|
||||||
|
|
@ -490,8 +490,8 @@ select {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
.game-card {
|
.game-card {
|
||||||
flex: 1 1 15rem;
|
flex: 1 1 17rem;
|
||||||
min-width: 15rem;
|
min-width: 17rem;
|
||||||
max-width: 24rem;
|
max-width: 24rem;
|
||||||
background: var(--panel-color);
|
background: var(--panel-color);
|
||||||
border: var(--border-width) solid var(--light-border-color);
|
border: var(--border-width) solid var(--light-border-color);
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,13 @@ function errorResult(err) {
|
||||||
|
|
||||||
const iconDir = id => path.join(DIRS.DATA, 'icons', id)
|
const iconDir = id => path.join(DIRS.DATA, 'icons', id)
|
||||||
|
|
||||||
function clearIconDir(id) {
|
// `keep` spares one file (the icon just written): the icon protocol picks the
|
||||||
|
// LAST icon.* alphabetically, so a leftover icon.png from an earlier pick
|
||||||
|
// would otherwise keep showing the old icon.
|
||||||
|
function clearIconDir(id, keep) {
|
||||||
try {
|
try {
|
||||||
for (const f of fs.readdirSync(iconDir(id))) {
|
for (const f of fs.readdirSync(iconDir(id))) {
|
||||||
if (f.startsWith('icon.')) fs.rmSync(path.join(iconDir(id), f), { force: true })
|
if (f.startsWith('icon.') && f !== keep) fs.rmSync(path.join(iconDir(id), f), { force: true })
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +39,11 @@ function clearIconDir(id) {
|
||||||
function extractGameIcon(id, exePath) {
|
function extractGameIcon(id, exePath) {
|
||||||
const out = path.join(iconDir(id), 'icon.ico')
|
const out = path.join(iconDir(id), 'icon.ico')
|
||||||
const res = extractIconFromExe(exePath, out)
|
const res = extractIconFromExe(exePath, out)
|
||||||
if (res.ok) library.updateGame(id, { iconSource: out })
|
// on failure nothing is cleared, so the previous icon survives a wrong exe
|
||||||
|
if (res.ok) {
|
||||||
|
clearIconDir(id, 'icon.ico')
|
||||||
|
library.updateGame(id, { iconSource: out })
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,11 +159,14 @@ function registerIpc({ getWindow, onGamesChanged }) {
|
||||||
if (!game || !game.exePath) return { ok: false, error: 'bad-request' }
|
if (!game || !game.exePath) return { ok: false, error: 'bad-request' }
|
||||||
return extractGameIcon(id, game.exePath)
|
return extractGameIcon(id, game.exePath)
|
||||||
})
|
})
|
||||||
ipcMain.handle('games:autoIcon', (e, id) => {
|
// exePath comes from the config field, which may hold a path the user just
|
||||||
|
// picked but has not saved yet - the icon has to come from THAT exe
|
||||||
|
ipcMain.handle('games:autoIcon', (e, id, exePath) => {
|
||||||
const game = library.getGame(id)
|
const game = library.getGame(id)
|
||||||
if (!game || !game.exePath) return { ok: false, error: 'bad-request' }
|
if (!game) return { ok: false, error: 'bad-request' }
|
||||||
clearIconDir(id)
|
const exe = exePath || game.exePath
|
||||||
return extractGameIcon(id, game.exePath)
|
if (!exe) return { ok: false, error: 'bad-request' }
|
||||||
|
return extractGameIcon(id, exe)
|
||||||
})
|
})
|
||||||
|
|
||||||
// "Open with PLWL" ------------------------------------------------------------------
|
// "Open with PLWL" ------------------------------------------------------------------
|
||||||
|
|
@ -253,7 +263,7 @@ function registerIpc({ getWindow, onGamesChanged }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// game icons as a protocol so the renderer can display them safely ---------------------------
|
// game icons as a protocol so the renderer can display them safely ---------------------------
|
||||||
protocol.handle('plwl-icon', request => {
|
protocol.handle('plwl-icon', async request => {
|
||||||
const id = decodeURIComponent(new URL(request.url).hostname)
|
const id = decodeURIComponent(new URL(request.url).hostname)
|
||||||
let file = null
|
let file = null
|
||||||
try {
|
try {
|
||||||
|
|
@ -265,7 +275,16 @@ function registerIpc({ getWindow, onGamesChanged }) {
|
||||||
if (pick) file = path.join(iconDir(id), pick)
|
if (pick) file = path.join(iconDir(id), pick)
|
||||||
} catch {}
|
} catch {}
|
||||||
if (!file) return new Response('', { status: 404 })
|
if (!file) return new Response('', { status: 404 })
|
||||||
return net.fetch('file://' + file)
|
// a new icon lands at the SAME url, so the browser would keep its cached
|
||||||
|
// copy and the library card would only catch up after a restart - never
|
||||||
|
// let the icon request be cached
|
||||||
|
const res = await net.fetch('file://' + file)
|
||||||
|
return new Response(await res.arrayBuffer(), {
|
||||||
|
headers: {
|
||||||
|
'content-type': res.headers.get('content-type') || 'image/x-icon',
|
||||||
|
'cache-control': 'no-store'
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return { games }
|
return { games }
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ contextBridge.exposeInMainWorld('plwl', {
|
||||||
gamesClearPlaytime: id => ipcRenderer.invoke('games:clearPlaytime', id),
|
gamesClearPlaytime: id => ipcRenderer.invoke('games:clearPlaytime', id),
|
||||||
gamesSetIconFile: (id, p) => ipcRenderer.invoke('games:setIconFile', id, p),
|
gamesSetIconFile: (id, p) => ipcRenderer.invoke('games:setIconFile', id, p),
|
||||||
gamesExtractIcon: id => ipcRenderer.invoke('games:extractIcon', id),
|
gamesExtractIcon: id => ipcRenderer.invoke('games:extractIcon', id),
|
||||||
gamesAutoIcon: id => ipcRenderer.invoke('games:autoIcon', id),
|
gamesAutoIcon: (id, exePath) => ipcRenderer.invoke('games:autoIcon', id, exePath),
|
||||||
gamesIconPath: id => ipcRenderer.invoke('games:iconPath', id),
|
gamesIconPath: id => ipcRenderer.invoke('games:iconPath', id),
|
||||||
gamesResetIcon: id => ipcRenderer.invoke('games:resetIcon', id),
|
gamesResetIcon: id => ipcRenderer.invoke('games:resetIcon', id),
|
||||||
gamesRemove: id => ipcRenderer.invoke('games:remove', id),
|
gamesRemove: id => ipcRenderer.invoke('games:remove', id),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue