mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Add empty page offering download
This commit is contained in:
parent
bf1ffef05a
commit
424c4c57a8
10 changed files with 295 additions and 9 deletions
173
src/empty_page.rs
Normal file
173
src/empty_page.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
use std::cell::OnceCell;
|
||||
|
||||
use adw::{
|
||||
prelude::*,
|
||||
subclass::{navigation_page::NavigationPageImpl, prelude::*},
|
||||
};
|
||||
use gettextrs::gettext;
|
||||
use glib::clone;
|
||||
use gtk::{gio, glib, glib::subclass::Signal};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
config, library::Library, process::Process, process_manager::ProcessManager,
|
||||
process_row::ProcessRow,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/empty_page.blp")]
|
||||
pub struct EmptyPage {
|
||||
pub library: OnceCell<Library>,
|
||||
pub process_manager: OnceCell<ProcessManager>,
|
||||
|
||||
#[template_child]
|
||||
pub download_button: TemplateChild<gtk::Button>,
|
||||
#[template_child]
|
||||
pub process_list: TemplateChild<gtk::ListBox>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for EmptyPage {
|
||||
const NAME: &'static str = "MusicusEmptyPage";
|
||||
type Type = super::EmptyPage;
|
||||
type ParentType = adw::NavigationPage;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for EmptyPage {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("ready").build()]);
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for EmptyPage {}
|
||||
impl NavigationPageImpl for EmptyPage {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct EmptyPage(ObjectSubclass<imp::EmptyPage>)
|
||||
@extends gtk::Widget, adw::NavigationPage;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl EmptyPage {
|
||||
pub fn new(library: &Library, process_manager: &ProcessManager) -> Self {
|
||||
let obj: Self = glib::Object::new();
|
||||
|
||||
for process in process_manager.processes() {
|
||||
obj.add_process(&process);
|
||||
}
|
||||
|
||||
obj.imp().library.set(library.to_owned()).unwrap();
|
||||
obj.imp()
|
||||
.process_manager
|
||||
.set(process_manager.to_owned())
|
||||
.unwrap();
|
||||
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn connect_ready<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("ready", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
async fn download_library(&self) {
|
||||
let dialog = adw::AlertDialog::builder()
|
||||
.heading(&gettext("Disclaimer"))
|
||||
.body(&gettext("You are about to download a library of audio files. These are from recordings that are in the public domain under EU law and are hosted on a server within the EU. Please ensure that you comply with the copyright laws of you country."))
|
||||
.build();
|
||||
|
||||
dialog.add_response("continue", &gettext("Continue"));
|
||||
dialog.set_response_appearance("continue", adw::ResponseAppearance::Suggested);
|
||||
dialog.add_response("cancel", &gettext("Cancel"));
|
||||
dialog.set_default_response(Some("cancel"));
|
||||
dialog.set_close_response("cancel");
|
||||
|
||||
let obj = self.to_owned();
|
||||
glib::spawn_future_local(async move {
|
||||
if dialog.choose_future(&obj).await == "continue" {
|
||||
obj.imp().download_button.set_visible(false);
|
||||
|
||||
let settings = gio::Settings::new(config::APP_ID);
|
||||
let url = if settings.boolean("use-custom-library-url") {
|
||||
settings.string("custom-library-url").to_string()
|
||||
} else {
|
||||
config::LIBRARY_URL.to_string()
|
||||
};
|
||||
|
||||
match obj.imp().library.get().unwrap().import_url(&url) {
|
||||
Ok(receiver) => {
|
||||
let process = Process::new(&gettext("Downloading music library"), receiver);
|
||||
|
||||
process.connect_finished_notify(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |process| {
|
||||
if process.finished() {
|
||||
if process.error().is_some() {
|
||||
obj.imp().download_button.set_visible(true);
|
||||
} else {
|
||||
obj.emit_by_name::<()>("ready", &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
obj.imp()
|
||||
.process_manager
|
||||
.get()
|
||||
.unwrap()
|
||||
.add_process(&process);
|
||||
|
||||
obj.add_process(&process);
|
||||
}
|
||||
Err(err) => log::error!("Failed to download library: {err:?}"),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn add_process(&self, process: &Process) {
|
||||
let row = ProcessRow::new(process);
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
move |row| {
|
||||
obj.imp()
|
||||
.process_manager
|
||||
.get()
|
||||
.unwrap()
|
||||
.remove_process(&row.process());
|
||||
|
||||
obj.imp().process_list.remove(row);
|
||||
|
||||
if obj.imp().process_list.first_child().is_none() {
|
||||
obj.imp().process_list.set_visible(false);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.imp().process_list.append(&row);
|
||||
self.imp().process_list.set_visible(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +77,16 @@ impl Library {
|
|||
Ok(obj)
|
||||
}
|
||||
|
||||
/// Whether this library is empty. The library is considered empty, if
|
||||
/// there are no tracks.
|
||||
pub fn is_empty(&self) -> Result<bool> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
Ok(tracks::table
|
||||
.first::<tables::Track>(connection)
|
||||
.optional()?
|
||||
.is_none())
|
||||
}
|
||||
|
||||
/// Import from a library archive.
|
||||
pub fn import_archive(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ mod application;
|
|||
mod config;
|
||||
mod db;
|
||||
mod editor;
|
||||
mod empty_page;
|
||||
mod library;
|
||||
mod library_manager;
|
||||
mod player;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ mod imp {
|
|||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/welcome_page.blp")]
|
||||
pub struct WelcomePage {
|
||||
#[template_child]
|
||||
pub status_page: TemplateChild<adw::StatusPage>,
|
||||
}
|
||||
pub struct WelcomePage {}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for WelcomePage {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use gtk::{gio, glib, glib::clone};
|
|||
use crate::{
|
||||
config,
|
||||
editor::tracks::TracksEditor,
|
||||
empty_page::EmptyPage,
|
||||
library::{Library, LibraryQuery},
|
||||
library_manager::LibraryManager,
|
||||
player::Player,
|
||||
|
|
@ -259,8 +260,29 @@ impl Window {
|
|||
));
|
||||
|
||||
self.imp().player.set_library(&library);
|
||||
|
||||
let is_empty = library.is_empty()?;
|
||||
self.imp().library.replace(Some(library));
|
||||
self.reset_view();
|
||||
|
||||
if is_empty {
|
||||
let navigation = self.imp().navigation_view.get();
|
||||
let empty_page = EmptyPage::new(
|
||||
self.imp().library.borrow().as_ref().unwrap(),
|
||||
&self.imp().process_manager,
|
||||
);
|
||||
|
||||
empty_page.connect_ready(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
move |_| {
|
||||
obj.reset_view();
|
||||
}
|
||||
));
|
||||
|
||||
navigation.replace(&[empty_page.into()]);
|
||||
} else {
|
||||
self.reset_view();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue