feat(home): sort control for the Library Albums rail
Adds a compact dropdown in the Library Albums (favoriteAlbums) row header, left of the page chevrons: Recently added (default), First added (reverse), Random (shuffle). Reorders the cached base list in Rust with no re-fetch and re-fires artwork by the new index. Also corrects the stale HomeView comment that claimed the recently-played refresh lives in the toolbar (it is a row-header button). 8 locales updated. cc
This commit is contained in:
13 files changed
+202
-5
No files matched your search
@@ -4500,3 +4500,14 @@ msgstr "Ausgabe: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Plex-Bibliothek synchronisieren"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Zuletzt hinzugefügt"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "Zuerst hinzugefügt"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Zufällig"
|
||||
|
||||
@@ -269,3 +269,14 @@ msgstr "Output: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Sync Plex library"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Recently added"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "First added"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Random"
|
||||
|
||||
@@ -4530,3 +4530,14 @@ msgstr "Salida: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Sincronizar biblioteca de Plex"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Recién agregados"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "Primeros agregados"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Aleatorio"
|
||||
|
||||
@@ -4500,3 +4500,14 @@ msgstr "Sortie : {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Synchroniser la bibliothèque Plex"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Ajoutés récemment"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "Ajoutés en premier"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Aléatoire"
|
||||
|
||||
@@ -4489,3 +4489,14 @@ msgstr "出力: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Plexライブラリを同期"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "最近追加した順"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "追加が古い順"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "ランダム"
|
||||
|
||||
@@ -4512,3 +4512,14 @@ msgstr "Uitvoer: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Plex-bibliotheek synchroniseren"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Recent toegevoegd"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "Eerst toegevoegd"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Willekeurig"
|
||||
|
||||
@@ -4500,3 +4500,14 @@ msgstr "Saída: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Sincronizar biblioteca do Plex"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Adicionados recentemente"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "Adicionados primeiro"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Aleatório"
|
||||
|
||||
@@ -4513,3 +4513,14 @@ msgstr "Вывод: {}"
|
||||
# Local Library header Plex sync button (#573)
|
||||
msgid "Sync Plex library"
|
||||
msgstr "Синхронизировать библиотеку Plex"
|
||||
|
||||
# Library Albums sort options (#566)
|
||||
msgid "Recently added"
|
||||
msgstr "Недавно добавленные"
|
||||
|
||||
msgid "First added"
|
||||
msgstr "Добавленные первыми"
|
||||
|
||||
msgid "Random"
|
||||
msgstr "Случайно"
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Typography } from "../foundation/typography.slint";
|
||||
import { DiscoverSection } from "../state.slint";
|
||||
import { AlbumCard } from "AlbumCard.slint";
|
||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { QbzSelect } from "../primitives/QbzSelect.slint";
|
||||
|
||||
component NavButton inherits Rectangle {
|
||||
in property <image> icon;
|
||||
@@ -71,6 +72,14 @@ export component Carousel inherits VerticalLayout {
|
||||
in property <bool> show-view-all: false;
|
||||
// Optional refresh affordance in the row header (recently-played rows).
|
||||
in property <bool> show-refresh: false;
|
||||
// Optional sort dropdown in the row header, above/left of the page
|
||||
// chevrons (Library Albums #566). `sort-options` are the localized
|
||||
// labels; `sort-index` is the current selection; `sort-changed(i)` fires
|
||||
// when the user picks another option.
|
||||
in property <bool> show-sort: false;
|
||||
in property <[string]> sort-options: [];
|
||||
in property <int> sort-index: 0;
|
||||
callback sort-changed(int);
|
||||
callback album-clicked(string);
|
||||
callback media-action(string, string, string);
|
||||
callback view-all-clicked();
|
||||
@@ -182,6 +191,21 @@ export component Carousel inherits VerticalLayout {
|
||||
}
|
||||
HorizontalLayout {
|
||||
spacing: 4px;
|
||||
// Sort dropdown (Library Albums): sits left of the page chevrons,
|
||||
// a compact toolbar-style select. Picking an option fires
|
||||
// sort-changed(i); the host re-orders the row's data in Rust.
|
||||
if root.show-sort: VerticalLayout {
|
||||
alignment: center;
|
||||
QbzSelect {
|
||||
sm: true;
|
||||
menu-width: 170px;
|
||||
options: root.sort-options;
|
||||
current-index: root.sort-index;
|
||||
selected(i) => {
|
||||
root.sort-changed(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
// "View all" sits to the LEFT of the carousel page chevrons,
|
||||
// matching the Tauri DiscoverySection header.
|
||||
if root.show-view-all: VerticalLayout {
|
||||
|
||||
@@ -121,9 +121,10 @@ component GenreButton inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Manual refresh for the recently-played rails — sits in the toolbar next
|
||||
// to the nav cluster. Same 32px icon-button form as GearButton, plus the
|
||||
// global hover tooltip (TooltipState — the TransportControls pattern).
|
||||
// Settings/config gear for the Home configurator, anchored in the toolbar's
|
||||
// right controls. (The recently-played refresh is NOT here — it lives in each
|
||||
// recently-played row header, left of the page chevrons; see Carousel
|
||||
// `show-refresh`.)
|
||||
component GearButton inherits Rectangle {
|
||||
callback clicked();
|
||||
|
||||
@@ -461,6 +462,18 @@ export component HomeView inherits Rectangle {
|
||||
if desc.id == "favoriteAlbums"
|
||||
&& HomeState.favorite-albums.albums.length > 0: Carousel {
|
||||
section: HomeState.favorite-albums;
|
||||
// Sort dropdown left of the page chevrons (#566).
|
||||
show-sort: true;
|
||||
sort-options: [
|
||||
@tr("Recently added"),
|
||||
@tr("First added"),
|
||||
@tr("Random"),
|
||||
];
|
||||
sort-index: HomeState.library-albums-sort;
|
||||
sort-changed(i) => {
|
||||
HomeState.library-albums-sort = i;
|
||||
HomeActions.set-library-albums-sort(i);
|
||||
}
|
||||
album-clicked(id) => {
|
||||
root.album-clicked(id);
|
||||
}
|
||||
|
||||
@@ -175,6 +175,10 @@ export global HomeState {
|
||||
// top-artists' title is @tr in the view arm (bare list, like For You).
|
||||
// (qobuzMixes, the fourth ported section, is static tiles — no state.)
|
||||
in property <DiscoverSection> favorite-albums;
|
||||
// Library Albums (favoriteAlbums) sort order: 0 = Recently added
|
||||
// (default), 1 = First added (reverse), 2 = Random (shuffled). Rust
|
||||
// reorders HomeState.favorite-albums in place when this changes.
|
||||
in-out property <int> library-albums-sort: 0;
|
||||
in property <DiscoverSection> release-watch;
|
||||
in property <[SlimItem]> top-artists: [];
|
||||
// Qobuz Playlists row (Home + Editor's Picks). Single-cover cards; the
|
||||
@@ -209,9 +213,12 @@ export global HomeActions {
|
||||
// Home was off-screen (dirty-flag gated in Rust — a no-op otherwise, so
|
||||
// mounting Home stays free).
|
||||
callback home-mounted();
|
||||
// Manual refresh (the toolbar button next to the nav cluster):
|
||||
// unconditionally re-read the local recently-played rails.
|
||||
// Manual refresh (the row-header button next to the Recently Played
|
||||
// Albums nav chevrons): unconditionally re-read the local rails.
|
||||
callback refresh-recent();
|
||||
// Library Albums sort changed (see HomeState.library-albums-sort). Rust
|
||||
// reorders the cached base list and re-pushes favorite-albums + artwork.
|
||||
callback set-library-albums-sort(int);
|
||||
}
|
||||
|
||||
// RecentAlbumsView — the full "Recently Played Albums" page opened from the
|
||||
|
||||
@@ -1084,6 +1084,11 @@ pub fn apply_home(window: &AppWindow, data: HomeData) {
|
||||
// For You twins (foryou::apply_favorite_albums / apply_release_watch /
|
||||
// apply_top_artists), separate lifecycles. Top Artists' title lives in
|
||||
// the HomeView arm (@tr, like ForYouView's) — its model is a bare list.
|
||||
// Cache the base (Recently-added) order so the header sort dropdown can
|
||||
// reorder without a re-fetch, and reset the selection to the default on
|
||||
// every fresh load (the load-time artwork dispatch is in base order).
|
||||
*crate::LIB_ALBUMS_BASE.lock().unwrap() = data.favorite_albums.clone();
|
||||
state.set_library_albums_sort(0);
|
||||
state.set_favorite_albums(crate::foryou::section(
|
||||
&qbz_i18n::t("Library Albums"),
|
||||
&data.favorite_albums,
|
||||
|
||||
@@ -3004,6 +3004,57 @@ fn refresh_recent_rails(
|
||||
});
|
||||
}
|
||||
|
||||
/// Base ("Recently added") order of the Home "Library Albums" rail
|
||||
/// (`favoriteAlbums`, #566), cached on every home load so the header sort
|
||||
/// dropdown can reorder without a re-fetch. Read by `apply_library_albums_sort`.
|
||||
pub(crate) static LIB_ALBUMS_BASE: std::sync::Mutex<Vec<crate::foryou::AlbumCard>> =
|
||||
std::sync::Mutex::new(Vec::new());
|
||||
|
||||
/// Reorder the cached Library-Albums base list per the sort selection.
|
||||
/// `mode`: 0 = Recently added (base), 1 = First added (reverse), 2 = Random.
|
||||
fn library_albums_sorted(mode: i32) -> Vec<crate::foryou::AlbumCard> {
|
||||
let mut cards = LIB_ALBUMS_BASE.lock().unwrap().clone();
|
||||
match mode {
|
||||
1 => cards.reverse(),
|
||||
2 => {
|
||||
use rand::seq::SliceRandom;
|
||||
cards.shuffle(&mut rand::rng());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
cards
|
||||
}
|
||||
|
||||
/// Re-push the Home "Library Albums" rail in the chosen sort order + re-fire
|
||||
/// its artwork by the new index. No re-fetch — reorders the cached base list
|
||||
/// (mirrors `refresh_recent_rails`' local re-apply). Runs on the UI thread
|
||||
/// (Slint callback); `spawn_loads` does its own threading and the covers are
|
||||
/// cache-served, so a reorder is near-instant.
|
||||
fn apply_library_albums_sort(
|
||||
weak: slint::Weak<AppWindow>,
|
||||
mode: i32,
|
||||
image_cache: artwork::ImageCache,
|
||||
) {
|
||||
let cards = library_albums_sorted(mode);
|
||||
if let Some(w) = weak.upgrade() {
|
||||
w.global::<HomeState>().set_favorite_albums(crate::foryou::section(
|
||||
&qbz_i18n::t("Library Albums"),
|
||||
&cards,
|
||||
));
|
||||
}
|
||||
let jobs: Vec<artwork::ArtworkJob> = cards
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, card)| {
|
||||
(!card.artwork_url.is_empty()).then(|| artwork::ArtworkJob {
|
||||
target: artwork::ArtworkTarget::HomeFavoriteAlbum { idx },
|
||||
url: card.artwork_url.clone(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
artwork::spawn_loads(jobs, weak, image_cache);
|
||||
}
|
||||
|
||||
/// Playback hook: a play was just recorded in the recently-played store
|
||||
/// (`playback::record_recent`). Marks the Home rails stale and — when the
|
||||
/// Home view is currently showing — refreshes them immediately, so the rails
|
||||
@@ -15222,6 +15273,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
refresh_recent_rails(weak.clone(), &handle, image_cache.clone());
|
||||
});
|
||||
}
|
||||
// Library Albums (#566) header sort: reorder the cached favorite-albums
|
||||
// base list (0 recent / 1 first / 2 random) and re-push it + its covers.
|
||||
{
|
||||
let weak = window.as_weak();
|
||||
let image_cache = image_cache.clone();
|
||||
window.global::<HomeActions>().on_set_library_albums_sort(move |mode| {
|
||||
apply_library_albums_sort(weak.clone(), mode, image_cache.clone());
|
||||
});
|
||||
}
|
||||
|
||||
// Qobuz Playlists category filter (multi-select, client-side). Toggling /
|
||||
// clearing a tag re-filters the cached playlists row and re-fires the
|
||||
|
||||
Reference in new issue
Block a user