feat(qt): the Albums funnel survives navigation and restarts
The filter worked but forgot itself twice over: leaving Local Library
reset it, and so did closing the app. The first one is not a bug in the
view, it is what this port does — navigating away DESTROYS the view
(project_qt_nav_eager_tabs), so a `property var filter: ({})` cannot
outlive a trip to Discover.
Both follow from one move: the funnel lives on the BRIDGE, mirrored into
the shared ui_prefs.json. The bridge outlives the view, and the file
outlives the process, so one property buys both kinds of persistence.
`setFilter()` is the only writer, so a mutator cannot change the funnel
and forget to persist it, and the view re-seeds from a Connections on
the qproperty rather than only at construction — a republish has to
reach a view that is already open.
The saved funnel is pruned where the source GATES are published, not
somewhere else, so the two can never disagree: a restored `jellyfin`
tick with no Jellyfin configured would match zero rows while the chip
that clears it is hidden by the same gate — an empty grid with a badge
counting a filter the user cannot see. Only the media-server keys are
pruned; local/offline/plex are always meaningful words.
Verified: ticked Jellyfin, went to Discover and back (badge and grid
held), restarted the process (held), pressed Clear (ui_prefs went to
{}). The prune is reasoned, not exercised — both servers are configured
on this box, so no gate is off to test it against.
cc
This commit is contained in:
4 files changed
+113
-3
No files matched your search
@@ -102,19 +102,48 @@ Rectangle {
|
||||
|
||||
// Albums quality/format/source filter (LibAlbumFilterState).
|
||||
property bool filterOpen: false
|
||||
property var filter: ({})
|
||||
// SEEDED FROM THE BRIDGE, not defaulted to `{}`. This view is DESTROYED on
|
||||
// every navigation away (see the bridge's `albums_filter` doc), so a
|
||||
// view-local default meant the funnel reset the moment the user visited
|
||||
// Discover — and never survived a restart at all. `QbzLocal.albumsFilter`
|
||||
// outlives the view and mirrors `ui_prefs.json`.
|
||||
property var filter: root.parseFilter(QbzLocal.albumsFilter)
|
||||
function parseFilter(json) {
|
||||
if (!json || json === "") return ({})
|
||||
try { return JSON.parse(json) || ({}) } catch (e) { return ({}) }
|
||||
}
|
||||
// A LATER republish still wins: the gates and the saved funnel are
|
||||
// published together when a media server is connected or removed, and the
|
||||
// pruning that happens there (a tick whose chip is now hidden) has to
|
||||
// reach a view that is already open. Guarded on a real difference so
|
||||
// writing the property back does not bounce.
|
||||
Connections {
|
||||
target: QbzLocal
|
||||
function onAlbumsFilterChanged() {
|
||||
var next = root.parseFilter(QbzLocal.albumsFilter)
|
||||
if (JSON.stringify(next) !== JSON.stringify(root.filter))
|
||||
root.filter = next
|
||||
}
|
||||
}
|
||||
readonly property int filterCount: {
|
||||
var n = 0
|
||||
for (var k in filter) if (filter[k]) n++
|
||||
return n
|
||||
}
|
||||
/// The ONE writer. Both mutators funnel through it, so neither can change
|
||||
/// the funnel and forget to persist it.
|
||||
function setFilter(f) {
|
||||
filter = f
|
||||
QbzLocal.setAlbumsFilterJson(
|
||||
Object.keys(f).length === 0 ? "" : JSON.stringify(f))
|
||||
}
|
||||
function toggleFilter(key) {
|
||||
var f = Object.assign({}, filter)
|
||||
f[key] = !f[key]
|
||||
if (!f[key]) delete f[key]
|
||||
filter = f
|
||||
setFilter(f)
|
||||
}
|
||||
function clearFilter() { filter = ({}) }
|
||||
function clearFilter() { setFilter({}) }
|
||||
|
||||
/// The funnel's state as the applied-filters tooltip wants it: three groups
|
||||
/// in the popup's own order, each holding the labels of the keys that are
|
||||
|
||||
Reference in new issue
Block a user