fix(qt): migrate playlist_meta, break a router binding loop, zebra the playlist
RECENTLY PLAYED PLAYLISTS STAYED EMPTY THROUGH EIGHT TRACKS, a restart and a manual refresh — while the plays were sitting in the database the whole time. Read off the live file rather than reasoned about: `playlist_play_events` held 3, 9 and 10 rows for three playlists, so the context edge was working perfectly; `playlist_meta` was empty, and its `.schema` said why — no `covers`, no `own_image`. `covers` and `own_image` were added by inlining them into the CREATE. `CREATE TABLE IF NOT EXISTS` does NOTHING when the table already exists, so anyone who had run the previous build kept the six-column table, every upsert failed on "no such column: covers", and the rail JOINs meta against events — so it rendered its empty state with the evidence one table away. The failure was invisible by construction: swallowed into a warn, and the UI's empty state is also its correct state for a user who has played nothing. A brand-new table is only new ONCE. The moment a build runs anywhere its schema is legacy and every later column is a migration, so the pair moves to the same idempotent ADD COLUMN the album history already uses, and the upsert's failure becomes an error rather than a warn. Verified against a copy of the real database: the columns land and all 22 events survive. A regression test builds the exact pre-covers schema, records a play into it, migrates, and asserts the upsert works and the old event still counts — it reproduces the bug without the fix, and it also asserts a second `init_schema` is a no-op, since every launch calls it. The meta is written by `playlist_qt::load`, so the three playlists already in the events table surface as soon as each is opened once. BINDING LOOP IN THE ROUTER, and this one was self-inflicted: "QML Loader: Binding loop detected for property source". `_stampRoute` runs inside the `source` binding, and it read the last path from a `property string` and then wrote it — a binding depending on a property it mutates. QML detects the cycle, warns, and stops re-evaluating, which is a broken router and not merely a noisy log. The holder becomes an object whose MEMBER is mutated: no change signal, so the dependency is satisfied once and never fires again. (The second warning on the same file, the `when` loop on the nav-flyout `activeTab` Binding, is untouched by any of this work and predates it. Left alone deliberately — it is a warning on a working feature, and rewriting that Binding is its own change.) ZEBRA on the playlist detail's track rows, like the local album page. TrackRow already owns the stripe, so it is one property. Release build clean, offscreen smoke green, 421/421 and 216/216. cc
This commit is contained in:
3 files changed
+102
-13
No files matched your search
@@ -895,6 +895,11 @@ Rectangle {
|
||||
showArtwork: true
|
||||
showAlbum: true
|
||||
showDownload: true
|
||||
// Alternating row tint, like the local album page
|
||||
// (LocalAlbumView.qml). TrackRow already owns the stripe —
|
||||
// it paints `#07ffffff` on even `number`s — so this is the
|
||||
// whole change.
|
||||
zebra: true
|
||||
selectMode: root.multiSelect
|
||||
checked: root.selected[item.id] === true
|
||||
onToggleSelect: root.toggleSelected(item.id)
|
||||
|
||||
Reference in new issue
Block a user