getting some presentation editor changes ready
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Chris Cochrun 2025-09-25 14:15:57 -05:00
parent 1896b9380d
commit 51c184fe37
4 changed files with 284 additions and 6 deletions

View file

@ -7,7 +7,7 @@ use sqlx::{
pool::PoolConnection, prelude::FromRow, query, sqlite::SqliteRow,
Row, Sqlite, SqliteConnection, SqlitePool,
};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use tracing::{debug, error};
use crate::{Background, Slide, SlideBuilder, TextAlignment};
@ -48,6 +48,39 @@ impl PartialEq for Presentation {
}
}
impl From<PathBuf> for Presentation {
fn from(value: PathBuf) -> Self {
let kind = match value
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
{
"pdf" => PresKind::Pdf,
"html" => PresKind::Html,
_ => PresKind::Generic,
};
let title = value
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
.to_string();
Self {
id: 0,
title,
path: value.canonicalize().unwrap_or(value),
kind,
}
}
}
impl From<&Path> for Presentation {
fn from(value: &Path) -> Self {
Self::from(value.to_owned())
}
}
impl From<&Presentation> for Value {
fn from(value: &Presentation) -> Self {
Self::List(vec![Self::Symbol(Symbol("presentation".into()))])
@ -284,18 +317,69 @@ pub async fn update_presentation_in_db(
.map(std::string::ToString::to_string)
.unwrap_or_default();
let html = presentation.kind == PresKind::Html;
query!(
let mut db = db.detach();
let id = presentation.id.clone();
if let Err(e) =
query!("SELECT id FROM presentations where id = $1", id)
.fetch_one(&mut db)
.await
{
if let Ok(ids) = query!("SELECT id FROM presentations")
.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, "Presentation not found");
max += 1;
let result = query!(
r#"INSERT into presentations VALUES($1, $2, $3, $4)"#,
max,
presentation.title,
path,
html,
)
.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!(?presentation, "should be been updated");
let result = query!(
r#"UPDATE presentations SET title = $2, file_path = $3, html = $4 WHERE id = $1"#,
presentation.id,
presentation.title,
path,
html
)
.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_presentation_from_db(

View file

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

View file

@ -0,0 +1,190 @@
use std::{io, path::PathBuf};
use crate::core::{
presentations::Presentation, service_items::ServiceTrait,
slide::Slide,
};
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 PresentationEditor {
pub presentation: Option<Presentation>,
slides: Option<Vec<Slide>>,
title: String,
editing: bool,
}
pub enum Action {
Task(Task<Message>),
UpdatePresentation(Presentation),
None,
}
#[derive(Debug, Clone)]
pub enum Message {
ChangePresentation(Presentation),
Update(Presentation),
ChangeTitle(String),
PickPresentation,
Edit(bool),
None,
}
impl PresentationEditor {
pub fn new() -> Self {
Self {
presentation: None,
slides: None,
title: "Death was Arrested".to_string(),
editing: false,
}
}
pub fn update(&mut self, message: Message) -> Action {
match message {
Message::ChangePresentation(presentation) => {
self.presentation = Some(presentation.clone());
self.title = presentation.title.clone();
let Ok(slides) = presentation.to_slides() else {
return Action::None;
};
self.slides = Some(slides);
return self.update(Message::Update(presentation));
}
Message::ChangeTitle(title) => {
self.title = title.clone();
if let Some(presentation) = &self.presentation {
let mut presentation = presentation.clone();
presentation.title = title;
return self
.update(Message::Update(presentation));
}
}
Message::Edit(edit) => {
debug!(edit);
self.editing = edit;
}
Message::Update(presentation) => {
warn!(?presentation);
return Action::UpdatePresentation(presentation);
}
Message::PickPresentation => {
let presentation_id = self
.presentation
.as_ref()
.map(|v| v.id)
.unwrap_or_default()
.clone();
let task = Task::perform(
pick_presentation(),
move |presentation_result| {
if let Ok(presentation) = presentation_result
{
let mut presentation =
Presentation::from(presentation);
presentation.id = presentation_id;
Message::ChangePresentation(presentation)
} else {
Message::None
}
},
);
return Action::Task(task);
}
Message::None => (),
}
Action::None
}
pub fn view(&self) -> Element<Message> {
let container = if let Some(slides) = &self.slides {
todo!();
// container(presentation)
} 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 presentation_selector = button::icon(
icon::from_name("folder-presentations-symbolic").scale(2),
)
.label("Presentation")
.tooltip("Select a presentation")
.on_press(Message::PickPresentation)
.padding(10);
row![
text::body("Title:"),
title_box,
horizontal_space(),
presentation_selector
]
.align_y(Vertical::Center)
.spacing(10)
.into()
}
pub const fn editing(&self) -> bool {
self.editing
}
}
impl Default for PresentationEditor {
fn default() -> Self {
Self::new()
}
}
async fn pick_presentation() -> Result<PathBuf, PresentationError> {
let dialog = Dialog::new().title("Choose a presentation...");
let bg_filter = FileFilter::new("Presentations")
.extension("pdf")
.extension("html");
dialog
.filter(bg_filter)
.directory(dirs::home_dir().expect("oops"))
.open_file()
.await
.map_err(|e| {
error!(?e);
PresentationError::DialogClosed
})
.map(|file| file.url().to_file_path().unwrap())
// rfd::AsyncFileDialog::new()
// .set_title("Choose a background...")
// .add_filter(
// "Presentations and Presentations",
// &["png", "jpeg", "mp4", "webm", "mkv", "jpg", "mpeg"],
// )
// .set_directory(dirs::home_dir().unwrap())
// .pick_file()
// .await
// .ok_or(PresentationError::BackgroundDialogClosed)
// .map(|file| file.path().to_owned())
}
#[derive(Debug, Clone)]
pub enum PresentationError {
DialogClosed,
IOError(io::ErrorKind),
}