make star button work
This commit is contained in:
parent
720b73f2a8
commit
5b0a7124a8
7 changed files with 84 additions and 3 deletions
|
@ -110,8 +110,8 @@ template $AudreyUiPlayQueueSong: Box {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
focusable: true;
|
focusable: true;
|
||||||
// TODO icon-name: bind $star_button_icon_name (template.song as <$AudreyModelSong>.starred) as <string>;
|
icon-name: bind $heart_button_icon_name(template.song as <$AudreyModelSong>.starred) as <string>;
|
||||||
icon-name: bind $star_button_icon_name() as <string>;
|
clicked => $on_heart_button_clicked() swapped;
|
||||||
|
|
||||||
styles [
|
styles [
|
||||||
"flat"
|
"flat"
|
||||||
|
|
|
@ -176,10 +176,11 @@ template $AudreyUiPlaybar: Adw.Bin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
icon-name: "heart-empty-symbolic"; // placeholder
|
icon-name: bind $heart_button_icon_name(template.song as <$AudreyModelSong>.starred) as <string>;
|
||||||
halign: end;
|
halign: end;
|
||||||
valign: center;
|
valign: center;
|
||||||
sensitive: bind template.idle-active inverted;
|
sensitive: bind template.idle-active inverted;
|
||||||
|
clicked => $on_heart_button_clicked() swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|
|
@ -94,6 +94,7 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
|
||||||
duration: bind template.duration;
|
duration: bind template.duration;
|
||||||
idle-active: bind template.idle-active;
|
idle-active: bind template.idle-active;
|
||||||
playlist-count: bind template.playlist-count;
|
playlist-count: bind template.playlist-count;
|
||||||
|
client: bind template.client;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ mod imp {
|
||||||
track: Cell<i64>,
|
track: Cell<i64>,
|
||||||
#[property(get, set)]
|
#[property(get, set)]
|
||||||
cover_art: RefCell<String>,
|
cover_art: RefCell<String>,
|
||||||
|
#[property(get, set)]
|
||||||
|
starred: Cell<bool>,
|
||||||
|
|
||||||
#[property(get, set)]
|
#[property(get, set)]
|
||||||
stream_url: RefCell<String>,
|
stream_url: RefCell<String>,
|
||||||
|
|
|
@ -299,6 +299,16 @@ impl Client {
|
||||||
pub fn stream_url(&self, id: &str) -> url::Url {
|
pub fn stream_url(&self, id: &str) -> url::Url {
|
||||||
self.url(&["rest", "stream"], &[("id", id)])
|
self.url(&["rest", "stream"], &[("id", id)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn star(&self, id: &str) -> Result<(), Error> {
|
||||||
|
let url = self.url(&["rest", "star"], &[("id", id)]);
|
||||||
|
self.get_bytes(url).await.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unstar(&self, id: &str) -> Result<(), Error> {
|
||||||
|
let url = self.url(&["rest", "unstar"], &[("id", id)]);
|
||||||
|
self.get_bytes(url).await.map(|_| ())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
|
|
|
@ -3,6 +3,7 @@ use adw::prelude::*;
|
||||||
use glib::subclass::InitializingObject;
|
use glib::subclass::InitializingObject;
|
||||||
use gtk::{gdk, gio, glib, subclass::prelude::*};
|
use gtk::{gdk, gio, glib, subclass::prelude::*};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -35,6 +36,9 @@ mod imp {
|
||||||
|
|
||||||
drag_pos: Cell<(i32, i32)>,
|
drag_pos: Cell<(i32, i32)>,
|
||||||
drag_widget: Cell<Option<gtk::ListBox>>,
|
drag_widget: Cell<Option<gtk::ListBox>>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
client: RefCell<Option<crate::wrappers::Client>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -174,6 +178,37 @@ mod imp {
|
||||||
self.obj()
|
self.obj()
|
||||||
.set_current(playlist_pos == self.position.get() as i64);
|
.set_current(playlist_pos == self.position.get() as i64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: duplicated in playbar....
|
||||||
|
#[template_callback]
|
||||||
|
fn heart_button_icon_name(&self, starred: bool) -> &'static str {
|
||||||
|
if starred {
|
||||||
|
"heart-full-symbolic"
|
||||||
|
} else {
|
||||||
|
"heart-empty-symbolic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
async fn on_heart_button_clicked(&self) {
|
||||||
|
// TODO: disable button while await ongoing?? consider making star/unstar methods in
|
||||||
|
// the model class and add a semaphore there for serialization
|
||||||
|
// FIXME: doesn't update all if track is duplicated in the play queue
|
||||||
|
|
||||||
|
let song = self.obj().song().unwrap();
|
||||||
|
let client = self.obj().client().unwrap();
|
||||||
|
if song.starred() {
|
||||||
|
match client.unstar(&song.id()).await {
|
||||||
|
Ok(()) => song.set_starred(false),
|
||||||
|
Err(err) => event!(Level::ERROR, "could not unstar song {}: {err}", song.id()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match client.star(&song.id()).await {
|
||||||
|
Ok(()) => song.set_starred(true),
|
||||||
|
Err(err) => event!(Level::ERROR, "could not star song {}: {err}", song.id()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +234,7 @@ impl Song {
|
||||||
self.set_song(&song);
|
self.set_song(&song);
|
||||||
self.set_position(position);
|
self.set_position(position);
|
||||||
self.set_playlist_pos(window.playlist_pos());
|
self.set_playlist_pos(window.playlist_pos());
|
||||||
|
self.set_client(window.client().unwrap());
|
||||||
|
|
||||||
song.need_thumbnail(
|
song.need_thumbnail(
|
||||||
window.client().as_ref().unwrap(),
|
window.client().as_ref().unwrap(),
|
||||||
|
|
|
@ -43,6 +43,9 @@ mod imp {
|
||||||
|
|
||||||
#[property(get, set)]
|
#[property(get, set)]
|
||||||
_show_pulse_bar: Cell<bool>,
|
_show_pulse_bar: Cell<bool>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
client: RefCell<Option<crate::wrappers::Client>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -162,6 +165,34 @@ mod imp {
|
||||||
fn song_album(&self, song: Option<&Song>) -> String {
|
fn song_album(&self, song: Option<&Song>) -> String {
|
||||||
song.map(Song::album).unwrap_or_default()
|
song.map(Song::album).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn heart_button_icon_name(&self, starred: bool) -> &'static str {
|
||||||
|
if starred {
|
||||||
|
"heart-full-symbolic"
|
||||||
|
} else {
|
||||||
|
"heart-empty-symbolic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
async fn on_heart_button_clicked(&self) {
|
||||||
|
// TODO: disable button while await ongoing??
|
||||||
|
|
||||||
|
let song = self.obj().song().unwrap();
|
||||||
|
let client = self.obj().client().unwrap();
|
||||||
|
if song.starred() {
|
||||||
|
match client.unstar(&song.id()).await {
|
||||||
|
Ok(()) => song.set_starred(false),
|
||||||
|
Err(err) => event!(Level::ERROR, "could not unstar song {}: {err}", song.id()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match client.star(&song.id()).await {
|
||||||
|
Ok(()) => song.set_starred(true),
|
||||||
|
Err(err) => event!(Level::ERROR, "could not star song {}: {err}", song.id()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Playbar {
|
impl Drop for Playbar {
|
||||||
|
|
Loading…
Reference in a new issue