diff --git a/apps/effectcraft-web/src/persist.rs b/apps/effectcraft-web/src/persist.rs index 6735d066..6ee2c86b 100644 --- a/apps/effectcraft-web/src/persist.rs +++ b/apps/effectcraft-web/src/persist.rs @@ -167,7 +167,7 @@ pub fn restore(s: &mut Session) -> bool { } if meta.dirty { // Unsaved changes stay unsaved. - s.bump(); + s.mark_unsaved(); } SNAP.with(|c| c.set((s.revision, 0.0))); log::info!("restored the last session ({})", meta.path.as_deref().unwrap_or("untitled")); diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 17fa491d..ca93f29b 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -635,6 +635,13 @@ impl Session { self.saved_revision = self.revision; } + /// The project has changes its file doesn't (a session restored with unsaved changes): it + /// stays modified until it is saved, whatever is undone. + pub fn mark_unsaved(&mut self) { + self.saved_project = Arc::new(Project::default()); + self.bump(); + } + pub fn active_comp(&self) -> Option<&Comp> { self.project.comp(self.state.active_comp?) } diff --git a/crates/engine/src/tests_history.rs b/crates/engine/src/tests_history.rs index d90ca7d0..c67e7c14 100644 --- a/crates/engine/src/tests_history.rs +++ b/crates/engine/src/tests_history.rs @@ -175,3 +175,18 @@ fn modified_mark_follows_undo_and_no_op_edits_record_nothing() { assert!(!s.is_dirty(), "rolled back"); let _ = std::fs::remove_file(path); } + +#[test] +fn a_session_restored_with_unsaved_changes_stays_modified() { + let mut s = Session::default(); + s.execute("comp.new", json!({"name": "H", "width": 64, "height": 64, "duration": 2})).unwrap(); + let p = (*s.project).clone(); + // The web app reopens the last visit's project, which had unsaved changes. + s.replace_project(p, None); + assert!(!s.is_dirty()); + s.mark_unsaved(); + assert!(s.is_dirty()); + solid(&mut s, "A"); + s.execute("edit.undo", json!({})).unwrap(); + assert!(s.is_dirty(), "undo doesn't reach a saved state"); +}