From 4222c480cb921607889169e480c64958c0ca1d51 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 18 Jul 2026 22:55:37 +0200 Subject: [PATCH] feat: remove the post duplication feature (closes #10) Co-Authored-By: Claude Fable 5 --- RUST_PLAN_CORE.md | 2 +- crates/bds-core/src/engine/post.rs | 102 ----------------------- crates/bds-ui/src/app.rs | 30 ------- crates/bds-ui/src/app/editor_handlers.rs | 5 -- crates/bds-ui/src/views/post_editor.rs | 14 ---- locales/ui/de.ftl | 1 - locales/ui/en.ftl | 1 - locales/ui/es.ftl | 1 - locales/ui/fr.ftl | 1 - locales/ui/it.ftl | 1 - 10 files changed, 1 insertion(+), 157 deletions(-) diff --git a/RUST_PLAN_CORE.md b/RUST_PLAN_CORE.md index d9f6806..2c07e62 100644 --- a/RUST_PLAN_CORE.md +++ b/RUST_PLAN_CORE.md @@ -68,7 +68,7 @@ Open: Available: - Dashboard and editors for posts, translations, media, tags, templates, scripts, and settings. -- Post create, edit, duplicate, publish, unpublish, discard, and delete flows. +- Post create, edit, publish, unpublish, discard, and delete flows. - Media import, replacement, metadata editing, translations, thumbnails, filters, and post assignment. - Template and Lua script creation, editing, validation, publication, and deletion. - Rope-based editing with syntax highlighting, selection, clipboard, undo/redo, word/line/page movement, line numbers, soft wrapping, mouse selection, and committed IME input. diff --git a/crates/bds-core/src/engine/post.rs b/crates/bds-core/src/engine/post.rs index 5a66d97..96bf909 100644 --- a/crates/bds-core/src/engine/post.rs +++ b/crates/bds-core/src/engine/post.rs @@ -457,60 +457,6 @@ pub fn discard_post_draft(conn: &Connection, data_dir: &Path, post_id: &str) -> } } -/// Create a new draft post by duplicating an existing post. -pub fn duplicate_post(conn: &Connection, data_dir: &Path, post_id: &str) -> EngineResult { - let source = qp::get_post_by_id(conn, post_id)?; - let body = if let Some(ref content) = source.content { - content.clone() - } else if !source.file_path.is_empty() { - let abs_path = data_dir.join(&source.file_path); - if abs_path.exists() { - let raw = fs::read_to_string(&abs_path)?; - let (_fm, body) = read_post_file(&raw).map_err(EngineError::Parse)?; - body - } else { - String::new() - } - } else { - String::new() - }; - - let duplicate_title = if source.title.is_empty() { - "Untitled Copy".to_string() - } else { - format!("{} Copy", source.title) - }; - - let duplicated = create_post( - conn, - data_dir, - &source.project_id, - &duplicate_title, - Some(&body), - source.tags.clone(), - source.categories.clone(), - source.author.as_deref(), - source.language.as_deref(), - source.template_slug.as_deref(), - )?; - - update_post( - conn, - data_dir, - &duplicated.id, - None, - None, - Some(source.excerpt.as_deref()), - None, - None, - None, - None, - None, - None, - Some(source.do_not_translate), - ) -} - /// Delete a post and all related data. pub fn delete_post(conn: &Connection, data_dir: &Path, post_id: &str) -> EngineResult<()> { let post = qp::get_post_by_id(conn, post_id)?; @@ -1540,54 +1486,6 @@ mod tests { assert_eq!(unchanged.content.as_deref(), Some("draft body")); } - #[test] - fn duplicate_post_creates_new_draft_copy() { - let (db, dir) = setup(); - let post = create_post( - db.conn(), - dir.path(), - "p1", - "Original", - Some("body text"), - vec!["rust".into()], - vec!["guide".into()], - Some("Alice"), - Some("en"), - None, - ) - .unwrap(); - let source = update_post( - db.conn(), - dir.path(), - &post.id, - None, - None, - Some(Some("excerpt")), - None, - None, - None, - None, - None, - None, - Some(true), - ) - .unwrap(); - - let duplicate = duplicate_post(db.conn(), dir.path(), &post.id).unwrap(); - - assert_ne!(duplicate.id, source.id); - assert_eq!(duplicate.status, PostStatus::Draft); - assert_eq!(duplicate.title, "Original Copy"); - assert_eq!(duplicate.content.as_deref(), Some("body text")); - assert_eq!(duplicate.tags, source.tags); - assert_eq!(duplicate.categories, source.categories); - assert_eq!(duplicate.author, source.author); - assert_eq!(duplicate.language, source.language); - assert_eq!(duplicate.excerpt, source.excerpt); - assert!(duplicate.do_not_translate); - assert!(duplicate.slug.starts_with("original-copy")); - } - #[test] fn publish_preserves_published_at_on_republish() { let (db, dir) = setup(); diff --git a/crates/bds-ui/src/app.rs b/crates/bds-ui/src/app.rs index c3d437f..f752b61 100644 --- a/crates/bds-ui/src/app.rs +++ b/crates/bds-ui/src/app.rs @@ -3744,36 +3744,6 @@ impl BdsApp { Task::none() } - fn duplicate_post_editor(&mut self, post_id: &str) -> Task { - let Some(db) = &self.db else { - return Task::none(); - }; - let Some(data_dir) = &self.data_dir else { - return Task::none(); - }; - match engine::post::duplicate_post(db.conn(), data_dir, post_id) { - Ok(post) => { - let tab = Tab { - id: post.id.clone(), - title: post.title.clone(), - tab_type: TabType::Post, - is_transient: false, - is_dirty: false, - }; - let idx = tabs::open_tab(&mut self.tabs, tab); - self.active_tab = self.tabs.get(idx).map(|tab| tab.id.clone()); - if let Some(tab) = self.tabs.get(idx).cloned() { - self.load_editor_for_tab(&tab); - } - self.enforce_panel_tab_fallback(); - self.sync_menu_state(); - self.notify(ToastLevel::Success, &t(self.ui_locale, "editor.saved")); - } - Err(e) => self.notify_operation_failed("editor.duplicate", e), - } - Task::none() - } - fn delete_media_editor(&mut self, media_id: &str) -> Task { let Some(db) = &self.db else { return Task::none(); diff --git a/crates/bds-ui/src/app/editor_handlers.rs b/crates/bds-ui/src/app/editor_handlers.rs index b4567b5..0f5d26d 100644 --- a/crates/bds-ui/src/app/editor_handlers.rs +++ b/crates/bds-ui/src/app/editor_handlers.rs @@ -15,7 +15,6 @@ impl BdsApp { }, Save(String), Publish(String), - Duplicate(String), Discard(String), ShowDelete { tab_id: String, @@ -169,9 +168,6 @@ impl BdsApp { PostEditorMsg::Publish => { deferred = DeferredPostAction::Publish(tab_id.clone()); } - PostEditorMsg::Duplicate => { - deferred = DeferredPostAction::Duplicate(tab_id.clone()); - } PostEditorMsg::Discard => { deferred = DeferredPostAction::Discard(tab_id.clone()); } @@ -280,7 +276,6 @@ impl BdsApp { } => self.translate_post_to(&post_id, &target_language), DeferredPostAction::Save(tab_id) => self.save_post_editor(&tab_id), DeferredPostAction::Publish(tab_id) => self.publish_post_editor(&tab_id), - DeferredPostAction::Duplicate(tab_id) => self.duplicate_post_editor(&tab_id), DeferredPostAction::Discard(tab_id) => self.discard_post_editor(&tab_id), DeferredPostAction::ShowDelete { tab_id, name } => { Task::done(Message::ShowModal(modal::ModalState::ConfirmDelete { diff --git a/crates/bds-ui/src/views/post_editor.rs b/crates/bds-ui/src/views/post_editor.rs index a0a8cd4..921f539 100644 --- a/crates/bds-ui/src/views/post_editor.rs +++ b/crates/bds-ui/src/views/post_editor.rs @@ -347,7 +347,6 @@ pub enum PostEditorMsg { RemoveCategory(String), Save, Publish, - Duplicate, Discard, Delete, InsertLink, @@ -412,19 +411,6 @@ pub fn view<'a>( .padding([6, 16]) .into(), ]; - if !on_translation { - header_action_items.push( - button( - text(t(locale, "editor.duplicate")) - .size(13) - .shaping(Shaping::Advanced), - ) - .on_press(Message::PostEditor(PostEditorMsg::Duplicate)) - .padding([6, 16]) - .style(inputs::secondary_button) - .into(), - ); - } if state.status == PostStatus::Draft { header_action_items.push( button( diff --git a/locales/ui/de.ftl b/locales/ui/de.ftl index b57d90d..e8dac5f 100644 --- a/locales/ui/de.ftl +++ b/locales/ui/de.ftl @@ -267,7 +267,6 @@ editor-templateSlug = Vorlage editor-doNotTranslate = Nicht übersetzen editor-publish = Veröffentlichen editor-discard = Verwerfen -editor-duplicate = Duplizieren editor-validate = Validieren editor-run = Ausführen editor-checkSyntax = Syntax prüfen diff --git a/locales/ui/en.ftl b/locales/ui/en.ftl index 794585d..f0ba63f 100644 --- a/locales/ui/en.ftl +++ b/locales/ui/en.ftl @@ -264,7 +264,6 @@ editor-templateSlug = Template editor-doNotTranslate = Do Not Translate editor-publish = Publish editor-discard = Discard -editor-duplicate = Duplicate editor-validate = Validate editor-run = Run editor-checkSyntax = Check Syntax diff --git a/locales/ui/es.ftl b/locales/ui/es.ftl index ce472ec..cee73fd 100644 --- a/locales/ui/es.ftl +++ b/locales/ui/es.ftl @@ -267,7 +267,6 @@ editor-templateSlug = Plantilla editor-doNotTranslate = No traducir editor-publish = Publicar editor-discard = Descartar -editor-duplicate = Duplicar editor-validate = Validar editor-run = Ejecutar editor-checkSyntax = Verificar sintaxis diff --git a/locales/ui/fr.ftl b/locales/ui/fr.ftl index 5f55d91..3d649ae 100644 --- a/locales/ui/fr.ftl +++ b/locales/ui/fr.ftl @@ -267,7 +267,6 @@ editor-templateSlug = Modèle editor-doNotTranslate = Ne pas traduire editor-publish = Publier editor-discard = Annuler les modifications -editor-duplicate = Dupliquer editor-validate = Valider editor-run = Exécuter editor-checkSyntax = Vérifier la syntaxe diff --git a/locales/ui/it.ftl b/locales/ui/it.ftl index 2daf4a4..6176145 100644 --- a/locales/ui/it.ftl +++ b/locales/ui/it.ftl @@ -267,7 +267,6 @@ editor-templateSlug = Modello editor-doNotTranslate = Non tradurre editor-publish = Pubblica editor-discard = Scarta -editor-duplicate = Duplica editor-validate = Valida editor-run = Esegui editor-checkSyntax = Controlla sintassi