Preserve behavior drafts until document commit succeeds
This commit is contained in:
@@ -103,7 +103,28 @@ struct Draft {
|
||||
values: BTreeMap<Id, String>,
|
||||
}
|
||||
impl State {
|
||||
pub(super) fn pending_owner(&self) -> Option<Id> {
|
||||
self.draft.as_ref().map(|d| d.owner)
|
||||
}
|
||||
|
||||
pub(super) fn prepare_pending(&self, p: &Project) -> Result<Command, String> {
|
||||
let command = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Choose a scenario tool first.")?
|
||||
.command(p)?;
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub(super) fn acknowledge_commit(&mut self) {
|
||||
self.draft = None;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
||||
return Err(PENDING_DRAFT.into());
|
||||
}
|
||||
match event {
|
||||
Event::Start(owner, tool) => {
|
||||
if p.elements.get(&owner).map(oa_core::Element::kind) != Some(Kind::Scenario) {
|
||||
@@ -127,14 +148,8 @@ impl State {
|
||||
values: BTreeMap::new(),
|
||||
});
|
||||
}
|
||||
Event::Cancel => self.draft = None,
|
||||
Event::Submit => {
|
||||
let d = self.draft.as_ref().ok_or("Choose a scenario tool first.")?;
|
||||
let command = d.command(p)?;
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
self.draft = None;
|
||||
return Ok(Some(command));
|
||||
}
|
||||
Event::Cancel => self.acknowledge_commit(),
|
||||
Event::Submit => return self.prepare_pending(p).map(Some),
|
||||
other => {
|
||||
if let Some(d) = &mut self.draft {
|
||||
match other {
|
||||
|
||||
@@ -189,7 +189,28 @@ impl Draft {
|
||||
}
|
||||
}
|
||||
impl State {
|
||||
pub(super) fn pending_owner(&self) -> Option<Id> {
|
||||
self.draft.as_ref().map(|d| d.machine)
|
||||
}
|
||||
|
||||
pub(super) fn prepare_pending(&self, p: &Project) -> Result<Command, String> {
|
||||
let command = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Choose a state modeling tool.")?
|
||||
.command(p)?;
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub(super) fn acknowledge_commit(&mut self) {
|
||||
self.draft = None;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||
if self.draft.is_some() && matches!(&event, Event::Start(..) | Event::Edit(..)) {
|
||||
return Err(PENDING_DRAFT.into());
|
||||
}
|
||||
match event {
|
||||
Event::Start(machine, tool, selected) => {
|
||||
let mut d = Draft::new(machine, tool);
|
||||
@@ -249,17 +270,8 @@ impl State {
|
||||
d.effect = effect.clone();
|
||||
self.draft = Some(d);
|
||||
}
|
||||
Event::Cancel => self.draft = None,
|
||||
Event::Submit => {
|
||||
let command = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Choose a state modeling tool.")?
|
||||
.command(p)?;
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
self.draft = None;
|
||||
return Ok(Some(command));
|
||||
}
|
||||
Event::Cancel => self.acknowledge_commit(),
|
||||
Event::Submit => return self.prepare_pending(p).map(Some),
|
||||
Event::Viewport(size) => self.viewport = Some(size),
|
||||
Event::Zoom(zoom) => self.zoom = Some(zoom.clamp(0.1, 2.)),
|
||||
Event::Fit => self.zoom = None,
|
||||
@@ -810,6 +822,7 @@ mod tests {
|
||||
}
|
||||
let mut result =
|
||||
execute_batch(&p, &[state.update(&p, Event::Submit).unwrap().unwrap()]).unwrap();
|
||||
state.acknowledge_commit();
|
||||
let r = result.relations.values_mut().next().unwrap();
|
||||
r.description = "Retain configuration".into();
|
||||
let (id, alias) = (r.id, r.alias.clone());
|
||||
@@ -846,6 +859,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let result =
|
||||
execute_batch(&p, &[state.update(&p, Event::Submit).unwrap().unwrap()]).unwrap();
|
||||
state.acknowledge_commit();
|
||||
let region = result
|
||||
.children(ready)
|
||||
.find(|e| e.kind() == Kind::Region)
|
||||
|
||||
@@ -76,7 +76,28 @@ struct Draft {
|
||||
control: ControlKind,
|
||||
}
|
||||
impl State {
|
||||
pub(super) fn pending_owner(&self) -> Option<Id> {
|
||||
self.draft.as_ref().map(|d| d.owner)
|
||||
}
|
||||
|
||||
pub(super) fn prepare_pending(&self, p: &Project) -> Result<Command, String> {
|
||||
let command = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Choose a contribution tool first.")?
|
||||
.command(p)?;
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub(super) fn acknowledge_commit(&mut self) {
|
||||
self.draft = None;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
||||
return Err(PENDING_DRAFT.into());
|
||||
}
|
||||
match event {
|
||||
Event::Viewport(size) => self.viewport = Some(size),
|
||||
Event::Zoom(zoom) => self.zoom = Some(zoom.clamp(0.1, 2.)),
|
||||
@@ -118,19 +139,8 @@ impl State {
|
||||
control: ControlKind::Choice,
|
||||
});
|
||||
}
|
||||
Event::Cancel => self.draft = None,
|
||||
Event::Submit => {
|
||||
let d = self
|
||||
.draft
|
||||
.as_ref()
|
||||
.ok_or("Choose a contribution tool first.")?;
|
||||
let command = d.command(p)?;
|
||||
// Validate before clearing the user's inputs. The ordinary command
|
||||
// handler remains responsible for the single undoable transaction.
|
||||
execute_batch(p, std::slice::from_ref(&command)).map_err(|e| e.to_string())?;
|
||||
self.draft = None;
|
||||
return Ok(Some(command));
|
||||
}
|
||||
Event::Cancel => self.acknowledge_commit(),
|
||||
Event::Submit => return self.prepare_pending(p).map(Some),
|
||||
other => {
|
||||
if let Some(d) = &mut self.draft {
|
||||
match other {
|
||||
@@ -1772,9 +1782,13 @@ mod tests {
|
||||
assert!(state.draft.is_some());
|
||||
state.update(&p, Event::Guard("ready".into())).unwrap();
|
||||
let command = state.update(&p, Event::Submit).unwrap().unwrap();
|
||||
assert!(state.draft.is_none());
|
||||
assert!(state.draft.is_some());
|
||||
assert!(matches!(command, Command::Batch(_)));
|
||||
assert_eq!(p.elements[&owner].steps().unwrap().len(), 2);
|
||||
let result = execute_batch(&p, &[command]).unwrap();
|
||||
state.acknowledge_commit();
|
||||
assert!(state.draft.is_none());
|
||||
assert_eq!(result.elements[&owner].steps().unwrap().len(), 6);
|
||||
}
|
||||
#[test]
|
||||
fn cycle_projection_terminates_and_retains_actual_links() {
|
||||
|
||||
Reference in New Issue
Block a user