From 1d19830cd862aed30596a4b90f36eb552319bac8 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Fri, 29 May 2026 11:47:33 +0200 Subject: [PATCH] separate note_content from note --- application/src/backend.rs | 51 +++++++++++++++++-- application/src/note.rs | 22 ++++++-- application/src/notebook.rs | 22 +++++++- application/src/window.rs | 9 +--- entity/src/lib.rs | 3 +- entity/src/note.rs | 8 +-- entity/src/notecontent.rs | 21 ++++++++ .../src/m20260502_184134_create_settings.rs | 24 ++++++++- ui/common/context.c | 1 + 9 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 entity/src/notecontent.rs diff --git a/application/src/backend.rs b/application/src/backend.rs index 8469c36..500dcd9 100644 --- a/application/src/backend.rs +++ b/application/src/backend.rs @@ -8,10 +8,11 @@ use tokio::sync::{broadcast, mpsc}; use migration::{Migrator, MigratorTrait}; use ui_rs::ui; -use entity::{collection, note, profile}; +use entity::{collection, note, notecontent, profile}; use entity::profile::Entity as Profile; use entity::collection::{create_notebook_hierarchy, CollectionType, Entity as Collection, Node}; use entity::note::{Entity as Note}; +use entity::notecontent::{Entity as NoteContent}; pub struct Backend { rt: Arc, @@ -296,13 +297,55 @@ impl BackendHandle { let _ = self.tx.send(cmd); } - pub fn save_note(&self, note: entity::note::ActiveModel, callback: F) - where F: FnOnce(Result) + Send + 'static { + pub fn get_note_content(&self, note_id: i32, callback: F) + where F: FnOnce(Result, DbErr>) + Send + 'static { let backend = self.backend.clone(); let cmd = Box::pin(async move { - let result = note.save(&backend.db).await; + let result = NoteContent::find() + .filter(notecontent::Column::NoteId.eq(note_id)) + .order_by_id_asc().one(&backend.db).await; callback(result); }); let _ = self.tx.send(cmd); } + + pub fn save_note(&self, note: note::ActiveModel, content: Option, callback: F) + where F: FnOnce(Result<(i32, i32), DbErr>) + Send + 'static { + let backend = self.backend.clone(); + let cmd = Box::pin(async move { + let result = if note.note_id.is_set() { + note.update(&backend.db).await + } else { + note.insert(&backend.db).await + }; + + match result { + Ok(note) => { + let note_id = note.note_id; + if let Some(content) = content { + let result2 = if content.id.is_set() { + content.update(&backend.db).await + } else { + content.insert(&backend.db).await + }; + + match result2 { + Ok(ctn) => { + callback(Ok((note_id, ctn.id))); + } + Err(e) => { + callback(Err(e)); + } + } + } else { + callback(Ok((note_id, 0))); + } + }, + Err(e) => { + callback(Err(e)); + } + } + }); + let _ = self.tx.send(cmd); + } } diff --git a/application/src/note.rs b/application/src/note.rs index 4a9bd42..e529895 100644 --- a/application/src/note.rs +++ b/application/src/note.rs @@ -10,6 +10,7 @@ use crate::window::NoteTypeTabView; #[derive(UiModel, Default)] pub struct Note { pub note_id: i32, + pub content_id: i32, pub kind: NoteType, pub created: DateTimeWithTimeZone, @@ -25,7 +26,6 @@ pub struct Note { impl Note { pub fn init_from_model(&mut self, model: &entity::note::Model) { self.note_id = model.note_id; - self.text.set(model.content.as_str()); let tab = match model.kind { NoteType::PlainTextNote => NoteTypeTabView::TextArea, @@ -33,6 +33,11 @@ impl Note { }; self.note_type.set(tab as i64); } + + pub fn init_content(&mut self, content: &entity::notecontent::Model) { + self.content_id = content.id; + self.text.set(content.content.as_str()); + } pub fn save(&self, collection_id: i32, backend: &BackendHandle) { let content = self.text.get(); @@ -41,19 +46,26 @@ impl Note { None => "Note".to_string() }; + let note_id = if self.note_id == 0 { NotSet } else { Set(self.note_id) }; + let note = entity::note::ActiveModel { - note_id: if self.note_id == 0 { NotSet } else { Set(self.note_id) }, + note_id: note_id.clone(), collection_id: Set(collection_id), kind: Set(self.kind.clone()), title: Set(title), - content: Set(content), lastmodified: Set(Utc::now().into()), created: if self.note_id == 0 { Set(Utc::now().into()) } else { NotSet } }; - backend.save_note(note, |result|{ + let notecontent = entity::notecontent::ActiveModel { + id: if self.content_id == 0 { NotSet } else { Set(self.content_id) }, + note_id: note_id, + content: Set(content), + }; + + backend.save_note(note, Some(notecontent), |result|{ match result { - Ok(model) => { + Ok(_model) => { println!("Note saved"); }, Err(error) => { diff --git a/application/src/notebook.rs b/application/src/notebook.rs index eb1249d..366590b 100644 --- a/application/src/notebook.rs +++ b/application/src/notebook.rs @@ -49,7 +49,7 @@ impl Notebook { let note_proxy = current.doc_proxy(); let backend = self.backend.clone(); let collection_id = self.collection_id; - note_proxy.call_mainthread(move |doc, note|{ + note_proxy.call_mainthread(move |_doc, note|{ note.save(collection_id, &backend); }); @@ -75,6 +75,24 @@ impl Notebook { } n.init_from_model(¬e); + + let proxy = d.doc_proxy(); + self.backend.get_note_content(note.note_id, move|result|{ + proxy.call_mainthread(|doc, note|{ + match result { + Ok(content) => { + if let Some(content) = content { + note.init_content(&content); + } else { + println!("note {}: no content", note.note_id); + } + }, + Err(e) => { + println!("Cannot get note content: {}", e); + } + } + }); + }); }); // Attach the new note @@ -87,7 +105,7 @@ impl Notebook { } } -pub fn note_getvalue<'a>(elm: &Note, col: i32, _row: i32) -> ListValue<'a> { +pub fn notelist_getvalue<'a>(elm: &Note, col: i32, _row: i32) -> ListValue<'a> { match col { 0 => ListValue::String(elm.title.clone()), 1 => ListValue::String(elm.lastmodified.to_string()), diff --git a/application/src/window.rs b/application/src/window.rs index a3629ca..2a7436f 100644 --- a/application/src/window.rs +++ b/application/src/window.rs @@ -33,7 +33,7 @@ use entity::collection::{Model as Collection, Node}; use entity::note::Model as Note; use crate::backend::{BackendHandle, BroadcastMessage}; use crate::newnotebook::new_notebook_dialog; -use crate::notebook::{note_getvalue, Notebook}; +use crate::notebook::{notelist_getvalue, Notebook}; #[derive(UiModel)] pub struct MainWindow { @@ -49,11 +49,6 @@ pub struct MainWindow { notebooks: UiSourceList } -impl Drop for MainWindow { - fn drop(&mut self) { - println!("MainWindow dropped!"); - } -} #[ui_actions] impl MainWindow { @@ -173,7 +168,7 @@ pub fn create_window(app: &App, ctx: &AppContext) -> UiObject } #[derive(EnumIter, DeriveActiveEnum, Clone, Debug, PartialEq, Default)] @@ -24,7 +27,4 @@ pub enum NoteType { MDTextNote = 1, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - impl ActiveModelBehavior for ActiveModel {} diff --git a/entity/src/notecontent.rs b/entity/src/notecontent.rs new file mode 100644 index 0000000..0396da1 --- /dev/null +++ b/entity/src/notecontent.rs @@ -0,0 +1,21 @@ +use sea_orm::entity::prelude::*; + +use sea_orm::{ActiveModelBehavior, DeriveEntityModel, DeriveRelation, EnumIter}; + +#[sea_orm::model] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "note_content")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + + #[sea_orm(unique)] + pub note_id: i32, + + pub content: String, + + #[sea_orm(belongs_to, from = "note_id", to = "note_id")] + pub note: HasOne +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/migration/src/m20260502_184134_create_settings.rs b/migration/src/m20260502_184134_create_settings.rs index 1d997c1..1001304 100644 --- a/migration/src/m20260502_184134_create_settings.rs +++ b/migration/src/m20260502_184134_create_settings.rs @@ -50,14 +50,34 @@ impl MigrationTrait for Migration { .col(integer("collection_id")) .col(integer("kind")) .col(string("title")) - .col(string_null("content")) .col(timestamp_with_time_zone("lastmodified")) .col(timestamp_with_time_zone("created")) .to_owned() - ).await + ).await?; + + manager + .create_table( + Table::create() + .table("note_content") + .if_not_exists() + .col(integer("id").primary_key()) + .col(integer("note_id").unique_key()) + .col(string("content")) + .foreign_key( + ForeignKey::create() + .name("fk-note_content-note") + .from("note_content", "note_id") + .to("note", "note_id") + .on_delete(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table("note_content").to_owned()).await?; manager .drop_table(Table::drop().table("note").to_owned()).await?; manager diff --git a/ui/common/context.c b/ui/common/context.c index 22ecd55..b369578 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -110,6 +110,7 @@ void uic_context_prepare_close(UiContext *ctx) { cxListClear(ctx->states); cxListClear(ctx->state_widgets); cxListClear(ctx->action_bindings); + uic_context_detach_all(ctx); } void uic_context_destroy(UiContext *ctx, void *document) { -- 2.47.3