starting to make db universal across app

This commit is contained in:
Chris Cochrun 2025-03-04 11:13:40 -06:00
parent 804850505e
commit d1ae7ba4f5
6 changed files with 196 additions and 122 deletions

View file

@ -9,7 +9,7 @@ use super::{
use crisp::types::{Keyword, Symbol, Value};
use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{query_as, SqliteConnection};
use sqlx::{query_as, SqliteConnection, SqlitePool};
use std::path::PathBuf;
use tracing::error;
@ -125,13 +125,15 @@ impl ServiceTrait for Image {
}
impl Model<Image> {
pub async fn new_image_model(db: &mut SqliteConnection) -> Self {
pub async fn new_image_model(db: &mut SqlitePool) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Image,
};
model.load_from_db(db).await;
let mut db = db.acquire().await.expect("probs");
model.load_from_db(&mut db).await;
model
}

View file

@ -2,7 +2,8 @@ use crisp::types::{Keyword, Symbol, Value};
use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{
prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection,
prelude::FromRow, query, sqlite::SqliteRow, Row,
SqliteConnection, SqlitePool,
};
use std::path::PathBuf;
use tracing::error;
@ -166,15 +167,14 @@ impl FromRow<'_, SqliteRow> for Presentation {
}
impl Model<Presentation> {
pub async fn new_presentation_model(
db: &mut SqliteConnection,
) -> Self {
pub async fn new_presentation_model(db: &mut SqlitePool) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Presentation,
};
let mut db = db.acquire().await.expect("probs");
model.load_from_db(db).await;
model.load_from_db(&mut db).await;
model
}

View file

@ -6,6 +6,7 @@ use miette::{miette, IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{
query, sqlite::SqliteRow, FromRow, Row, SqliteConnection,
SqlitePool,
};
use tracing::{debug, error};
@ -380,25 +381,68 @@ impl Model<Song> {
index: i32,
db: &mut SqliteConnection,
) -> Result<()> {
self.update_item(item, index)?;
self.update_item(item.clone(), index)?;
let verse_order = {
if let Some(vo) = item.verse_order {
vo.into_iter()
.map(|mut s| {
s.push_str(" ");
s
})
.collect::<String>()
} else {
String::from("")
}
};
let audio = item
.audio
.map(|a| a.to_str().unwrap_or_default().to_string());
let background = item
.background
.map(|b| b.path.to_str().unwrap_or_default().to_string());
// let text_alignment = item.text_alignment.map(|ta| match ta {
// TextAlignment::TopLeft => todo!(),
// TextAlignment::TopCenter => todo!(),
// TextAlignment::TopRight => todo!(),
// TextAlignment::MiddleLeft => todo!(),
// TextAlignment::MiddleCenter => todo!(),
// TextAlignment::MiddleRight => todo!(),
// TextAlignment::BottomLeft => todo!(),
// TextAlignment::BottomCenter => todo!(),
// TextAlignment::BottomRight => todo!(),
// })
query!(
r#"UPDATE songs SET title = {} WHERE id = {}"#,
r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10 WHERE id = $1"#,
item.id,
item.title,
item.id
item.lyrics,
item.author,
item.ccli,
verse_order,
audio,
item.font,
item.font_size,
background
)
.fetch_one(db)
.await?;
.execute(db)
.await
.into_diagnostic()?;
Ok(())
}
pub async fn new_song_model(db: &mut SqliteConnection) -> Self {
pub async fn new_song_model(db: &mut SqlitePool) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Song,
};
let mut db = db.acquire().await.expect("probs");
model.load_from_db(db).await;
model.load_from_db(&mut db).await;
model
}

View file

@ -11,7 +11,7 @@ use cosmic::iced::Executor;
use crisp::types::{Keyword, Symbol, Value};
use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};
use sqlx::{query_as, SqliteConnection};
use sqlx::{query_as, SqliteConnection, SqlitePool};
use std::path::PathBuf;
use tracing::error;
@ -170,13 +170,14 @@ impl ServiceTrait for Video {
}
impl Model<Video> {
pub async fn new_video_model(db: &mut SqliteConnection) -> Self {
pub async fn new_video_model(db: &mut SqlitePool) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Video,
};
let mut db = db.acquire().await.expect("probs");
model.load_from_db(db).await;
model.load_from_db(&mut db).await;
model
}

View file

@ -1,5 +1,5 @@
use clap::{command, Parser};
use core::model::LibraryKind;
use core::model::{get_db, LibraryKind};
use core::service_items::{ServiceItem, ServiceItemModel};
use core::slide::*;
use cosmic::app::context_drawer::ContextDrawer;
@ -27,6 +27,7 @@ use cosmic::{widget::Container, Theme};
use crisp::types::Value;
use lisp::parse_lisp;
use miette::{miette, Result};
use sqlx::{SqliteConnection, SqlitePool};
use std::fs::read_to_string;
use std::path::PathBuf;
use tracing::{debug, level_filters::LevelFilter};
@ -417,60 +418,34 @@ impl cosmic::Application for App {
}
Message::SongEditor(message) => {
debug!(?message);
match message {
song_editor::Message::ChangeFont(ref font) => {
if let Some(mut song) =
let library_task = if let Some(mut song) =
self.song_editor.song.clone()
{
match message {
song_editor::Message::ChangeFont(
ref font,
) => {
song.font = Some(font.to_string());
self.song_editor.song =
Some(song.clone());
if let Some(library) = &mut self.library {
match library.update_song(song) {
Ok(_) => (),
Err(e) => error!(?e),
}
};
};
}
song_editor::Message::ChangeFontSize(
font_size,
) => {
if let Some(mut song) =
self.song_editor.song.clone()
{
song.font_size = Some(font_size as i32);
self.song_editor.song =
Some(song.clone());
if let Some(library) = &mut self.library {
match library.update_song(song) {
Ok(_) => (),
Err(e) => error!(?e),
}
};
};
}
song_editor::Message::ChangeTitle(ref title) => {
if let Some(mut song) =
self.song_editor.song.clone()
{
song_editor::Message::ChangeTitle(
ref title,
) => {
song.title = title.to_string();
self.song_editor.song =
Some(song.clone());
if let Some(library) = &mut self.library {
match library.update_song(song) {
Ok(_) => (),
Err(e) => error!(?e),
}
};
};
}
song_editor::Message::ChangeVerseOrder(
ref vo,
) => {
if let Some(mut song) =
self.song_editor.song.clone()
{
let verse_order = vo
.split(" ")
.into_iter()
@ -479,13 +454,6 @@ impl cosmic::Application for App {
song.verse_order = Some(verse_order);
self.song_editor.song =
Some(song.clone());
if let Some(library) = &mut self.library {
match library.update_song(song) {
Ok(_) => (),
Err(e) => error!(?e),
}
};
};
}
song_editor::Message::ChangeLyrics(
ref action,
@ -493,45 +461,42 @@ impl cosmic::Application for App {
self.song_editor
.lyrics
.perform(action.clone());
let lyrics = self.song_editor.lyrics.text();
if let Some(mut song) =
self.song_editor.song.clone()
{
let lyrics =
self.song_editor.lyrics.text();
song.lyrics = Some(lyrics.to_string());
self.song_editor.song =
Some(song.clone());
if let Some(library) = &mut self.library {
match library.update_song(song) {
Ok(_) => (),
Err(e) => error!(?e),
}
};
};
}
song_editor::Message::ChangeAuthor(
ref author,
) => {
if let Some(mut song) =
self.song_editor.song.clone()
{
song.author = Some(author.to_string());
self.song_editor.song =
Some(song.clone());
if let Some(library) = &mut self.library {
match library.update_song(song) {
Ok(_) => (),
Err(e) => error!(?e),
}
};
};
}
song_editor::Message::Edit(_) => todo!(),
_ => (),
};
if let Some(library) = &mut self.library {
let task = library.update(
library::Message::UpdateSong(song),
);
task.map(|m| {
cosmic::app::Message::App(Message::None)
})
} else {
Task::none()
}
} else {
Task::none()
};
let song_editor_task =
self.song_editor.update(message).map(|m| {
debug!(?m);
cosmic::app::Message::App(Message::None)
})
});
Task::batch(vec![song_editor_task, library_task])
}
Message::Present(message) => {
// debug!(?message);
@ -853,7 +818,7 @@ where
}
fn add_library(&mut self) -> Task<Message> {
Task::perform(async { Library::new().await }, |x| {
Task::perform(async move { Library::new().await }, |x| {
cosmic::app::Message::App(Message::AddLibrary(x))
})
}

View file

@ -1,21 +1,20 @@
use cosmic::{
iced::{alignment::Vertical, Background, Border, Length},
iced_core::widget::tree,
iced_widget::{column, row as rowm, text as textm},
theme,
widget::{
container, horizontal_space, icon, mouse_area, responsive,
row, scrollable, text, Container, DndSource, Space, Widget,
},
Element, Task,
};
use miette::{miette, Result};
use tracing::debug;
use miette::{miette, IntoDiagnostic, Result};
use sqlx::{SqliteConnection, SqlitePool};
use tracing::{debug, error};
use crate::core::{
content::Content,
images::Image,
model::{get_db, LibraryKind, Model},
model::{LibraryKind, Model},
presentations::Presentation,
service_items::ServiceItem,
songs::Song,
@ -34,6 +33,7 @@ pub(crate) struct Library {
hovered_item: Option<(LibraryKind, i32)>,
dragged_item: Option<(LibraryKind, i32)>,
editing_item: Option<(LibraryKind, i32)>,
db: SqlitePool,
}
#[derive(Clone, Debug)]
@ -45,12 +45,13 @@ pub(crate) enum Message {
OpenLibrary(Option<LibraryKind>),
HoverItem(Option<(LibraryKind, i32)>),
SelectItem(Option<(LibraryKind, i32)>),
UpdateSong(Song),
None,
}
impl Library {
impl<'a> Library {
pub async fn new() -> Self {
let mut db = get_db().await;
let mut db = add_db().await.expect("probs");
Self {
song_library: Model::new_song_model(&mut db).await,
image_library: Model::new_image_model(&mut db).await,
@ -65,6 +66,7 @@ impl Library {
hovered_item: None,
dragged_item: None,
editing_item: None,
db,
}
}
@ -72,7 +74,7 @@ impl Library {
self.song_library.get_item(index)
}
pub fn update(&mut self, message: Message) -> Task<Message> {
pub fn update(&'a mut self, message: Message) -> Task<Message> {
match message {
Message::AddItem => Task::none(),
Message::None => Task::none(),
@ -98,6 +100,33 @@ impl Library {
self.selected_item = item;
Task::none()
}
Message::UpdateSong(song) => {
let Some((kind, index)) = self.editing_item else {
error!("Not editing an item");
return Task::none();
};
if kind != LibraryKind::Song {
error!("Not editing a song item");
return Task::none();
}
let mut db = self.db.clone();
let mut song_library = self.song_library.clone();
let future = update_song(
song,
index as usize,
&mut db,
&mut song_library,
);
Task::perform(future, |r| {
match r {
Ok(_) => (),
Err(e) => error!(?e),
};
Message::None
})
}
}
}
@ -116,7 +145,7 @@ impl Library {
column.height(Length::Fill).spacing(5).into()
}
pub fn library_item<'a, T>(
pub fn library_item<T>(
&'a self,
model: &'a Model<T>,
) -> Element<'a, Message>
@ -257,7 +286,7 @@ impl Library {
column![button, lib_container].into()
}
fn single_item<'a, T>(
fn single_item<T>(
&'a self,
index: usize,
item: &'a T,
@ -342,7 +371,10 @@ impl Library {
.into()
}
pub(crate) fn update_song(&mut self, song: Song) -> Result<()> {
pub(crate) async fn update_song(
&'a mut self,
song: Song,
) -> Result<()> {
let Some((kind, index)) = self.editing_item else {
return Err(miette!("Not editing an item"));
};
@ -352,7 +384,11 @@ impl Library {
}
if let Some(_) = self.song_library.items.get(index as usize) {
self.song_library.update_item(song, index);
let mut db = self.db.acquire().await.expect("probs");
self.song_library
.update_song(song, index, &mut db)
.await?;
Ok(())
} else {
Err(miette!("Song not found"))
@ -360,6 +396,32 @@ impl Library {
}
}
async fn add_db() -> Result<SqlitePool> {
let mut data = dirs::data_local_dir().unwrap();
data.push("lumina");
data.push("library-db.sqlite3");
let mut db_url = String::from("sqlite://");
db_url.push_str(data.to_str().unwrap());
SqlitePool::connect(&db_url).await.into_diagnostic()
}
pub(crate) async fn update_song(
song: Song,
index: usize,
db: &mut SqlitePool,
song_library: &mut Model<Song>,
) -> Result<()> {
if let Some(_) = song_library.items.get(index) {
let mut db = db.acquire().await.expect("foo");
song_library
.update_song(song, index as i32, &mut db)
.await?;
Ok(())
} else {
Err(miette!("Song not found"))
}
}
fn elide_text(text: String, width: f32) -> String {
const CHAR_SIZE: f32 = 8.0;
let text_length = text.len() as f32 * CHAR_SIZE;