use threadloom::{threadloom, create_signal, View, WriteSignal};
use threadloom_core::Boundary;
#[derive(Clone, PartialEq, Debug)]
struct Todo {
id: usize,
text: String,
completed: bool,
}
fn todo_app() -> (View, WriteSignal<Vec<Todo>>) {
let (todos, set_todos) = create_signal(vec![
Todo { id: 1, text: "Buy milk".into(), completed: false },
Todo { id: 2, text: "Learn Rust".into(), completed: true },
]);
let view = threadloom! {
div(class="todoapp") {
ul(class="todo-list") {
{
move || {
let current_todos = todos.get();
let items = current_todos.into_iter().map(|todo| {
let class_name = if todo.completed { "completed" } else { "" };
threadloom! {
li(class=class_name) {
label { { todo.text.clone() } }
}
}
}).collect::<Vec<_>>();
threadloom! { { items } }
}
}
}
}
};
(view, set_todos)
}
fn find_boundary(view: &View) -> Option<Boundary> {
match view {
View::DynamicNode(boundary) => Some(boundary.clone()),
View::Element { children, .. } => children.iter().find_map(find_boundary),
View::Fragment(children) => children.iter().find_map(find_boundary),
_ => None,
}
}
fn count_items(view: &View) -> usize {
match view {
View::Element { tag, .. } if tag == "li" => 1,
View::Element { children, .. } => children.iter().map(count_items).sum(),
View::Fragment(children) => children.iter().map(count_items).sum(),
_ => 0,
}
}
#[test]
fn test_reactivity_end_to_end() {
let (app_view, set_todos) = todo_app();
let boundary = find_boundary(&app_view).expect("Expected dynamic boundary in view");
println!("Boundary id is {:?}", boundary.id);
let initial_inner_view = boundary.id.track(|| (boundary.compute.borrow_mut())());
assert_eq!(count_items(&initial_inner_view), 2);
println!("Boundary before set: {:?}", boundary.id.is_dirty());
assert!(!boundary.id.is_dirty());
println!("Setting todos...");
set_todos.set(vec![
Todo { id: 1, text: "Buy milk".into(), completed: false },
Todo { id: 2, text: "Learn Rust".into(), completed: true },
Todo { id: 3, text: "Ship Threadloom".into(), completed: false },
]);
println!("Boundary after set: {:?}", boundary.id.is_dirty());
assert!(boundary.id.is_dirty());
let pending = threadloom_core::take_pending_boundaries();
assert!(pending.contains(&boundary.id));
let new_inner_view = boundary.id.track(|| (boundary.compute.borrow_mut())());
assert_eq!(count_items(&new_inner_view), 3);
}