Reuse Slides: insert into the presentation and after the slide the picker was opened for (#1103)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Faabyy
2026-10-25 13:47:23 +00:00
co-authored by Claude
parent 95a8f90d1a
commit 52d0fcd6c7
2 changed files with 48 additions and 5 deletions
+16 -5
View File
@@ -459,6 +459,8 @@ pub struct SlideApp {
pub show: Option<show::Show>,
/// Current slide's animations playing on the editor canvas (not the full-screen show).
anim_preview: Option<AnimPreview>,
/// Where the Reuse Slides picker inserts: the presentation and slide current when it opened.
reuse_target: Option<FileTarget>,
/// Audio/video playback (editor control bar, slide show, `media.*` commands).
pub media: media::MediaHost,
pub status: Option<(String, f64)>,
@@ -557,6 +559,7 @@ impl SlideApp {
thesaurus_query: String::new(),
show: None,
anim_preview: None,
reuse_target: None,
media: media::MediaHost::new(audio_out),
status: None,
opening: None,
@@ -1290,6 +1293,7 @@ impl SlideApp {
Ok(list) => {
let mut d = dialogs::Dialog::new("reuseSlides");
d.params = json!({"path": path, "source": source, "keep": keep, "slides": list["slides"]});
self.reuse_target = Some(self.capture_file_target());
self.dialog = Some(d);
}
Err(e) => self.set_status(e.to_string()),
@@ -1297,18 +1301,25 @@ impl SlideApp {
}
/// Insert the slides `picked` (indices) of `source` (the `name` + `data`, or `path`, that
/// `slide.reuseList` listed) after the current slide. Returns whether they were inserted.
/// `slide.reuseList` listed) after the slide that was current when the picker opened, in that
/// presentation, even if another one is active now; if it is closed or the slide is gone,
/// nothing is inserted. Returns whether they were inserted.
pub fn reuse_slides(&mut self, source: &Value, keep: bool, picked: &[usize]) -> bool {
let mut args = if source.is_object() { source.clone() } else { json!({}) };
args["keepFormatting"] = json!(keep);
args["slides"] = json!(picked);
match self.session.execute("slide.reuse", &args) {
Ok(r) => {
self.set_status(format!("Inserted {} slides", r["slides"]));
let result = match self.reuse_target {
Some(target) if !self.viewing_target(&target) => self.execute_on_target(&target, "Reuse Slides", "slide.reuse", &args),
_ => self.session.execute("slide.reuse", &args).map(|_| ()).map_err(|e| e.to_string()),
};
match result {
Ok(()) => {
self.reuse_target = None;
self.set_status(format!("Inserted {} slides", picked.len()));
true
}
Err(e) => {
self.set_status(e.to_string());
self.set_status(e);
false
}
}
+32
View File
@@ -88,6 +88,38 @@ fn inserts_what_was_listed_even_if_the_file_changed_meanwhile() {
assert!(!t.iter().any(|x| x == "Other"), "{t:?}");
}
#[test]
fn inserts_into_the_presentation_the_picker_was_opened_for() {
let file = Rc::new(RefCell::new(deck_bytes(&["Alpha"])));
let mut h = harness(&file);
open_dialog(&mut h);
let first = h.state().session.active().unwrap().uid;
// Another presentation becomes active while the dialog is open.
h.state_mut().session.execute("file.new", &json!({})).unwrap();
let second = h.state().session.active().unwrap().uid;
assert_ne!(first, second);
press_insert(&mut h);
assert!(h.state().dialog.is_none());
let s = &h.state().session;
let doc = |uid: u64| s.documents().iter().find(|d| d.uid == uid).unwrap();
assert!(doc(first).doc.slides.iter().any(|x| x.title() == "Alpha"), "inserted where the picker was opened");
assert_eq!(doc(second).doc.slides.len(), 1, "the other presentation is untouched");
assert_eq!(s.active().unwrap().uid, second, "the view stays on the presentation the user moved to");
}
#[test]
fn nothing_is_inserted_once_that_presentation_is_closed() {
let file = Rc::new(RefCell::new(deck_bytes(&["Alpha"])));
let mut h = harness(&file);
open_dialog(&mut h);
h.state_mut().session.execute("file.new", &json!({})).unwrap();
h.state_mut().session.close_document(0);
press_insert(&mut h);
assert_eq!(h.state().session.documents().len(), 1);
assert!(!titles(&h).iter().any(|x| x == "Alpha"), "{:?}", titles(&h));
assert_eq!(h.state().dialog.as_ref().map(|d| d.id.as_str()), Some("reuseSlides"), "the dialog stays open with the reason");
}
#[test]
fn a_failed_insert_keeps_the_dialog_open() {
let file = Rc::new(RefCell::new(deck_bytes(&["Alpha"])));