Async images
This commit is contained in:
parent
97a1d198cb
commit
f4447a2648
|
@ -323,6 +323,7 @@ dependencies = [
|
||||||
"dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"reqwest 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"reqwest 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -9,6 +9,7 @@ cairo-rs = "0.5"
|
||||||
dirs = "1.0"
|
dirs = "1.0"
|
||||||
gdk = "0.9"
|
gdk = "0.9"
|
||||||
gdk-pixbuf = "0.5"
|
gdk-pixbuf = "0.5"
|
||||||
|
glib = "0.6"
|
||||||
gtk = { version = "0.5", features = [ "v3_22" ] }
|
gtk = { version = "0.5", features = [ "v3_22" ] }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
|
|
@ -57,6 +57,7 @@ pub struct ArtistAlbum {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub tracks_count: i32,
|
pub tracks_count: i32,
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
pub cover: Image,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
|
|
@ -93,7 +93,9 @@ fn main() {
|
||||||
let login_page = ui::login_page::render(state.clone());
|
let login_page = ui::login_page::render(state.clone());
|
||||||
|
|
||||||
state.borrow().stack.add_named(&login_page, "login");
|
state.borrow().stack.add_named(&login_page, "login");
|
||||||
window.add(&state.borrow().stack);
|
let scrolled = ScrolledWindow::new(None, None);
|
||||||
|
scrolled.add(&state.borrow().stack);
|
||||||
|
window.add(&scrolled);
|
||||||
window.show_all();
|
window.show_all();
|
||||||
|
|
||||||
if state.borrow().instance.is_some() && state.borrow().token.is_some() {
|
if state.borrow().instance.is_some() && state.borrow().token.is_some() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use gtk::*;
|
use gtk::*;
|
||||||
use std::{fs, thread};
|
use std::{fs, thread};
|
||||||
use crate::{Download, State, api};
|
use crate::{Download, State, api, ui::network_image::NetworkImage};
|
||||||
|
|
||||||
pub struct Card<T: CardModel> {
|
pub struct Card<T: CardModel> {
|
||||||
model: T,
|
model: T,
|
||||||
|
@ -17,8 +17,12 @@ impl<T: 'static> Card<T> where T: CardModel {
|
||||||
|
|
||||||
pub fn render(&self) -> Grid {
|
pub fn render(&self) -> Grid {
|
||||||
let card = Grid::new();
|
let card = Grid::new();
|
||||||
|
card.set_column_spacing(12);
|
||||||
|
card.set_valign(Align::Start);
|
||||||
|
|
||||||
if let Some(url) = self.model.image_url() {
|
if let Some(url) = self.model.image_url() {
|
||||||
// TODO
|
let img = NetworkImage::new(format!("https://{}{}", self.state.borrow().instance.clone().unwrap(), url));
|
||||||
|
card.attach(&*img.img.borrow(), 0, 0, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let main_text = Label::new(self.model.text().as_ref());
|
let main_text = Label::new(self.model.text().as_ref());
|
||||||
|
@ -30,11 +34,22 @@ impl<T: 'static> Card<T> where T: CardModel {
|
||||||
sub_text.set_hexpand(true);
|
sub_text.set_hexpand(true);
|
||||||
sub_text.set_halign(Align::Start);
|
sub_text.set_halign(Align::Start);
|
||||||
|
|
||||||
let dl_bt = Button::new_from_icon_name("go-down", 32);
|
let dl_bt = Button::new_with_label("Download");
|
||||||
dl_bt.set_label("Download");
|
dl_bt.set_valign(Align::Center);
|
||||||
|
dl_bt.set_vexpand(true);
|
||||||
|
dl_bt.get_style_context().map(|c| c.add_class("suggested-action"));
|
||||||
|
|
||||||
|
let dl_list = self.model.downloads(self.state.clone());
|
||||||
|
if dl_list.len() > 1 { // Not only one song
|
||||||
|
let more_bt = Button::new_with_label("Details");
|
||||||
|
more_bt.set_valign(Align::Center);
|
||||||
|
more_bt.set_vexpand(true);
|
||||||
|
card.attach(&more_bt, 2, 0, 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let model = self.model.clone();
|
let model = self.model.clone();
|
||||||
dl_bt.connect_clicked(move |_| {
|
dl_bt.connect_clicked(clone!(state, model => move |_| {
|
||||||
let downloads = state.borrow().downloads.clone();
|
let downloads = state.borrow().downloads.clone();
|
||||||
for dl in model.downloads(state.clone()) {
|
for dl in model.downloads(state.clone()) {
|
||||||
let token = state.borrow().token.clone().unwrap_or_default();
|
let token = state.borrow().token.clone().unwrap_or_default();
|
||||||
|
@ -53,11 +68,11 @@ impl<T: 'static> Card<T> where T: CardModel {
|
||||||
println!("saved {:?}", dl.output);
|
println!("saved {:?}", dl.output);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
card.attach(&main_text, 0, 0, 1, 1);
|
card.attach(&main_text, 1, 0, 1, 1);
|
||||||
card.attach(&sub_text, 0, 1, 1, 1);
|
card.attach(&sub_text, 1, 1, 1, 1);
|
||||||
card.attach(&dl_bt, 1, 0, 2, 1);
|
card.attach(&dl_bt, 3, 0, 1, 2);
|
||||||
|
|
||||||
card
|
card
|
||||||
}
|
}
|
||||||
|
@ -84,6 +99,12 @@ impl CardModel for api::Artist {
|
||||||
format!("{} albums", self.albums.clone().unwrap().len())
|
format!("{} albums", self.albums.clone().unwrap().len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn image_url(&self) -> Option<String> {
|
||||||
|
self.albums.clone()?.iter()
|
||||||
|
.next()
|
||||||
|
.and_then(|album| album.cover.medium_square_crop.clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn downloads(&self, state: State) -> Vec<Download> {
|
fn downloads(&self, state: State) -> Vec<Download> {
|
||||||
let mut dls = vec![];
|
let mut dls = vec![];
|
||||||
for album in self.albums.clone().unwrap_or_default() {
|
for album in self.albums.clone().unwrap_or_default() {
|
||||||
|
@ -120,6 +141,10 @@ impl CardModel for api::Album {
|
||||||
format!("{} tracks, by {}", self.tracks.clone().map(|t| t.len()).unwrap_or_default(), self.artist.name)
|
format!("{} tracks, by {}", self.tracks.clone().map(|t| t.len()).unwrap_or_default(), self.artist.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn image_url(&self) -> Option<String> {
|
||||||
|
self.cover.medium_square_crop.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn downloads(&self, state: State) -> Vec<Download> {
|
fn downloads(&self, state: State) -> Vec<Download> {
|
||||||
self.tracks.clone().unwrap_or_default().iter().filter_map(|track|
|
self.tracks.clone().unwrap_or_default().iter().filter_map(|track|
|
||||||
api::Upload::get_for_track(track.id, state.borrow().instance.clone().unwrap(), state.borrow().token.clone().unwrap()).map(|u| Download {
|
api::Upload::get_for_track(track.id, state.borrow().instance.clone().unwrap(), state.borrow().token.clone().unwrap()).map(|u| Download {
|
||||||
|
@ -140,6 +165,10 @@ impl CardModel for api::Track {
|
||||||
format!("By {}, in {}", self.artist.name, self.album.title)
|
format!("By {}, in {}", self.artist.name, self.album.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn image_url(&self) -> Option<String> {
|
||||||
|
self.album.cover.medium_square_crop.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn downloads(&self, state: State) -> Vec<Download> {
|
fn downloads(&self, state: State) -> Vec<Download> {
|
||||||
println!("yoy");
|
println!("yoy");
|
||||||
let upload = match api::Upload::get_for_track(self.id, state.borrow().instance.clone().unwrap(), state.borrow().token.clone().unwrap()) {
|
let upload = match api::Upload::get_for_track(self.id, state.borrow().instance.clone().unwrap(), state.borrow().token.clone().unwrap()) {
|
||||||
|
|
|
@ -82,6 +82,7 @@ pub fn render(state: State) -> gtk::Box {
|
||||||
cont.add(&search);
|
cont.add(&search);
|
||||||
|
|
||||||
let results = gtk::Box::new(Orientation::Vertical, 12);
|
let results = gtk::Box::new(Orientation::Vertical, 12);
|
||||||
|
results.set_valign(Align::Start);
|
||||||
cont.add(&results);
|
cont.add(&results);
|
||||||
|
|
||||||
let widgets = Rc::new(RefCell::new(
|
let widgets = Rc::new(RefCell::new(
|
||||||
|
|
|
@ -3,6 +3,7 @@ use gtk::prelude::*;
|
||||||
pub mod card;
|
pub mod card;
|
||||||
pub mod login_page;
|
pub mod login_page;
|
||||||
pub mod main_page;
|
pub mod main_page;
|
||||||
|
pub mod network_image;
|
||||||
|
|
||||||
fn title(text: &str) -> gtk::Label {
|
fn title(text: &str) -> gtk::Label {
|
||||||
let lbl = gtk::Label::new(text);
|
let lbl = gtk::Label::new(text);
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
use gtk::*;
|
||||||
|
use std::{
|
||||||
|
sync::mpsc, cell::RefCell,
|
||||||
|
fs, thread, rc::Rc, time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct NetworkImage {
|
||||||
|
pub img: Rc<RefCell<Image>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkImage {
|
||||||
|
pub fn new(url: String) -> NetworkImage {
|
||||||
|
let image = Rc::new(RefCell::new(
|
||||||
|
Image::new_from_icon_name("image-loading", 4)
|
||||||
|
));
|
||||||
|
let dest_file = url.split("/media/").last().unwrap().replace('/', "-");
|
||||||
|
let dest = dirs::cache_dir().unwrap().join(env!("CARGO_PKG_NAME")).join(dest_file.to_string());
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
thread::spawn(clone!(dest => move || {
|
||||||
|
fs::create_dir_all(dest.parent().unwrap()).unwrap();
|
||||||
|
let mut file = fs::File::create(dest.clone()).unwrap(); // TODO: check if it exists
|
||||||
|
|
||||||
|
reqwest::Client::new()
|
||||||
|
.get(&url)
|
||||||
|
.send()
|
||||||
|
.unwrap()
|
||||||
|
.copy_to(&mut file)
|
||||||
|
.unwrap();
|
||||||
|
tx.send(dest).unwrap();
|
||||||
|
}));
|
||||||
|
gtk::idle_add(clone!(image => move || { // Check every 0.5s
|
||||||
|
match rx.recv_timeout(Duration::from_millis(500)) {
|
||||||
|
Err(_) => glib::Continue(true),
|
||||||
|
Ok(res) => {
|
||||||
|
let pb = gdk_pixbuf::Pixbuf::new_from_file_at_scale(res, 64, 64, true).unwrap();
|
||||||
|
image.borrow().set_from_pixbuf(&pb);
|
||||||
|
glib::Continue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
NetworkImage {
|
||||||
|
img: image.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue