diff --git a/CHANGELOG.md b/CHANGELOG.md index 16fa7a7..78a749b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 1.4.2 + +- One place on the home screen always stays unpinned. With every place pinned, the last game opened (the + jailbreak's entry, say) had nowhere to go and ended up in the game library. Pin the whole row now leaves the + first place free, and Lineup will not save a home screen pinned in full. +- A row pinned in full by an older version opens with an offer to free its first place. + ## 1.4.1 1.4.0 was not released; its changes ship here. diff --git a/app/api.js b/app/api.js index 78ed7f6..c1566e6 100644 --- a/app/api.js +++ b/app/api.js @@ -3,7 +3,7 @@ import {Archives, Downloads, FileSystem, Http, Payloads} from '@ps5-react/core'; import {T} from './i18n.js'; -export const VERSION = '1.4.1'; +export const VERSION = '1.4.2'; const BASE = 'http://127.0.0.1:9077'; /** An error the app shows as is: a short title and what to do about it. */ diff --git a/app/app.json b/app/app.json index 5ecf898..01c36cd 100644 --- a/app/app.json +++ b/app/app.json @@ -3,7 +3,7 @@ "titleId": "PPSA99077", "contentId": "UP9000-PPSA99077_00-LINEUP0000000000", "name": "Lineup", - "version": "01.004.001", + "version": "01.004.002", "render": { "width": 1920, "height": 1080 diff --git a/app/i18n.js b/app/i18n.js index e601f07..0645b1c 100644 --- a/app/i18n.js +++ b/app/i18n.js @@ -53,6 +53,11 @@ const en = { missingBody: 'The home screen database was rebuilt or updated, so the games no longer hold their places. ' + 'Pin them again?', reapply: 'Pin again', later: 'Not now', + keepFree: 'Leave one place on the home screen unpinned, for the last game opened.', + fullTitle: 'Every place on the home screen is pinned', + fullBody: 'The last game opened has no place left there and ends up in the game library. ' + + 'Free the first place for it?', + freeFirst: 'Free the first place', }, saved: ['Order saved', 'Closing the app. The home screen comes back with the new order.'], closeNow: 'Close now', @@ -158,6 +163,11 @@ const pt = { missingBody: 'O banco de dados da tela inicial foi reconstruído ou atualizado, e os jogos não seguram mais o ' + 'lugar. Fixar de novo?', reapply: 'Fixar de novo', later: 'Agora não', + keepFree: 'Deixe um lugar da tela inicial sem fixar, para o último jogo aberto.', + fullTitle: 'Todos os lugares da tela inicial estão fixos', + fullBody: 'O último jogo aberto fica sem lugar ali e vai parar na biblioteca de jogos. ' + + 'Liberar o primeiro lugar para ele?', + freeFirst: 'Liberar o primeiro lugar', }, saved: ['Ordem salva', 'Fechando o app. A tela inicial já volta com a nova ordem.'], closeNow: 'Fechar agora', diff --git a/app/index.jsx b/app/index.jsx index 0b5a93a..4f6fb30 100644 --- a/app/index.jsx +++ b/app/index.jsx @@ -39,6 +39,14 @@ const sortKey = name => name.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCas // Games moved or pinned/unpinned since the row was read. `pinned` maps the games read pinned to their slots. const changed = s => s.order.reduce((count, id, i) => count + (s.home[id] !== i || !s.pins[id] !== !(id in s.pinned)), 0); const allPinned = pinned => Object.fromEntries(Object.keys(pinned).map(id => [id, true])); +// Every home place pinned: the last game opened (the jailbreak's entry, say) would have no place there and end up in +// the game library, so one stays free. lineupd refuses such a save too. +const homeFull = (order, pins) => order.slice(0, HOME_LIMIT).every(id => pins[id]); +const without = (pins, id) => { + const rest = {...pins}; + delete rest[id]; + return rest; +}; // The order the pins ask for, as lineupd's triggers work it out: pinned games at their slots (packed at the end of // a row that got shorter), the others in their order in the free places. @@ -295,10 +303,11 @@ function App() { if (game.pinned !== null && game.pinned !== undefined) pinned[game.titleId] = game.pinned; }); const base = list.map(game => game.titleId); - const missing = pins === 'missing'; + // A row pinned in full by 1.4.1 or older. + const layer = pins === 'missing' ? 'pinsMissing' : pins === 'on' && homeFull(base, allPinned(pinned)) ? 'pinsFull' : null; light(false); patch({phase: 'row', profiles, user, games, home, base, order: base, pins: allPinned(pinned), pinned, focus: 0, - dir: 1, lifted: false, layer: missing ? 'pinsMissing' : null, target: null, pending: missing ? null : {index: 0}}); + dir: 1, lifted: false, layer, target: null, pending: layer ? null : {index: 0}}); }; const boot = async () => { @@ -418,6 +427,11 @@ function App() { toast(T.alreadySaved); return; } + if (homeFull(now.order, now.pins)) { + close(); + toast(T.pins.keepFree); + return; + } light(false); patch({lifted: false, layer: 'busy'}); const result = await api.save(now.user, now.order, now.games, now.pins); @@ -480,13 +494,11 @@ function App() { return true; } if (action === 'square') { - patch(p => { - const id = p.order[p.focus]; - const pins = {...p.pins}; - if (pins[id]) delete pins[id]; - else pins[id] = true; - return {pins}; - }); + const p = ref.current; + const id = p.order[p.focus]; + const pins = p.pins[id] ? without(p.pins, id) : {...p.pins, [id]: true}; + if (!p.pins[id] && homeFull(p.order, pins)) toast(T.pins.keepFree); + else patch({pins}); return true; } return false; @@ -496,14 +508,17 @@ function App() { patch(p => ({order: p.base, pins: allPinned(p.pinned), layer: null, pending: {index: 0}})); toast(T.discarded); }; - const pinAll = on => patch(p => ({pins: on ? Object.fromEntries(p.order.map(id => [id, true])) : {}, layer: null, - pending: {index: p.focus}})); - // Puts the games back at their slots and saves, which creates the triggers again. + // The whole row but its first place, which stays free. + const pinAll = on => patch(p => ({pins: on ? Object.fromEntries(p.order.slice(1).map(id => [id, true])) : {}, + layer: null, pending: {index: p.focus}})); + // Puts the games back at their slots and saves, which creates the triggers again. A row pinned in full gets its + // first place freed. const reapply = () => { const now = ref.current; const order = pinnedOrder(now.order, now.pinned); - patch({order}); - save(null, {...now, order}, true); + const pins = homeFull(order, now.pins) ? without(now.pins, order[0]) : now.pins; + patch({order, pins}); + save(null, {...now, order, pins}, true); }; const unpinEverywhere = async () => { patch({layer: 'busy'}); @@ -664,6 +679,12 @@ function App() {