@@ -6,6 +6,7 @@ use crate::todos::{Todos, TodosActions};
6use crate::visibility::Visibility;
6use crate::visibility::Visibility;
7use dodrio::{RootRender, VdomWeak};
7use dodrio::{RootRender, VdomWeak};
8use serde::{Deserialize, Serialize};
8use serde::{Deserialize, Serialize};
9+use std::ops::{Deref, DerefMut};
10/// The controller for the TodoMVC app.
11/// The controller for the TodoMVC app.
@@ -18,107 +19,122 @@ use serde::{Deserialize, Serialize};
18#[derive(Default, Deserialize, Serialize)]
19#[derive(Default, Deserialize, Serialize)]
19pub struct Controller;
20pub struct Controller;
21−/// Invoke `f` with the root `Todos` component, save the (likely just modified)
22−/// todos to local storage, and schedule a new `dodrio` render.
23−fn with_todos<F>(root: &mut dyn RootRender, vdom: VdomWeak, f: F)
25− F: FnOnce(&mut Todos),
27− let todos = root.unwrap_mut::<Todos>();
29− todos.save_to_local_storage();
30− vdom.schedule_render();
33impl TodosActions for Controller {
22impl TodosActions for Controller {
34 fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak) {
23 fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak) {
35− with_todos(root, vdom, |todos| {
24+ let mut todos = AutoCommitTodos::new(root, vdom);
36− let all_complete = todos.todos().iter().all(|t| t.is_complete());
25+ let all_complete = todos.todos().iter().all(|t| t.is_complete());
37− for t in todos.todos_mut() {
26+ for t in todos.todos_mut() {
38− t.set_complete(!all_complete);
27+ t.set_complete(!all_complete);
43 fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String) {
31 fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String) {
44− with_todos(root, vdom, |todos| {
32+ let mut todos = AutoCommitTodos::new(root, vdom);
45− todos.set_draft(draft);
33+ todos.set_draft(draft);
49 fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak) {
36 fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak) {
50− with_todos(root, vdom, |todos| {
37+ let mut todos = AutoCommitTodos::new(root, vdom);
51− let title = todos.take_draft();
38+ let title = todos.take_draft();
52− let title = title.trim();
39+ let title = title.trim();
53− if !title.is_empty() {
40+ if !title.is_empty() {
54− let id = todos.todos().len();
41+ let id = todos.todos().len();
55− let new = Todo::new(id, title);
42+ let new = Todo::new(id, title);
56− todos.add_todo(new);
43+ todos.add_todo(new);
61 fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility) {
47 fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility) {
62− with_todos(root, vdom, |todos| {
48+ let mut todos = AutoCommitTodos::new(root, vdom);
63− todos.set_visibility(vis);
49+ todos.set_visibility(vis);
67 fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak) {
52 fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak) {
68− with_todos(root, vdom, |todos| {
53+ let mut todos = AutoCommitTodos::new(root, vdom);
69− todos.delete_completed();
54+ todos.delete_completed();
74impl TodoActions for Controller {
58impl TodoActions for Controller {
75 fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
59 fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
76− with_todos(root, vdom, |todos| {
60+ let mut todos = AutoCommitTodos::new(root, vdom);
77− let t = &mut todos.todos_mut()[id];
61+ let t = &mut todos.todos_mut()[id];
78− let completed = t.is_complete();
62+ let completed = t.is_complete();
79− t.set_complete(!completed);
63+ t.set_complete(!completed);
83 fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
66 fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
84− with_todos(root, vdom, |todos| {
67+ let mut todos = AutoCommitTodos::new(root, vdom);
85− todos.delete_todo(id);
68+ todos.delete_todo(id);
89 fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
71 fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
90− with_todos(root, vdom, |todos| {
72+ let mut todos = AutoCommitTodos::new(root, vdom);
91− let t = &mut todos.todos_mut()[id];
73+ let t = &mut todos.todos_mut()[id];
92− let desc = t.title().to_string();
74+ let desc = t.title().to_string();
93− t.set_edits(Some(desc));
75+ t.set_edits(Some(desc));
97 fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String) {
78 fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String) {
98− with_todos(root, vdom, |todos| {
79+ let mut todos = AutoCommitTodos::new(root, vdom);
99− let t = &mut todos.todos_mut()[id];
80+ let t = &mut todos.todos_mut()[id];
100− t.set_edits(Some(edits));
81+ t.set_edits(Some(edits));
104 fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
84 fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
105− with_todos(root, vdom, |todos| {
85+ let mut todos = AutoCommitTodos::new(root, vdom);
106− let t = &mut todos.todos_mut()[id];
86+ let t = &mut todos.todos_mut()[id];
107− if let Some(edits) = t.take_edits() {
87+ if let Some(edits) = t.take_edits() {
108− let edits = edits.trim();
88+ let edits = edits.trim();
109− if edits.is_empty() {
89+ if edits.is_empty() {
110− todos.delete_todo(id);
90+ todos.delete_todo(id);
111− } else {
91+ } else {
112− t.set_title(edits);
92+ t.set_title(edits);
118 fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
97 fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
119− with_todos(root, vdom, |todos| {
98+ let mut todos = AutoCommitTodos::new(root, vdom);
120− let t = &mut todos.todos_mut()[id];
99+ let t = &mut todos.todos_mut()[id];
121− let _ = t.take_edits();
100+ let _ = t.take_edits();
104+/// An RAII type that dereferences to a `Todos` and once it is dropped, saves
105+/// the (presumably just modified) todos to local storage, and schedules a new
107+pub struct AutoCommitTodos<'a> {
108+ todos: &'a mut Todos,
112+impl AutoCommitTodos<'_> {
113+ /// Construct a new `AutoCommitTodos` from the root rendering component and
115+ pub fn new(root: &mut dyn RootRender, vdom: VdomWeak) -> AutoCommitTodos {
116+ let todos = root.unwrap_mut::<Todos>();
117+ AutoCommitTodos { todos, vdom }
121+impl Drop for AutoCommitTodos<'_> {
122+ fn drop(&mut self) {
123+ self.todos.save_to_local_storage();
124+ self.vdom.schedule_render();
128+impl Deref for AutoCommitTodos<'_> {
129+ type Target = Todos;
131+ fn deref(&self) -> &Todos {
136+impl DerefMut for AutoCommitTodos<'_> {
137+ fn deref_mut(&mut self) -> &mut Todos {