mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-25 20:37:24 +02:00
Implement deletion
This commit is contained in:
parent
b25d7fe8ee
commit
751dcde351
8 changed files with 340 additions and 40 deletions
39
data/ui/error_dialog.blp
Normal file
39
data/ui/error_dialog.blp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $MusicusErrorDialog: Adw.Dialog {
|
||||
content-width: 600;
|
||||
content-height: 400;
|
||||
|
||||
Adw.ToastOverlay toast_overlay {
|
||||
Adw.ToolbarView {
|
||||
[top]
|
||||
Adw.HeaderBar {
|
||||
title-widget: Adw.WindowTitle {
|
||||
title: _("Error");
|
||||
};
|
||||
|
||||
[end]
|
||||
Gtk.Button {
|
||||
icon-name: "edit-copy-symbolic";
|
||||
tooltip-text: _("Copy details to clipboard");
|
||||
clicked => $copy() swapped;
|
||||
|
||||
styles [
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Gtk.ScrolledWindow {
|
||||
Gtk.Label error_label {
|
||||
xalign: 0.0;
|
||||
margin-start: 12;
|
||||
margin-end: 12;
|
||||
margin-top: 12;
|
||||
margin-bottom: 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,30 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $MusicusWindow : Adw.ApplicationWindow {
|
||||
template $MusicusWindow: Adw.ApplicationWindow {
|
||||
title: _("Musicus");
|
||||
|
||||
Adw.ToolbarView {
|
||||
Gtk.Stack stack {
|
||||
transition-type: over_up_down;
|
||||
Adw.ToastOverlay toast_overlay {
|
||||
Adw.ToolbarView {
|
||||
Gtk.Stack stack {
|
||||
transition-type: over_up_down;
|
||||
|
||||
Gtk.StackPage {
|
||||
name: "navigation";
|
||||
child: Adw.NavigationView navigation_view {
|
||||
$MusicusWelcomePage {
|
||||
folder-selected => $set_library_folder() swapped;
|
||||
}
|
||||
};
|
||||
Gtk.StackPage {
|
||||
name: "navigation";
|
||||
|
||||
child: Adw.NavigationView navigation_view {
|
||||
$MusicusWelcomePage {
|
||||
folder-selected => $set_library_folder() swapped;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[bottom]
|
||||
Gtk.Revealer player_bar_revealer {
|
||||
reveal-child: true;
|
||||
transition-type: slide_up;
|
||||
}
|
||||
}
|
||||
|
||||
[bottom]
|
||||
Gtk.Revealer player_bar_revealer {
|
||||
reveal-child: true;
|
||||
transition-type: slide_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
use std::cell::OnceCell;
|
||||
|
||||
use adw::subclass::prelude::*;
|
||||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, Properties},
|
||||
glib::{self, clone, Properties},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::models::*, editor::album::AlbumEditor, library::Library, player::Player,
|
||||
playlist_item::PlaylistItem, recording_tile::RecordingTile,
|
||||
playlist_item::PlaylistItem, recording_tile::RecordingTile, util::error_dialog::ErrorDialog,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -19,6 +20,9 @@ mod imp {
|
|||
#[properties(wrapper_type = super::AlbumPage)]
|
||||
#[template(file = "data/ui/album_page.blp")]
|
||||
pub struct AlbumPage {
|
||||
#[property(get, construct_only)]
|
||||
pub toast_overlay: OnceCell<adw::ToastOverlay>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
|
||||
|
|
@ -90,10 +94,28 @@ mod imp {
|
|||
})
|
||||
.build();
|
||||
|
||||
// let obj = self.obj().to_owned();
|
||||
let obj = self.obj().to_owned();
|
||||
let delete_action = gio::ActionEntry::builder("delete")
|
||||
.activate(move |_, _, _| {
|
||||
log::error!("Delete not implemented");
|
||||
if let Err(err) = obj
|
||||
.library()
|
||||
.delete_album(&obj.imp().album.get().unwrap().album_id)
|
||||
{
|
||||
let toast = adw::Toast::builder()
|
||||
.title(&gettext("Failed to delete album"))
|
||||
.button_label("Details")
|
||||
.build();
|
||||
|
||||
toast.connect_button_clicked(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |_| {
|
||||
ErrorDialog::present(&err, &obj);
|
||||
}
|
||||
));
|
||||
|
||||
obj.toast_overlay().add_toast(toast);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
|
|
@ -115,12 +137,14 @@ glib::wrapper! {
|
|||
#[gtk::template_callbacks]
|
||||
impl AlbumPage {
|
||||
pub fn new(
|
||||
toast_overlay: &adw::ToastOverlay,
|
||||
navigation: &adw::NavigationView,
|
||||
library: &Library,
|
||||
player: &Player,
|
||||
album: Album,
|
||||
) -> Self {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("toast-overlay", toast_overlay)
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.property("player", player)
|
||||
|
|
|
|||
|
|
@ -879,6 +879,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_person(&self, person_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(persons::table)
|
||||
.filter(persons::person_id.eq(person_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_instrument(&self, name: TranslatedString) -> Result<Instrument> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
|
|
@ -921,6 +933,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_instrument(&self, instrument_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(instruments::table)
|
||||
.filter(instruments::instrument_id.eq(instrument_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_role(&self, name: TranslatedString) -> Result<Role> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
|
|
@ -962,6 +986,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_role(&self, role_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(roles::table)
|
||||
.filter(roles::role_id.eq(role_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_work(
|
||||
&self,
|
||||
name: TranslatedString,
|
||||
|
|
@ -1175,6 +1211,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_work(&self, work_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(works::table)
|
||||
.filter(works::work_id.eq(work_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_ensemble(&self, name: TranslatedString) -> Result<Ensemble> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
|
|
@ -1223,6 +1271,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_ensemble(&self, ensemble_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(ensembles::table)
|
||||
.filter(ensembles::ensemble_id.eq(ensemble_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_recording(
|
||||
&self,
|
||||
work: Work,
|
||||
|
|
@ -1345,6 +1405,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_recording(&self, recording_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(recordings::table)
|
||||
.filter(recordings::recording_id.eq(recording_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_album(
|
||||
&self,
|
||||
name: TranslatedString,
|
||||
|
|
@ -1427,6 +1499,18 @@ impl Library {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_album(&self, album_id: &str) -> Result<()> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
diesel::delete(albums::table)
|
||||
.filter(albums::album_id.eq(album_id))
|
||||
.execute(connection)?;
|
||||
|
||||
self.changed();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Import a track into the music library.
|
||||
// TODO: Support mediums.
|
||||
pub fn import_track(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use formatx::formatx;
|
|||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, Properties},
|
||||
glib::{self, clone, Properties},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
|
|
@ -25,6 +25,7 @@ use crate::{
|
|||
recording_tile::RecordingTile,
|
||||
search_tag::Tag,
|
||||
tag_tile::TagTile,
|
||||
util::error_dialog::ErrorDialog,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -34,6 +35,9 @@ mod imp {
|
|||
#[properties(wrapper_type = super::SearchPage)]
|
||||
#[template(file = "data/ui/search_page.blp")]
|
||||
pub struct SearchPage {
|
||||
#[property(get, construct_only)]
|
||||
pub toast_overlay: OnceCell<adw::ToastOverlay>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
|
||||
|
|
@ -162,12 +166,14 @@ glib::wrapper! {
|
|||
#[gtk::template_callbacks]
|
||||
impl SearchPage {
|
||||
pub fn new(
|
||||
toast_overlay: &adw::ToastOverlay,
|
||||
navigation: &adw::NavigationView,
|
||||
library: &Library,
|
||||
player: &Player,
|
||||
query: LibraryQuery,
|
||||
) -> Self {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("toast-overlay", toast_overlay)
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.property("player", player)
|
||||
|
|
@ -230,24 +236,82 @@ impl SearchPage {
|
|||
}
|
||||
|
||||
fn delete(&self) {
|
||||
log::warn!("Deletion not implemented");
|
||||
if let Some(highlight) = &*self.imp().highlight.borrow() {
|
||||
match highlight {
|
||||
Tag::Composer(person) | Tag::Performer(person) => {
|
||||
if let Err(err) = self.library().delete_person(&person.person_id) {
|
||||
let toast = adw::Toast::builder()
|
||||
.title(&gettext("Failed to delete person"))
|
||||
.button_label("Details")
|
||||
.build();
|
||||
|
||||
// if let Some(highlight) = &*self.imp().highlight.borrow() {
|
||||
// match highlight {
|
||||
// Tag::Composer(person) | Tag::Performer(person) => {
|
||||
// // TODO
|
||||
// }
|
||||
// Tag::Ensemble(ensemble) => {
|
||||
// // TODO
|
||||
// }
|
||||
// Tag::Instrument(instrument) => {
|
||||
// // TODO
|
||||
// }
|
||||
// Tag::Work(work) => {
|
||||
// // TODO
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
toast.connect_button_clicked(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
move |_| {
|
||||
ErrorDialog::present(&err, &obj);
|
||||
}
|
||||
));
|
||||
|
||||
self.toast_overlay().add_toast(toast);
|
||||
}
|
||||
}
|
||||
Tag::Ensemble(ensemble) => {
|
||||
if let Err(err) = self.library().delete_ensemble(&ensemble.ensemble_id) {
|
||||
let toast = adw::Toast::builder()
|
||||
.title(&gettext("Failed to delete ensemble"))
|
||||
.button_label("Details")
|
||||
.build();
|
||||
|
||||
toast.connect_button_clicked(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
move |_| {
|
||||
ErrorDialog::present(&err, &obj);
|
||||
}
|
||||
));
|
||||
|
||||
self.toast_overlay().add_toast(toast);
|
||||
}
|
||||
}
|
||||
Tag::Instrument(instrument) => {
|
||||
if let Err(err) = self.library().delete_instrument(&instrument.instrument_id) {
|
||||
let toast = adw::Toast::builder()
|
||||
.title(&gettext("Failed to delete instrument"))
|
||||
.button_label("Details")
|
||||
.build();
|
||||
|
||||
toast.connect_button_clicked(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
move |_| {
|
||||
ErrorDialog::present(&err, &obj);
|
||||
}
|
||||
));
|
||||
|
||||
self.toast_overlay().add_toast(toast);
|
||||
}
|
||||
}
|
||||
Tag::Work(work) => {
|
||||
if let Err(err) = self.library().delete_work(&work.work_id) {
|
||||
let toast = adw::Toast::builder()
|
||||
.title(&gettext("Failed to delete work"))
|
||||
.button_label("Details")
|
||||
.build();
|
||||
|
||||
toast.connect_button_clicked(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self,
|
||||
move |_| {
|
||||
ErrorDialog::present(&err, &obj);
|
||||
}
|
||||
));
|
||||
|
||||
self.toast_overlay().add_toast(toast);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
|
|
@ -297,6 +361,7 @@ impl SearchPage {
|
|||
|
||||
if query_changed {
|
||||
self.navigation().push(&SearchPage::new(
|
||||
&self.toast_overlay(),
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
&self.player(),
|
||||
|
|
@ -325,6 +390,7 @@ impl SearchPage {
|
|||
}
|
||||
|
||||
self.navigation().push(&SearchPage::new(
|
||||
&self.toast_overlay(),
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
&self.player(),
|
||||
|
|
@ -347,6 +413,7 @@ impl SearchPage {
|
|||
|
||||
fn show_album(&self, album: &Album) {
|
||||
self.navigation().push(&AlbumPage::new(
|
||||
&self.toast_overlay(),
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
&self.player(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
pub mod activatable_row;
|
||||
pub mod drag_widget;
|
||||
pub mod error_dialog;
|
||||
|
||||
use gtk::glib;
|
||||
use lazy_static::lazy_static;
|
||||
|
|
|
|||
79
src/util/error_dialog.rs
Normal file
79
src/util/error_dialog.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use std::cell::OnceCell;
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, Properties},
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||
#[properties(wrapper_type = super::ErrorDialog)]
|
||||
#[template(file = "data/ui/error_dialog.blp")]
|
||||
pub struct ErrorDialog {
|
||||
#[property(get, construct_only)]
|
||||
pub error_text: OnceCell<String>,
|
||||
|
||||
#[template_child]
|
||||
pub toast_overlay: TemplateChild<adw::ToastOverlay>,
|
||||
|
||||
#[template_child]
|
||||
pub error_label: TemplateChild<gtk::Label>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for ErrorDialog {
|
||||
const NAME: &'static str = "MusicusErrorDialog";
|
||||
type Type = super::ErrorDialog;
|
||||
type ParentType = adw::Dialog;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ErrorDialog {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
self.error_label.set_label(&self.obj().error_text());
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for ErrorDialog {}
|
||||
impl AdwDialogImpl for ErrorDialog {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ErrorDialog(ObjectSubclass<imp::ErrorDialog>)
|
||||
@extends gtk::Widget, adw::Dialog;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl ErrorDialog {
|
||||
pub fn present(err: &anyhow::Error, parent: &impl IsA<gtk::Widget>) {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("error-text", &format!("{err:?}"))
|
||||
.build();
|
||||
|
||||
obj.present(Some(parent));
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn copy(&self) {
|
||||
if let Some(display) = gdk::Display::default() {
|
||||
display.clipboard().set_text(&self.error_text());
|
||||
self.imp()
|
||||
.toast_overlay
|
||||
.add_toast(adw::Toast::new(&gettext("Copied to clipboard")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,8 @@ mod imp {
|
|||
pub player: Player,
|
||||
pub process_manager: ProcessManager,
|
||||
|
||||
#[template_child]
|
||||
pub toast_overlay: TemplateChild<adw::ToastOverlay>,
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
|
|
@ -242,6 +244,7 @@ impl Window {
|
|||
fn reset_view(&self) {
|
||||
let navigation = self.imp().navigation_view.get();
|
||||
navigation.replace(&[SearchPage::new(
|
||||
&self.imp().toast_overlay,
|
||||
&navigation,
|
||||
self.imp().library.borrow().as_ref().unwrap(),
|
||||
&self.imp().player,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue