image adding and updating works now
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Chris Cochrun 2025-09-25 13:48:46 -05:00
parent b26fdd980b
commit 1896b9380d
5 changed files with 296 additions and 27 deletions

View file

@ -14,7 +14,7 @@ use sqlx::{
SqlitePool,
};
use std::path::{Path, PathBuf};
use tracing::error;
use tracing::{debug, error};
#[derive(
Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
@ -210,17 +210,65 @@ pub async fn update_image_in_db(
.to_str()
.map(std::string::ToString::to_string)
.unwrap_or_default();
query!(
let mut db = db.detach();
let id = image.id.clone();
if let Err(e) = query!("SELECT id FROM images where id = $1", id)
.fetch_one(&mut db)
.await
{
if let Ok(ids) =
query!("SELECT id FROM images").fetch_all(&mut db).await
{
let Some(mut max) = ids.iter().map(|r| r.id).max() else {
return Err(miette::miette!("cannot find max id"));
};
debug!(?e, "Image not found");
max += 1;
let result = query!(
r#"INSERT into images VALUES($1, $2, $3)"#,
max,
image.title,
path,
)
.execute(&mut db)
.await
.into_diagnostic();
return match result {
Ok(_) => {
debug!("should have been updated");
Ok(())
}
Err(e) => {
error! {?e};
Err(e)
}
};
} else {
return Err(miette::miette!("cannot find ids"));
}
};
debug!(?image, "should be been updated");
let result = query!(
r#"UPDATE images SET title = $2, file_path = $3 WHERE id = $1"#,
image.id,
image.title,
path,
)
.execute(&mut db.detach())
.await
.into_diagnostic()?;
.execute(&mut db)
.await.into_diagnostic();
Ok(())
match result {
Ok(_) => {
debug!("should have been updated");
Ok(())
}
Err(e) => {
error! {?e};
Err(e)
}
}
}
pub async fn get_image_from_db(

View file

@ -3,7 +3,6 @@ use core::service_items::ServiceItem;
use core::slide::{
Background, BackgroundKind, Slide, SlideBuilder, TextAlignment,
};
use core::songs::Song;
use cosmic::app::context_drawer::ContextDrawer;
use cosmic::app::{Core, Settings, Task};
use cosmic::iced::alignment::Vertical;
@ -49,6 +48,7 @@ use ui::song_editor::{self, SongEditor};
use ui::EditorMode;
use crate::core::kinds::ServiceItemKind;
use crate::ui::image_editor::{self, ImageEditor};
use crate::ui::text_svg::{self};
use crate::ui::video_editor::{self, VideoEditor};
use crate::ui::widgets::draggable;
@ -126,6 +126,7 @@ struct App {
editor_mode: Option<EditorMode>,
song_editor: SongEditor,
video_editor: VideoEditor,
image_editor: ImageEditor,
searching: bool,
search_query: String,
search_results: Vec<ServiceItem>,
@ -142,6 +143,7 @@ enum Message {
Library(library::Message),
SongEditor(song_editor::Message),
VideoEditor(video_editor::Message),
ImageEditor(image_editor::Message),
File(PathBuf),
OpenWindow,
CloseWindow(Option<window::Id>),
@ -328,6 +330,7 @@ impl cosmic::Application for App {
editor_mode: None,
song_editor,
video_editor: VideoEditor::new(),
image_editor: ImageEditor::new(),
searching: false,
search_results: vec![],
search_query: String::new(),
@ -767,6 +770,27 @@ impl cosmic::Application for App {
song_editor::Action::None => Task::none(),
}
}
Message::ImageEditor(message) => {
match self.image_editor.update(message) {
image_editor::Action::Task(task) => {
task.map(|m| {
cosmic::Action::App(Message::ImageEditor(
m,
))
})
}
image_editor::Action::UpdateImage(image) => {
if let Some(_) = &mut self.library {
self.update(Message::Library(
library::Message::UpdateImage(image),
))
} else {
Task::none()
}
}
image_editor::Action::None => Task::none(),
}
}
Message::VideoEditor(message) => {
match self.video_editor.update(message) {
video_editor::Action::Task(task) => {
@ -975,7 +999,14 @@ impl cosmic::Application for App {
let video = lib_video.to_owned();
return self.update(Message::VideoEditor(video_editor::Message::ChangeVideo(video)));
},
core::model::LibraryKind::Image => todo!(),
core::model::LibraryKind::Image => {
let Some(lib_image) = library.get_image(index) else {
return Task::none();
};
self.editor_mode = Some(kind.into());
let image = lib_image.to_owned();
return self.update(Message::ImageEditor(image_editor::Message::ChangeImage(image)));
},
core::model::LibraryKind::Presentation => todo!(),
}
}
@ -1314,7 +1345,9 @@ impl cosmic::Application for App {
EditorMode::Song => {
self.song_editor.view().map(Message::SongEditor)
}
EditorMode::Image => todo!(),
EditorMode::Image => {
self.image_editor.view().map(Message::ImageEditor)
}
EditorMode::Video => {
self.video_editor.view().map(Message::VideoEditor)
}

182
src/ui/image_editor.rs Normal file
View file

@ -0,0 +1,182 @@
use std::{io, path::PathBuf};
use crate::core::images::Image;
use cosmic::{
dialog::file_chooser::{open::Dialog, FileFilter},
iced::{alignment::Vertical, Length},
iced_widget::{column, row},
theme,
widget::{
self, button, container, horizontal_space, icon, text,
text_input, Space,
},
Element, Task,
};
use tracing::{debug, error, warn};
#[derive(Debug)]
pub struct ImageEditor {
pub image: Option<Image>,
title: String,
editing: bool,
}
pub enum Action {
Task(Task<Message>),
UpdateImage(Image),
None,
}
#[derive(Debug, Clone)]
pub enum Message {
ChangeImage(Image),
Update(Image),
ChangeTitle(String),
PickImage,
Edit(bool),
None,
}
impl ImageEditor {
pub fn new() -> Self {
Self {
image: None,
title: "Death was Arrested".to_string(),
editing: false,
}
}
pub fn update(&mut self, message: Message) -> Action {
match message {
Message::ChangeImage(image) => {
self.image = Some(image.clone());
self.title = image.title.clone();
return self.update(Message::Update(image));
}
Message::ChangeTitle(title) => {
self.title = title.clone();
if let Some(image) = &self.image {
let mut image = image.clone();
image.title = title;
return self.update(Message::Update(image));
}
}
Message::Edit(edit) => {
debug!(edit);
self.editing = edit;
}
Message::Update(image) => {
warn!(?image);
return Action::UpdateImage(image);
}
Message::PickImage => {
let image_id = self
.image
.as_ref()
.map(|v| v.id)
.unwrap_or_default()
.clone();
let task = Task::perform(
pick_image(),
move |image_result| {
if let Ok(image) = image_result {
let mut image = Image::from(image);
image.id = image_id;
Message::ChangeImage(image)
} else {
Message::None
}
},
);
return Action::Task(task);
}
Message::None => (),
}
Action::None
}
pub fn view(&self) -> Element<Message> {
let container = if let Some(pic) = &self.image {
let image = widget::image(pic.path.clone());
container(image)
} else {
container(Space::new(0, 0))
};
let column = column![
self.toolbar(),
container.center_x(Length::FillPortion(2))
]
.spacing(theme::active().cosmic().space_l());
column.into()
}
fn toolbar(&self) -> Element<Message> {
let title_box = text_input("Title...", &self.title)
.on_input(Message::ChangeTitle);
let image_selector = button::icon(
icon::from_name("folder-images-symbolic").scale(2),
)
.label("Image")
.tooltip("Select a image")
.on_press(Message::PickImage)
.padding(10);
row![
text::body("Title:"),
title_box,
horizontal_space(),
image_selector
]
.align_y(Vertical::Center)
.spacing(10)
.into()
}
pub const fn editing(&self) -> bool {
self.editing
}
}
impl Default for ImageEditor {
fn default() -> Self {
Self::new()
}
}
async fn pick_image() -> Result<PathBuf, ImageError> {
let dialog = Dialog::new().title("Choose a image...");
let bg_filter = FileFilter::new("Images")
.extension("png")
.extension("jpeg")
.extension("gif")
.extension("heic")
.extension("webp")
.extension("jpg");
dialog
.filter(bg_filter)
.directory(dirs::home_dir().expect("oops"))
.open_file()
.await
.map_err(|e| {
error!(?e);
ImageError::DialogClosed
})
.map(|file| file.url().to_file_path().unwrap())
// rfd::AsyncFileDialog::new()
// .set_title("Choose a background...")
// .add_filter(
// "Images and Images",
// &["png", "jpeg", "mp4", "webm", "mkv", "jpg", "mpeg"],
// )
// .set_directory(dirs::home_dir().unwrap())
// .pick_file()
// .await
// .ok_or(ImageError::BackgroundDialogClosed)
// .map(|file| file.path().to_owned())
}
#[derive(Debug, Clone)]
pub enum ImageError {
DialogClosed,
IOError(io::ErrorKind),
}

View file

@ -439,27 +439,28 @@ impl<'a> Library {
return Action::None;
};
match self
if self
.image_library
.update_item(image.clone(), index)
.is_err()
{
Ok(()) => {
return Action::Task(
Task::future(self.db.acquire()).and_then(
move |conn| {
Task::perform(
update_image_in_db(
image.clone(),
conn,
),
|_| Message::ImageChanged,
)
},
),
);
}
Err(_) => todo!(),
}
error!("Couldn't update image in model");
return Action::None;
};
return Action::Task(
Task::future(self.db.acquire()).and_then(
move |conn| {
Task::perform(
update_image_in_db(
image.to_owned(),
conn,
),
|_| Message::ImageChanged,
)
},
),
);
}
Message::ImageChanged => (),
Message::UpdateVideo(video) => {
@ -960,6 +961,10 @@ impl<'a> Library {
pub fn get_video(&self, index: i32) -> Option<&Video> {
self.video_library.get_item(index)
}
pub fn get_image(&self, index: i32) -> Option<&Image> {
self.image_library.get_item(index)
}
}
async fn add_images() -> Option<Vec<Image>> {

View file

@ -1,6 +1,7 @@
use crate::core::model::LibraryKind;
pub mod double_ended_slider;
pub mod image_editor;
pub mod library;
pub mod presenter;
pub mod service;