From a33fe4ba63f8f727bd4bc21c4e59cd706bad9a3b Mon Sep 17 00:00:00 2001 From: Erica Z Date: Sun, 24 Nov 2024 19:53:57 +0100 Subject: [PATCH] carousel thing --- resources/album_carousel.blp | 4 +-- resources/window.blp | 8 +++--- src/model/album.rs | 12 ++++++++ src/subsonic/album_list.rs | 29 ++++++++++--------- src/ui/album_carousel.rs | 56 ++++++++++++++++++++++++++++++------ src/ui/window.rs | 28 ++++++++++++++++-- 6 files changed, 106 insertions(+), 31 deletions(-) diff --git a/resources/album_carousel.blp b/resources/album_carousel.blp index eaaa1f5..3bd2649 100644 --- a/resources/album_carousel.blp +++ b/resources/album_carousel.blp @@ -80,7 +80,7 @@ template $AudreyUiAlbumCarousel: Adw.Bin { homogeneous: true; Label { - label: bind template.item as .string; + label: bind template.item as <$AudreyModelAlbum>.name; ellipsize: end; xalign: 0; margin-start: 6; @@ -91,7 +91,7 @@ template $AudreyUiAlbumCarousel: Adw.Bin { } Label { - label: bind template.item as .string; + label: bind template.item as <$AudreyModelAlbum>.artist; ellipsize: end; xalign: 0; margin-start: 6; diff --git a/resources/window.blp b/resources/window.blp index c35db60..6182185 100644 --- a/resources/window.blp +++ b/resources/window.blp @@ -41,22 +41,22 @@ template $AudreyUiWindow: Adw.ApplicationWindow { child: Box { orientation: vertical; - $AudreyUiAlbumCarousel { + $AudreyUiAlbumCarousel carousel1 { title: _("Explore from your library"); type: random; } - $AudreyUiAlbumCarousel { + $AudreyUiAlbumCarousel carousel2 { title: _("Newly added releases"); type: newest; } - $AudreyUiAlbumCarousel { + $AudreyUiAlbumCarousel carousel3 { title: _("Recently played"); type: recent; } - $AudreyUiAlbumCarousel { + $AudreyUiAlbumCarousel carousel4 { title: _("Most played"); type: frequent; } diff --git a/src/model/album.rs b/src/model/album.rs index eac8687..ea753f2 100644 --- a/src/model/album.rs +++ b/src/model/album.rs @@ -30,3 +30,15 @@ mod imp { glib::wrapper! { pub struct Album(ObjectSubclass); } + +impl Album { + pub fn from_api(album: &crate::subsonic::schema::AlbumID3) -> Self { + let album: Self = glib::Object::builder() + .property("id", &album.id) + .property("name", &album.name) + .property("artist", &album.artist) + .build(); + + album + } +} diff --git a/src/subsonic/album_list.rs b/src/subsonic/album_list.rs index d29d632..971278d 100644 --- a/src/subsonic/album_list.rs +++ b/src/subsonic/album_list.rs @@ -22,21 +22,24 @@ impl Client { size: u32, offset: u32, ) -> Result, Error> { - match type_ { - AlbumListType::Newest => { - self.get_subsonic::(self.url( - &["rest", "getAlbumList2"], - &[ - ("type", "newest"), - ("size", &size.to_string()), - ("offset", &offset.to_string()), - ], - )) - .await - } + let type_ = match type_ { + AlbumListType::Newest => "newest", + AlbumListType::Random => "random", + AlbumListType::Recent => "recent", + AlbumListType::Frequent => "frequent", _ => todo!("{type_:?}"), - } + }; + + self.get_subsonic::(self.url( + &["rest", "getAlbumList2"], + &[ + ("type", type_), + ("size", &size.to_string()), + ("offset", &offset.to_string()), + ], + )) + .await .map(|response| response.album_list2.album.unwrap_or_default()) } diff --git a/src/ui/album_carousel.rs b/src/ui/album_carousel.rs index dffa26b..069854b 100644 --- a/src/ui/album_carousel.rs +++ b/src/ui/album_carousel.rs @@ -1,9 +1,12 @@ +use crate::subsonic::AlbumListType; use adw::prelude::*; use adw::subclass::prelude::*; use adw::{gio, glib}; use glib::subclass::InitializingObject; use std::cell::{Cell, OnceCell, RefCell}; +use std::rc::Rc; use std::sync::OnceLock; +use tracing::{event, Level}; #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)] #[enum_type(name = "AudreyUiAlbumCarouselType")] @@ -26,6 +29,7 @@ mod imp { #[derive(gtk::CompositeTemplate, Default)] #[template(resource = "/eu/callcc/audrey/album_carousel.ui")] pub struct AlbumCarousel { + pub api: RefCell>>, title: RefCell, model: OnceCell, r#type: Cell, @@ -95,16 +99,43 @@ mod imp { .get_or_init(gio::ListStore::new::) } - fn update_model(&self) { - let window: crate::ui::Window = match self.obj().root() { - None => return, - Some(root) => root.dynamic_cast().unwrap(), - }; - let _api = window.api(); + pub fn update_model(&self) { + self.model().remove_all(); - match self.r#type.get() { - _ => todo!(), - } + let api = self.api.borrow(); + let api = match api.as_ref() { + Some(api) => api, + None => return, + }; + + let api = Rc::clone(&api); + let carousel = self.obj().downgrade(); + + let type_ = match self.r#type.get() { + Type::Random => AlbumListType::Random, + Type::Newest => AlbumListType::Newest, + Type::Recent => AlbumListType::Recent, + Type::Frequent => AlbumListType::Frequent, + }; + + glib::spawn_future_local(async move { + let album_list = match api.album_list(&type_, 50, 0).await { + Ok(album_list) => album_list, + Err(err) => { + event!(Level::ERROR, "could not fetch albums for carousel: {}", err); + return; + } + }; + + // TODO: handle to cancel on drop etc etc + let carousel = carousel.upgrade().unwrap(); + for album in album_list { + carousel + .imp() + .model() + .append(&crate::model::Album::from_api(&album)); + } + }); } } } @@ -114,3 +145,10 @@ glib::wrapper! { @extends adw::Bin, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } + +impl AlbumCarousel { + pub fn set_api(&self, api: Option<&Rc>) { + self.imp().api.replace(api.map(Rc::clone)); + self.imp().update_model(); + } +} diff --git a/src/ui/window.rs b/src/ui/window.rs index 91ae045..00ba91a 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -1,4 +1,5 @@ use crate::model::Song; +use crate::ui::AlbumCarousel; use crate::util::buffering::BufferingPulseController; use crate::{mpris, mpv}; use adw::prelude::*; @@ -90,6 +91,15 @@ mod imp { refreshing_albums: Cell, css_provider: gtk::CssProvider, + + #[template_child] + pub carousel1: TemplateChild, + #[template_child] + pub carousel2: TemplateChild, + #[template_child] + pub carousel3: TemplateChild, + #[template_child] + pub carousel4: TemplateChild, } impl Default for Window { @@ -152,6 +162,11 @@ mod imp { refreshing_albums: Cell::new(true), css_provider: gtk::CssProvider::new(), + + carousel1: Default::default(), + carousel2: Default::default(), + carousel3: Default::default(), + carousel4: Default::default(), } } } @@ -1011,11 +1026,18 @@ impl Window { } pub fn setup_connected(&self, api: crate::subsonic::Client) { - if self.imp().api.replace(Some(Rc::new(api))).is_some() { - unimplemented!("changing the api object"); - } + let api = Rc::new(api); self.set_can_click_shuffle_all(true); + + self.imp().carousel1.set_api(Some(&api)); + self.imp().carousel2.set_api(Some(&api)); + self.imp().carousel3.set_api(Some(&api)); + self.imp().carousel4.set_api(Some(&api)); + + if self.imp().api.replace(Some(api)).is_some() { + unimplemented!("changing the api object"); + } } pub fn playlist_next(&self) {