use crate::prelude::*;
use async_channel::Receiver;
use async_channel::Sender;
use beet_core::exports::futures_lite;
use beet_core::prelude::*;
use beet_dom::prelude::*;
pub fn propagate_signal_effect(world: DeferredWorld, cx: HookContext) {
let entity = cx.entity;
let signal = world.entity(entity).get::<SignalEffect>()
.unwrap();
let subscribe = signal.effect_subscriber();
let sender = world.resource::<DirtySignals>().sender();
effect(move || {
subscribe();
futures_lite::future::block_on(async {
sender.send(entity).await.ok()
});
ReactiveApp::queue_update();
});
}
#[derive(Resource)]
pub struct DirtySignals {
send: Sender<Entity>,
recv: Receiver<Entity>,
}
impl Default for DirtySignals {
fn default() -> Self {
let (send, recv) = async_channel::unbounded();
Self { send, recv }
}
}
impl DirtySignals {
pub fn sender(&self) -> Sender<Entity> { self.send.clone() }
}
pub fn flush_signals(
mut commands: Commands,
dirty: ResMut<DirtySignals>,
effects: Query<&SignalEffect>,
) {
let mut entities = Vec::new();
while let Ok(entity) = dirty.recv.try_recv() {
if !entities.contains(&entity) {
entities.push(entity);
}
}
for entity in entities {
if let Ok(effect) = effects.get(entity) {
commands.run_system(effect.system_id());
}
}
}
#[cfg(test)]
mod test {
use crate::prelude::*;
use beet_core::prelude::*;
use beet_dom::prelude::*;
#[test]
fn app_signals() {
let mut app = App::new();
app.add_plugins(SignalsPlugin);
let (get, set) = signal("foo".to_string());
let entity = app
.world_mut()
.spawn((TextNode::new("foo".to_string()), get.into_bundle()))
.id();
app.world()
.entity(entity)
.get::<TextNode>()
.unwrap()
.0
.xref()
.xpect_eq("foo");
set("bar".to_string());
app.update();
app.world()
.entity(entity)
.get::<TextNode>()
.unwrap()
.0
.xref()
.xpect_eq("bar");
}
#[test]
fn text_nodes() {
let mut app = App::new();
app.add_plugins(SignalsPlugin);
let (get, set) = signal(5u32);
let div = app
.world_mut()
.spawn(rsx! { <div>{get}</div> })
.get::<Children>()
.unwrap()[0];
let text = app.world().entity(div).get::<Children>().unwrap()[0];
app.world()
.entity(text)
.get::<TextNode>()
.unwrap()
.0
.xref()
.xpect_eq("5");
set(10);
app.update();
app.world()
.entity(text)
.get::<TextNode>()
.unwrap()
.0
.xref()
.xpect_eq("10");
}
#[test]
fn attributes() {
let mut app = App::new();
app.add_plugins(SignalsPlugin);
let (get, set) = signal("foo");
let div = app
.world_mut()
.spawn(rsx! { <div class=get /> })
.get::<Children>()
.unwrap()[0];
let attr = app.world().entity(div).get::<Attributes>().unwrap()[0];
app.world()
.entity(attr)
.get::<TextNode>()
.unwrap()
.to_string()
.xref()
.xpect_eq("foo");
set("bar");
app.update();
app.world()
.entity(attr)
.get::<TextNode>()
.unwrap()
.to_string()
.xref()
.xpect_eq("bar");
}
#[test]
fn attribute_blocks() {
#[derive(Default, Buildable, AttributeBlock)]
struct Foo {
class: Option<DerivedGetter<String>>,
}
#[template]
fn Bar(#[field(flatten)] foo: Foo) -> impl Bundle {
rsx! { <div {foo} /> }
}
let mut app = App::new();
app.add_plugins(ApplyDirectivesPlugin);
let (get, set) = signal("foo".to_string());
let template = app
.world_mut()
.spawn((BeetRoot, rsx! { <Bar class=get /> }))
.get::<Children>()
.unwrap()[0];
app.update();
let template_inner =
app.world().entity(template).get::<Children>().unwrap()[0];
let div = app
.world()
.entity(template_inner)
.get::<Children>()
.unwrap()[0];
let attr = app.world().entity(div).get::<Attributes>().unwrap()[0];
app.world()
.entity(attr)
.get::<TextNode>()
.unwrap()
.to_string()
.xref()
.xpect_eq("foo");
set("bar".to_string());
app.update();
app.world()
.entity(attr)
.get::<TextNode>()
.unwrap()
.to_string()
.xref()
.xpect_eq("bar");
}
#[test]
fn bundle_node() {}
#[beet_core::test]
async fn reactive_app() {
let mut app = App::new();
app.add_plugins(SignalsPlugin);
app.set_runner(ReactiveApp::runner);
let world = app.world_mut();
let (get, set) = signal(5);
let entity = world.spawn(get.into_bundle()).id();
world
.entity(entity)
.get::<TextNode>()
.unwrap()
.0
.clone()
.xpect_eq("5");
app.run();
set(7);
time_ext::sleep_secs(0).await;
ReactiveApp::with(|app| {
app.world()
.entity(entity)
.get::<TextNode>()
.unwrap()
.0
.clone()
.xpect_eq("7");
});
}
}