| ... | ... | @@ -411,7 +411,7 @@ impl Site { |
| 411 | 411 | /// Does `change` to the section's pages, `shown` the page open; `None` where what it |
| 412 | 412 | /// changes is gone or holds what it did not make, which it leaves alone. |
| 413 | 413 | async fn change( |
| 414 | | &self, |
| 414 | &mut self, |
| 415 | 415 | change: Change, |
| 416 | 416 | shown: ExGuid, |
| 417 | 417 | ) -> Result<Option<Applied>, Box<dyn Error>> { |
| ... | ... | @@ -454,10 +454,9 @@ impl Site { |
| 454 | 454 | let (show, added) = self.after(&listed, &pages, show, &mut ops); |
| 455 | 455 | if !created && binned { |
| 456 | 456 | let pages: Vec<Page> = kept.iter().map(|kept| kept.page.clone()).collect(); |
| 457 | | self.library |
| 458 | | .reopen()? |
| 459 | | .recycle_pages_async(&pages, &self.author) |
| 460 | | .await?; |
| 457 | let mut notebook = self.library.reopen()?; |
| 458 | notebook.recycle_pages_async(&pages, &self.author).await?; |
| 459 | self.library = Arc::new(self.library.with(notebook)); |
| 461 | 460 | self.replica.written_async().await?; |
| 462 | 461 | } |
| 463 | 462 | self.apply(ops).await?; |
| ... | ... | @@ -508,10 +507,9 @@ impl Site { |
| 508 | 507 | if !created && binned { |
| 509 | 508 | let identities: Vec<[u8; 16]> = |
| 510 | 509 | back.iter().filter_map(|kept| kept.page.identity).collect(); |
| 511 | | self.library |
| 512 | | .reopen()? |
| 513 | | .unrecycle_pages_async(&identities) |
| 514 | | .await?; |
| 510 | let mut notebook = self.library.reopen()?; |
| 511 | notebook.unrecycle_pages_async(&identities).await?; |
| 512 | self.library = Arc::new(self.library.with(notebook)); |
| 515 | 513 | } |
| 516 | 514 | Applied { |
| 517 | 515 | undo: Change::Delete { |
| ... | ... | @@ -1062,13 +1060,29 @@ impl State { |
| 1062 | 1060 | }; |
| 1063 | 1061 | self.persist()?; |
| 1064 | 1062 | let session = self.session.as_ref().ok_or("No section is open")?; |
| 1065 | | let site = Site { |
| 1063 | let changing = if matches!( |
| 1064 | &change, |
| 1065 | Change::Delete { created: false, .. } | Change::Restore { created: false, .. } |
| 1066 | ) && matches!(session.library.notebook, Ok(Some(_))) |
| 1067 | { |
| 1068 | Some( |
| 1069 | session |
| 1070 | .library |
| 1071 | .structure |
| 1072 | .try_lock_arc() |
| 1073 | .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::WouldBlock))?, |
| 1074 | ) |
| 1075 | } else { |
| 1076 | None |
| 1077 | }; |
| 1078 | let mut site = Site { |
| 1066 | 1079 | replica: Arc::clone(session.section.replica()), |
| 1067 | 1080 | library: Arc::clone(&session.library), |
| 1068 | 1081 | author: self.author.clone(), |
| 1069 | 1082 | fresh: self.dated_page(None, "")?, |
| 1070 | 1083 | }; |
| 1071 | 1084 | let shown = session.space; |
| 1085 | let original = Arc::clone(&site.library); |
| 1072 | 1086 | let proxy = self.proxy.clone(); |
| 1073 | 1087 | self.undo.busy += 1; |
| 1074 | 1088 | self.switching = Some((None, web_time::Instant::now())); |
| ... | ... | @@ -1080,8 +1094,34 @@ impl State { |
| 1080 | 1094 | }; |
| 1081 | 1095 | let failed = applied.as_ref().err().map(ToString::to_string); |
| 1082 | 1096 | let (stale, applied) = (failed.is_none(), applied.ok().flatten()); |
| 1097 | let library = Arc::clone(&site.library); |
| 1083 | 1098 | let _ = proxy.send_event(UserEvent::Then(Box::new(move |state: &mut State| { |
| 1084 | 1099 | state.undo.busy -= 1; |
| 1100 | let lineage = |listed: &Library| { |
| 1101 | listed.location == original.location |
| 1102 | && Arc::ptr_eq(&listed.structure, &original.structure) |
| 1103 | }; |
| 1104 | if !Arc::ptr_eq(&original, &library) |
| 1105 | && state.notebooks.iter().any(|listed| lineage(listed)) |
| 1106 | { |
| 1107 | let path = state |
| 1108 | .session |
| 1109 | .as_ref() |
| 1110 | .filter(|session| lineage(&session.library)) |
| 1111 | .map(|session| session.tabs[session.tab].path.clone()); |
| 1112 | if let Some(path) = path { |
| 1113 | state.adopt(Arc::clone(&library), Some(&path))?; |
| 1114 | } else { |
| 1115 | state.list(&library); |
| 1116 | if state |
| 1117 | .sectionless |
| 1118 | .as_ref() |
| 1119 | .is_some_and(|listed| lineage(listed)) |
| 1120 | { |
| 1121 | state.sectionless = Some(library); |
| 1122 | } |
| 1123 | } |
| 1124 | } |
| 1085 | 1125 | let Some(applied) = applied else { |
| 1086 | 1126 | // What it would change is gone: Undo goes on to the step before. |
| 1087 | 1127 | return match taken { |
| ... | ... | @@ -1102,6 +1142,7 @@ impl State { |
| 1102 | 1142 | state.edited(Vec::new()); |
| 1103 | 1143 | Ok(()) |
| 1104 | 1144 | }))); |
| 1145 | drop(changing); |
| 1105 | 1146 | if let Some(failed) = failed { |
| 1106 | 1147 | return Err(failed.into()); |
| 1107 | 1148 | } |
| ... | ... | @@ -1426,21 +1467,41 @@ mod tests { |
| 1426 | 1467 | fn deleted_pages_come_back_where_they_were_and_leave_the_bin() { |
| 1427 | 1468 | let fixture = Fixture::new("delete", &["First", "Second", "Third"]); |
| 1428 | 1469 | let second = fixture.space("Second"); |
| 1429 | | let deleted = fixture |
| 1430 | | .change(Change::Delete { |
| 1470 | let mut site = fixture.site(); |
| 1471 | assert!(site.library.tabs(crate::recycle::BIN).is_empty()); |
| 1472 | let deleted = pollster::block_on(site.change( |
| 1473 | Change::Delete { |
| 1431 | 1474 | pages: vec![second], |
| 1432 | 1475 | show: None, |
| 1433 | 1476 | created: false, |
| 1434 | | }) |
| 1435 | | .unwrap(); |
| 1477 | }, |
| 1478 | fixture.space("First"), |
| 1479 | )) |
| 1480 | .unwrap() |
| 1481 | .unwrap(); |
| 1482 | assert!(!site.library.tabs(crate::recycle::BIN).is_empty()); |
| 1483 | assert!(!Arc::ptr_eq(&site.library, &fixture.library)); |
| 1484 | assert!(Arc::ptr_eq( |
| 1485 | &site.library.structure, |
| 1486 | &fixture.library.structure |
| 1487 | )); |
| 1488 | assert!(Arc::ptr_eq(&site.replica, fixture.section().replica())); |
| 1489 | assert!(fixture.library.tabs(crate::recycle::BIN).is_empty()); |
| 1490 | let deleted_library = Arc::clone(&site.library); |
| 1436 | 1491 | assert_eq!(fixture.titles(), titled(&["First", "Third"])); |
| 1437 | 1492 | assert_eq!(deleted.show, fixture.space("Third")); |
| 1438 | 1493 | assert_eq!(fixture.binned(), ["Second"]); |
| 1439 | | let restored = fixture.change(deleted.undo).unwrap(); |
| 1494 | let restored = pollster::block_on(site.change(deleted.undo, deleted.show)) |
| 1495 | .unwrap() |
| 1496 | .unwrap(); |
| 1497 | assert!(!Arc::ptr_eq(&site.library, &deleted_library)); |
| 1498 | assert!(!site.library.tabs(crate::recycle::BIN).is_empty()); |
| 1440 | 1499 | assert_eq!(fixture.titles(), titled(&["First", "Second", "Third"])); |
| 1441 | 1500 | assert_eq!(restored.show, fixture.space("Second")); |
| 1442 | 1501 | assert!(fixture.binned().is_empty()); |
| 1443 | | let again = fixture.change(restored.undo).unwrap(); |
| 1502 | let again = pollster::block_on(site.change(restored.undo, restored.show)) |
| 1503 | .unwrap() |
| 1504 | .unwrap(); |
| 1444 | 1505 | assert_eq!(fixture.titles(), titled(&["First", "Third"])); |
| 1445 | 1506 | assert_eq!(fixture.binned(), ["Second"]); |
| 1446 | 1507 | |