adding a remove from db function

This commit is contained in:
Chris Cochrun 2025-09-17 09:27:10 -05:00
parent de0722b430
commit e2406b5462
6 changed files with 245 additions and 29 deletions

View file

@ -168,6 +168,17 @@ impl Model<Image> {
} }
} }
pub async fn remove_from_db(
db: PoolConnection<Sqlite>,
id: i32,
) -> Result<()> {
query!("DELETE FROM images WHERE id = $1", id)
.execute(&mut db.detach())
.await
.into_diagnostic()
.map(|_| ())
}
pub async fn update_image_in_db( pub async fn update_image_in_db(
image: Image, image: Image,
db: PoolConnection<Sqlite>, db: PoolConnection<Sqlite>,

View file

@ -263,6 +263,17 @@ impl Model<Presentation> {
} }
} }
pub async fn remove_from_db(
db: PoolConnection<Sqlite>,
id: i32,
) -> Result<()> {
query!("DELETE FROM presentations WHERE id = $1", id)
.execute(&mut db.detach())
.await
.into_diagnostic()
.map(|_| ())
}
pub async fn update_presentation_in_db( pub async fn update_presentation_in_db(
presentation: Presentation, presentation: Presentation,
db: PoolConnection<Sqlite>, db: PoolConnection<Sqlite>,

View file

@ -181,7 +181,7 @@ impl TryFrom<PathBuf> for Background {
} }
} }
Err(e) => { Err(e) => {
error!("Couldn't canonicalize: {e} {:?}", path); // error!("Couldn't canonicalize: {e} {:?}", path);
Err(ParseError::CannotCanonicalize) Err(ParseError::CannotCanonicalize)
} }
} }

View file

@ -407,19 +407,17 @@ impl Model<Song> {
} }
} }
} }
}
pub async fn remove_from_db( pub async fn remove_from_db(
&mut self, db: PoolConnection<Sqlite>,
db: &mut SqlitePool,
id: i32, id: i32,
) -> Result<()> { ) -> Result<()> {
let db = db.acquire().await.expect("probs"); query!("DELETE FROM songs WHERE id = $1", id)
query!("delete from songs where id = $1", id)
.execute(&mut db.detach()) .execute(&mut db.detach())
.await .await
.into_diagnostic() .into_diagnostic()
.map(|_| ()) .map(|_| ())
}
} }
pub async fn update_song_in_db( pub async fn update_song_in_db(

View file

@ -11,10 +11,10 @@ use crisp::types::{Keyword, Symbol, Value};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{ use sqlx::{
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, pool::PoolConnection, query, query_as, Sqlite, SqliteConnection,
query, query_as, SqlitePool,
}; };
use std::path::PathBuf; use std::path::{Path, PathBuf};
use tracing::error; use tracing::error;
#[derive( #[derive(
@ -35,6 +35,25 @@ impl From<&Video> for Value {
} }
} }
impl From<PathBuf> for Video {
fn from(value: PathBuf) -> Self {
let title: String = value.file_name().map_or_else(|| "Video".into(), |filename| {
filename.to_str().unwrap_or("Video").into()
});
Self {
title,
path: value,
..Default::default()
}
}
}
impl From<&Path> for Video {
fn from(value: &Path) -> Self {
Self::from(value.to_owned())
}
}
impl Content for Video { impl Content for Video {
fn title(&self) -> String { fn title(&self) -> String {
self.title.clone() self.title.clone()
@ -205,6 +224,17 @@ impl Model<Video> {
} }
} }
pub async fn remove_from_db(
db: PoolConnection<Sqlite>,
id: i32,
) -> Result<()> {
query!("DELETE FROM videos WHERE id = $1", id)
.execute(&mut db.detach())
.await
.into_diagnostic()
.map(|_| ())
}
pub async fn update_video_in_db( pub async fn update_video_in_db(
video: Video, video: Video,
db: PoolConnection<Sqlite>, db: PoolConnection<Sqlite>,

View file

@ -1,34 +1,35 @@
use std::collections::HashMap; use std::collections::HashMap;
use cosmic::{ use cosmic::{
Element, Task,
iced::{ iced::{
Background, Border, Color, Length, alignment::Vertical, alignment::Vertical, clipboard::dnd::DndAction,
clipboard::dnd::DndAction, futures::FutureExt, futures::FutureExt, Background, Border, Color, Length,
}, },
iced_core::widget::tree::State, iced_core::widget::tree::State,
iced_widget::{column, row as rowm, text as textm}, iced_widget::{column, row as rowm, text as textm},
theme, theme,
widget::{ widget::{
Container, DndSource, Space, button, container, context_menu, button, container, context_menu, dnd_destination,
horizontal_space, icon, horizontal_space, icon,
menu::{self, Action as MenuAction}, menu::{self, Action as MenuAction},
mouse_area, responsive, row, scrollable, text, text_input, mouse_area, responsive, row, scrollable, text, text_input,
Container, DndSource, Space,
}, },
Element, Task,
}; };
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use rapidfuzz::distance::levenshtein; use rapidfuzz::distance::levenshtein;
use sqlx::{Sqlite, SqlitePool, pool::PoolConnection}; use sqlx::{pool::PoolConnection, Sqlite, SqlitePool};
use tracing::{debug, error, warn}; use tracing::{debug, error, warn};
use crate::core::{ use crate::core::{
content::Content, content::Content,
images::{Image, update_image_in_db}, images::{self, update_image_in_db, Image},
model::{LibraryKind, Model}, model::{LibraryKind, Model},
presentations::{Presentation, update_presentation_in_db}, presentations::{self, update_presentation_in_db, Presentation},
service_items::ServiceItem, service_items::ServiceItem,
songs::{Song, update_song_in_db}, songs::{self, update_song_in_db, Song},
videos::{Video, update_video_in_db}, videos::{self, update_video_in_db, Video},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -78,7 +79,7 @@ pub(crate) enum Action {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) enum Message { pub(crate) enum Message {
AddItem, AddItem(LibraryKind),
DeleteItem((LibraryKind, i32)), DeleteItem((LibraryKind, i32)),
OpenItem(Option<(LibraryKind, i32)>), OpenItem(Option<(LibraryKind, i32)>),
HoverLibrary(Option<LibraryKind>), HoverLibrary(Option<LibraryKind>),
@ -128,37 +129,171 @@ impl<'a> Library {
pub fn update(&'a mut self, message: Message) -> Action { pub fn update(&'a mut self, message: Message) -> Action {
match message { match message {
Message::AddItem => (), Message::AddItem(kind) => match kind {
LibraryKind::Song => todo!(),
LibraryKind::Video => {
let future = cosmic::dialog::file_chooser::open::Dialog::new().filter(cosmic::dialog::file_chooser::FileFilter::new("videos").extension("mp4").extension("mkv").extension("webm").extension(".mpeg")).open_file();
let task = Task::future(future).and_then(|r| {
if let Ok(video) = r
.url()
.to_file_path()
.and_then(|path| Ok(Video::from(path)))
{
self.video_library.add_item(video);
};
Task::none()
});
}
LibraryKind::Image => todo!(),
LibraryKind::Presentation => todo!(),
},
Message::None => (), Message::None => (),
Message::DeleteItem((kind, index)) => { Message::DeleteItem((kind, index)) => {
match kind { match kind {
LibraryKind::Song => { LibraryKind::Song => {
let Some(song) =
self.song_library.get_item(index)
else {
error!(
"There appears to not be a song here"
);
return Action::None;
};
let song = song.clone();
if let Err(e) = if let Err(e) =
self.song_library.remove_item(index) self.song_library.remove_item(index)
{ {
error!(?e); error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
songs::remove_from_db(
db, song.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
} }
} }
LibraryKind::Video => { LibraryKind::Video => {
let Some(video) =
self.video_library.get_item(index)
else {
error!(
"There appears to not be a video here"
);
return Action::None;
};
let video = video.clone();
if let Err(e) = if let Err(e) =
self.video_library.remove_item(index) self.video_library.remove_item(index)
{ {
error!(?e); error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
videos::remove_from_db(
db, video.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
} }
} }
LibraryKind::Image => { LibraryKind::Image => {
let Some(image) =
self.image_library.get_item(index)
else {
error!(
"There appears to not be a image here"
);
return Action::None;
};
let image = image.clone();
if let Err(e) = if let Err(e) =
self.image_library.remove_item(index) self.image_library.remove_item(index)
{ {
error!(?e); error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
images::remove_from_db(
db, image.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
} }
} }
LibraryKind::Presentation => { LibraryKind::Presentation => {
let Some(presentation) =
self.presentation_library.get_item(index)
else {
error!(
"There appears to not be a presentation here"
);
return Action::None;
};
let presentation = presentation.clone();
if let Err(e) = self if let Err(e) = self
.presentation_library .presentation_library
.remove_item(index) .remove_item(index)
{ {
error!(?e); error!(?e);
} else {
let task =
Task::future(self.db.acquire())
.and_then(move |db| {
Task::perform(
presentations::remove_from_db(
db,
presentation.id,
),
|r| {
match r {
Err(e) => {
error!(?e)
}
_ => (),
}
Message::None
},
)
});
return Action::Task(task);
} }
} }
}; };
@ -515,11 +650,42 @@ impl<'a> Library {
let library_toolbar = rowm!( let library_toolbar = rowm!(
text_input("Search...", ""), text_input("Search...", ""),
button::icon(icon::from_name("add")) button::icon(icon::from_name("add"))
.on_press(Message::AddItem(model.kind))
); );
let library_column = let library_column =
column![library_toolbar, items].spacing(3); column![library_toolbar, items].spacing(3);
let library_dnd = dnd_destination(
Container::new(library_column).padding(5) library_column,
vec![
"image/png".into(),
"image/jpg".into(),
"image/heif".into(),
"image/gif".into(),
"video/mp4".into(),
"video/AV1".into(),
"video/H264".into(),
"video/H265".into(),
"video/mpeg".into(),
"video/mkv".into(),
"video/webm".into(),
"video/ogg".into(),
"video/vnd.youtube.yt".into(),
"video/x-matroska".into(),
"application/pdf".into(),
"text/html".into(),
"text/md".into(),
"text/org".into(),
],
)
.on_enter(|x, y, mimes| {
warn!(?mimes);
Message::None
})
.on_finish(|mime, data, action, x, y| {
warn!(?mime, ?data, ?action);
Message::None
});
Container::new(library_dnd).padding(5)
} else { } else {
Container::new(Space::new(0, 0)) Container::new(Space::new(0, 0))
}; };