Prechádzať zdrojové kódy

wip: begint to accept iterator types

Jonathan Kelley 4 rokov pred
rodič
commit
742f150

+ 25 - 26
Cargo.toml

@@ -1,35 +1,34 @@
 [workspace]
 members = [
-    # Built-in
     "packages/dioxus",
     "packages/core-macro",
     "packages/core",
     "packages/web",
-    "packages/webview/client",
-    "packages/webview",
     "packages/liveview",
-    # "packages/router",
-    # "packages/ssr",
-    # "packages/webview",
-    # "packages/livehost",
-    # "packages/vscode-ext",
-    # "packages/recoil",
-    # "packages/redux",
-    # "packages/macro",
-    # TODO @Jon, share the validation code
-    # "packages/web",
-    # "packages/hooks",
-    # "packages/cli",
-    # "examples",
-    # "packages/html-macro",
-    # "packages/html-macro-2",
-    #
-    #
-    #
-    # Pulled from percy
-    # "packages/html-macro-test",
-    # "packages/virtual-dom-rs",
-    # "packages/virtual-node",
 ]
 
-
+# Built-in
+# "packages/webview/client",
+# "packages/webview",
+# "packages/router",
+# "packages/ssr",
+# "packages/webview",
+# "packages/livehost",
+# "packages/vscode-ext",
+# "packages/recoil",
+# "packages/redux",
+# "packages/macro",
+# TODO @Jon, share the validation code
+# "packages/web",
+# "packages/hooks",
+# "packages/cli",
+# "examples",
+# "packages/html-macro",
+# "packages/html-macro-2",
+#
+#
+#
+# Pulled from percy
+# "packages/html-macro-test",
+# "packages/virtual-dom-rs",
+# "packages/virtual-node",

+ 1 - 1
packages/core-macro/src/rsxt.rs

@@ -72,7 +72,7 @@ impl ToTokens for RsxRender {
 
         // create a lazy tree that accepts a bump allocator
         let final_tokens = quote! {
-            move |ctx| {
+            move |ctx: &dioxus::prelude::NodeCtx<'_>| -> dioxus::prelude::VNode<'_>{
                 let bump = ctx.bump;
                 #new_toks
             }

+ 3 - 6
packages/core/src/context.rs

@@ -34,8 +34,6 @@ pub struct Context<'src> {
     pub(crate) hooks: &'src RefCell<Vec<*mut Hook>>,
     pub(crate) bump: &'src Bump,
 
-    pub(crate) final_nodes: Rc<RefCell<Option<VNode<'static>>>>,
-
     pub listeners: &'src RefCell<Vec<*const dyn Fn(crate::events::VirtualEvent)>>,
 
     // holder for the src lifetime
@@ -97,7 +95,7 @@ impl<'a> Context<'a> {
     ///     ctx.render(lazy_tree)
     /// }
     ///```
-    pub fn render(self, lazy_nodes: impl FnOnce(&'_ NodeCtx<'a>) -> VNode<'a> + 'a) -> DomTree {
+    pub fn render(&self, lazy_nodes: impl FnOnce(&'_ NodeCtx<'a>) -> VNode<'a> + 'a) -> DomTree {
         let ctx = NodeCtx {
             bump: self.bump,
             scope: self.scope,
@@ -106,9 +104,8 @@ impl<'a> Context<'a> {
         };
 
         let safe_nodes = lazy_nodes(&ctx);
-        let unsafe_nodes = unsafe { std::mem::transmute::<VNode<'a>, VNode<'static>>(safe_nodes) };
-        self.final_nodes.deref().borrow_mut().replace(unsafe_nodes);
-        DomTree {}
+        let root: VNode<'static> = unsafe { std::mem::transmute(safe_nodes) };
+        DomTree { root }
     }
 }
 

+ 6 - 6
packages/core/src/debug_renderer.rs

@@ -48,13 +48,13 @@ mod tests {
 
     #[test]
     fn ensure_creation() -> Result<(), ()> {
-        static Example: FC<()> = |ctx, props| {
-            //
-            ctx.render(html! { <div> "hello world" </div> })
-        };
+        // static Example: FC<()> = |ctx, props| {
+        //     //
+        //     ctx.render(html! { <div> "hello world" </div> })
+        // };
 
-        let mut dom = VirtualDom::new(Example);
-        let machine = DiffMachine::new();
+        // let mut dom = VirtualDom::new(Example);
+        // let machine = DiffMachine::new();
 
         Ok(())
     }

+ 2 - 0
packages/core/src/lib.rs

@@ -102,6 +102,7 @@ pub(crate) mod innerlude {
 
     pub(crate) use nodes::*;
 
+    pub use crate::context::NodeCtx;
     pub use crate::diff::DiffMachine;
     pub use crate::patch::{EditList, EditMachine};
     // pub use crate::patchdx;
@@ -137,6 +138,7 @@ pub mod prelude {
     use crate::nodes;
     pub use nodes::*;
 
+    pub use crate::context::NodeCtx;
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
     pub use crate::innerlude::FC;

+ 212 - 52
packages/core/src/nodebuilder.rs

@@ -5,7 +5,7 @@ use std::{any::Any, borrow::BorrowMut, intrinsics::transmute, u128};
 use crate::{
     context::NodeCtx,
     events::VirtualEvent,
-    innerlude::{Properties, VComponent, FC},
+    innerlude::{DomTree, Properties, VComponent, FC},
     nodes::{Attribute, Listener, NodeKey, VNode},
     prelude::VElement,
 };
@@ -16,13 +16,14 @@ use crate::{
 /// function for building `<div>` elements or the `button` function for building
 /// `<button>` elements.
 #[derive(Debug)]
-pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
+pub struct ElementBuilder<'a, Listeners, Attributes, Children>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Attributes: 'a + AsRef<[Attribute<'a>]>,
     Children: 'a + AsRef<[VNode<'a>]>,
 {
-    ctx: &'b NodeCtx<'a>,
+    ctx: NodeCtx<'a>,
+    // ctx: &'b NodeCtx<'a>,
     key: NodeKey,
     tag_name: &'a str,
     listeners: Listeners,
@@ -31,10 +32,9 @@ where
     namespace: Option<&'a str>,
 }
 
-impl<'a, 'b>
+impl<'a>
     ElementBuilder<
         'a,
-        'b,
         bumpalo::collections::Vec<'a, Listener<'a>>,
         bumpalo::collections::Vec<'a, Attribute<'a>>,
         bumpalo::collections::Vec<'a, VNode<'a>>,
@@ -63,23 +63,24 @@ impl<'a, 'b>
     /// let my_element_builder = ElementBuilder::new(&b, tag_name);
     /// # fn flip_coin() -> bool { true }
     /// ```
-    pub fn new(ctx: &'b NodeCtx<'a>, tag_name: &'static str) -> Self {
+    pub fn new(ctx: &NodeCtx<'a>, tag_name: &'static str) -> Self {
+        // pub fn new(ctx: &'b NodeCtx<'a>, tag_name: &'static str) -> Self {
         // pub fn new<B>(ctx: &'a mut NodeCtx<'a>, tag_name: &'a str) -> Self {
         let bump = ctx.bump;
-        ElementBuilder {
-            ctx,
-            key: NodeKey::NONE,
-            tag_name,
-            listeners: bumpalo::collections::Vec::new_in(bump),
-            attributes: bumpalo::collections::Vec::new_in(bump),
-            children: bumpalo::collections::Vec::new_in(bump),
-            namespace: None,
-        }
+        todo!()
+        // ElementBuilder {
+        //     ctx,
+        //     key: NodeKey::NONE,
+        //     tag_name,
+        //     listeners: bumpalo::collections::Vec::new_in(bump),
+        //     attributes: bumpalo::collections::Vec::new_in(bump),
+        //     children: bumpalo::collections::Vec::new_in(bump),
+        //     namespace: None,
+        // }
     }
 }
 
-impl<'a, 'b, Listeners, Attributes, Children>
-    ElementBuilder<'a, 'b, Listeners, Attributes, Children>
+impl<'a, Listeners, Attributes, Children> ElementBuilder<'a, Listeners, Attributes, Children>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Attributes: 'a + AsRef<[Attribute<'a>]>,
@@ -113,7 +114,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
+    pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, L, Attributes, Children>
     where
         L: 'a + AsRef<[Listener<'a>]>,
     {
@@ -152,7 +153,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
+    pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, Listeners, A, Children>
     where
         A: 'a + AsRef<[Attribute<'a>]>,
     {
@@ -191,7 +192,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
+    pub fn children<C>(self, children: C) -> ElementBuilder<'a, Listeners, Attributes, C>
     where
         C: 'a + AsRef<[VNode<'a>]>,
     {
@@ -310,8 +311,8 @@ where
     }
 }
 
-impl<'a, 'b, Attributes, Children>
-    ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
+impl<'a, Attributes, Children>
+    ElementBuilder<'a, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
 where
     Attributes: 'a + AsRef<[Attribute<'a>]>,
     Children: 'a + AsRef<[VNode<'a>]>,
@@ -364,8 +365,8 @@ where
     }
 }
 
-impl<'a, 'b, Listeners, Children>
-    ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
+impl<'a, Listeners, Children>
+    ElementBuilder<'a, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Children: 'a + AsRef<[VNode<'a>]>,
@@ -423,8 +424,8 @@ where
     }
 }
 
-impl<'a, 'b, Listeners, Attributes>
-    ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
+impl<'a, Listeners, Attributes>
+    ElementBuilder<'a, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Attributes: 'a + AsRef<[Attribute<'a>]>,
@@ -450,7 +451,190 @@ where
         self
     }
 
-    // pub fn virtual_child(mut self)
+    pub fn iter_child(mut self, nodes: impl IntoIterator<Item = DomTree>) -> Self {
+        for item in nodes.into_iter() {
+            self.children.push(item.root);
+        }
+        self
+    }
+
+    pub fn into_tomdtree(mut self, nodes: impl IntoDomTree<'a>) -> Self {
+        self
+    }
+}
+
+impl IntoIterator for DomTree {
+    type Item = DomTree;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
+    }
+}
+
+pub trait IntoDomTree<'a> {
+    type NodeIter: IntoIterator<Item = DomTree>;
+    fn into_domtree(self, ctx: &NodeCtx<'a>) -> Self::NodeIter;
+}
+
+impl IntoDomTree<'_> for DomTree {
+    type NodeIter = std::iter::Once<DomTree>;
+    fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
+        std::iter::once(self)
+    }
+}
+
+impl<'a, G> IntoDomTree<'a> for NodeWrapper<'a, G>
+where
+    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
+{
+    type NodeIter = std::iter::Once<DomTree>;
+
+    fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
+        let p: VNode<'_> = (self.inner)(ctx);
+        let root: VNode<'static> = unsafe { std::mem::transmute(p) };
+        std::iter::once(DomTree { root })
+    }
+}
+
+impl<'a, G, I> IntoDomTree<'a> for I
+where
+    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
+    I: Iterator<Item = NodeWrapper<'a, G>>,
+    // O: Iterator<Item = DomTree>,
+{
+    type NodeIter = std::iter::Map<I, O>;
+
+    fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
+        self.map(|f| {
+            //
+            let caller = (f.inner);
+            let r = caller(ctx);
+        })
+        // todo!()
+        // let p: VNode<'_> = (self.inner)(ctx);
+        // let root: VNode<'static> = unsafe { std::mem::transmute(p) };
+        // std::iter::once(DomTree { root })
+    }
+}
+
+/*
+all possible impls
+
+rsx!{ }
+ctx.render(rsx!)
+
+map(rsx!)
+map(ctx.render(rsx!))
+*/
+
+pub struct NodeWrapper<'a, G>
+where
+    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
+{
+    inner: G,
+    _p: std::marker::PhantomData<&'a ()>,
+}
+
+fn new_wrapper<'a, G>(f: G) -> NodeWrapper<'a, G>
+where
+    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
+{
+    NodeWrapper {
+        inner: f,
+        _p: std::default::Default::default(),
+    }
+}
+
+#[test]
+fn test_iterator_of_nodes<'b>() {
+    let p = (0..10).map(|f| {
+        //
+        new_wrapper(rsx! {
+            div {
+                "aaa {f}"
+            }
+        })
+    });
+
+    let g = p.into_iter();
+    for f in g {}
+
+    static Example: FC<()> = |ctx, props| {
+        ctx.render(|c| {
+            //
+            ElementBuilder::new(c, "div")
+                .into_tomdtree({
+                    // rsx!
+                    new_wrapper(move |n: &NodeCtx| -> VNode {
+                        //
+                        ElementBuilder::new(n, "div").finish()
+                    })
+                    // use the macro to be "blind" to the type
+                    // everything gets interpreted as
+                    /*
+
+
+
+                    for node in {expr} {
+                        ctx.push_alloc_node(node)
+                    }
+
+
+                    so.... just make sure whatever enters {} is iterable (domtree and nodewrapper need impls, )
+
+
+                    */
+                    //
+                })
+                .into_tomdtree({
+                    // render to wrapper -> tree
+                    ctx.render(rsx! {
+                        div {}
+                    })
+                })
+                .into_tomdtree({
+                    // map rsx!
+                    (0..10).map(|f| {
+                        new_wrapper(move |n: &NodeCtx| -> VNode {
+                            //
+                            ElementBuilder::new(n, "div").finish()
+                        })
+                    })
+                })
+                .finish()
+        })
+    };
+
+    use crate::prelude::*;
+
+    // static Example: FC<()> = |ctx, props| {
+    //     //
+    //     let list = (0..10).map(|f| {
+    //         ctx.render(rsx! {
+    //             div {}
+    //         })
+    //     });
+
+    //     ctx.render(|c| {
+    //         ElementBuilder::new(c, "div")
+    //             .iter_child({
+    //                 //
+    //                 ctx.render(rsx! {
+    //                     div {}
+    //                 })
+    //             })
+    //             .iter_child({
+    //                 //
+    //                 (0..10).map(|f| {
+    //                     ctx.render(rsx! {
+    //                         div {}
+    //                     })
+    //                 })
+    //             })
+    //             .iter_child(list)
+    //             .finish()
+    //     })
+    // };
 }
 
 /// Construct a text VNode.
@@ -510,7 +694,7 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
 // /// });
 // /// ```
 // pub fn on<'a, 'b>(
-//     // pub fn on<'a, 'b, F: 'static>(
+//     // pub fn on<'a, F: 'static>(
 //     bump: &'a Bump,
 //     event: &'static str,
 //     callback: impl Fn(VirtualEvent) + 'a,
@@ -525,27 +709,3 @@ pub fn virtual_child<'a, T: Properties + 'a>(ctx: &NodeCtx<'a>, f: FC<T>, p: T)
     let propsd: &'a mut _ = ctx.bump.alloc(p);
     VNode::Component(crate::nodes::VComponent::new(f, propsd))
 }
-
-trait Bany {
-    // fn compare_to<P: Properties>(other: &P) -> bool;
-}
-
-pub fn test_vchild<T: Properties>(p: &T) {
-    // let r = p as *const dyn Bany;
-    // let r = p as *const dyn Bany;
-
-    // std::any::Any
-    // let r = p as &dyn Any;
-    // let g = p as *const u128;
-    // let l = unsafe { std::mem::transmute::<&T, &dyn Any>(p) };
-}
-
-/*
-
-Problem:
-compare two props that we know are the same type without transmute
-
-
-
-
-*/

+ 19 - 11
packages/core/src/nodes.rs

@@ -14,7 +14,10 @@ use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
 
 /// A domtree represents the result of "Viewing" the context
 /// It's a placeholder over vnodes, to make working with lifetimes easier
-pub struct DomTree;
+pub struct DomTree {
+    // this should *never* be publicly accessible to external
+    pub(crate) root: VNode<'static>,
+}
 
 // ==============================
 //   VNODES
@@ -88,9 +91,7 @@ impl<'a> VNode<'a> {
             VNode::Suspended => {
                 todo!()
             }
-            VNode::Component(c) => {
-                c.key
-            }
+            VNode::Component(c) => c.key,
         }
     }
 }
@@ -253,7 +254,7 @@ pub struct VComponent<'src> {
     pub ass_scope: Rc<VCompAssociatedScope>,
 
     pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
-    pub caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'src>,
+    pub caller: Rc<dyn Fn(Context) -> DomTree + 'src>,
 
     // a pointer into the bump arena (given by the 'src lifetime)
     raw_props: *const (),
@@ -296,11 +297,7 @@ impl<'a> VComponent<'a> {
             }
         };
 
-        let caller = move |ctx: Context| -> DomTree {
-            // cast back into the right lifetime
-            let safe_props: &P = unsafe { &*(raw_props as *const P) };
-            component(ctx, props)
-        };
+        let caller = Rc::new(create_closure(component, raw_props));
 
         Self {
             key: NodeKey::NONE,
@@ -308,9 +305,20 @@ impl<'a> VComponent<'a> {
             user_fc: caller_ref,
             raw_props: props as *const P as *const _,
             _p: PhantomData,
-            caller: Rc::new(caller),
+            caller,
             comparator: Rc::new(props_comparator),
             stable_addr: Rc::new(RefCell::new(None)),
         }
     }
 }
+
+fn create_closure<'a, P: Properties + 'a>(
+    component: FC<P>,
+    raw_props: *const (),
+) -> impl for<'r> Fn(Context<'r>) -> DomTree + 'a {
+    move |ctx: Context| -> DomTree {
+        // cast back into the right lifetime
+        let safe_props: &'a P = unsafe { &*(raw_props as *const P) };
+        component(ctx, safe_props)
+    }
+}

+ 7 - 14
packages/core/src/scope.rs

@@ -83,7 +83,7 @@ impl Scope {
     /// This function downcasts the function pointer based on the stored props_type
     ///
     /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
-    pub fn run_scope(&mut self) -> Result<()> {
+    pub fn run_scope<'b>(&'b mut self) -> Result<()> {
         // pub fn run_scope<'bump>(&'bump mut self) -> Result<()> {
         let frame = {
             let frame = self.frames.next();
@@ -91,24 +91,17 @@ impl Scope {
             frame
         };
 
-        let node_slot = std::rc::Rc::new(RefCell::new(None));
-
-        let ctx = Context {
+        let ctx: Context<'b> = Context {
             arena: &self.hook_arena,
             hooks: &self.hooks,
             bump: &frame.bump,
             idx: 0.into(),
             _p: PhantomData {},
-            final_nodes: node_slot.clone(),
             scope: self.myidx,
             listeners: &self.listeners,
         };
         let caller = self.caller.upgrade().expect("Failed to get caller");
 
-        // todo!()
-        // Note that the actual modification of the vnode head element occurs during this call
-        let _: DomTree = (caller.as_ref())(ctx);
-
         /*
         SAFETY ALERT
 
@@ -121,11 +114,11 @@ impl Scope {
         - Public API cannot drop or destructure VNode
         */
 
-        frame.head_node = node_slot
-            .as_ref()
-            .borrow_mut()
-            .take()
-            .expect("Viewing did not happen");
+        frame.head_node = unsafe {
+            let caller2: Rc<OpaqueComponent<'b>> = std::mem::transmute(caller);
+            let tree = (caller2.as_ref())(ctx);
+            tree.root
+        };
 
         Ok(())
     }

+ 15 - 5
packages/core/src/virtual_dom.rs

@@ -3,12 +3,16 @@
 use crate::{error::Error, innerlude::*};
 use crate::{patch::Edit, scope::Scope};
 use generational_arena::Arena;
-use std::{any::TypeId, borrow::{Borrow, BorrowMut}, rc::{Rc, Weak}};
+use std::{
+    any::TypeId,
+    borrow::{Borrow, BorrowMut},
+    rc::{Rc, Weak},
+};
 use thiserror::private::AsDynError;
 
 // We actually allocate the properties for components in their parent's properties
 // We then expose a handle to use those props for render in the form of "OpaqueComponent"
-pub(crate) type OpaqueComponent<'a> = dyn Fn(Context) -> DomTree + 'a;
+pub(crate) type OpaqueComponent<'a> = dyn for<'b> Fn(Context<'b>) -> DomTree + 'a;
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 /// Differences are converted into patches which a renderer can use to draw the UI.
@@ -47,8 +51,15 @@ impl VirtualDom {
     pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
         let mut components = Arena::new();
 
+        // let prr = Rc::new(root_props);
+
         // the root is kept around with a "hard" allocation
-        let root_caller: Rc<OpaqueComponent> = Rc::new(move |ctx| root(ctx, &root_props));
+        let root_caller: Rc<OpaqueComponent> = Rc::new(move |ctx| {
+            //
+            // let p2 = &root_props;
+            // let p2 = prr.clone();
+            root(ctx, &root_props)
+        });
 
         // we then expose this to the component with a weak allocation
         let weak_caller: Weak<OpaqueComponent> = Rc::downgrade(&root_caller);
@@ -110,9 +121,8 @@ impl VirtualDom {
                 LifeCycleEvent::PropsChanged { caller, id, scope } => {
                     let idx = scope.upgrade().unwrap().as_ref().borrow().unwrap();
                     unsafe {
-
                         let p = &mut *(very_unsafe_components);
-                        let c = p.get_mut(idx).unwrap();                        
+                        let c = p.get_mut(idx).unwrap();
                         c.update_caller(caller);
                         c.run_scope()?;
                         diff_machine.change_list.load_known_root(id);

+ 131 - 0
packages/liveview/examples/simpletide.rs

@@ -0,0 +1,131 @@
+pub fn main() {
+    #[cfg(feature = "client")]
+    wasm_bindgen_futures::spawn_local(async { client::app().await.unwrap() });
+
+    #[cfg(feature = "server")]
+    async_std::task::block_on(async { server::app().await.expect("") });
+}
+
+/// ===============================
+///  Common code (shared types)
+/// ===============================
+#[derive(PartialEq, strum::EnumIter, strum::Display, strum::AsRefStr, strum::EnumString)]
+pub enum SelectedStream {
+    Football,
+    Hockey,
+    Socker,
+}
+
+/// Client-specific code
+#[cfg(feature = "client")]
+mod client {
+    use super::*;
+    use dioxus_core::prelude::*;
+    use strum::IntoEnumIterator;
+
+    pub async fn app() -> anyhow::Result<()> {
+        Ok(dioxus_web::WebsysRenderer::start(APP).await)
+    }
+
+    static APP: FC<()> = |ctx, props| {
+        todo!()
+        // let (selected_stream, set_stream) = use_state(&ctx, || SelectedStream::Football);
+
+        // let opts = SelectedStream::iter().map(|name| rsx! { option { "{name}", value: "{name}" } });
+
+        // ctx.render(rsx! {
+        //     div {
+        //         h1 { "Tide basic CRUD app" }
+        //         h2 { "Chosen stream: {selected_stream}" }
+        //         select {
+        //             value: {selected_stream.as_ref()}
+        //             "{selected_stream}"
+        //             {opts}
+        //         }
+        //     }
+        // })
+    };
+}
+
+/// Server-specific code
+#[cfg(feature = "server")]
+mod server {
+    use async_std::sync::RwLock;
+    pub use log::info;
+    use std::sync::Arc;
+    use tide::Request;
+    use tide_websockets::{Message, WebSocket, WebSocketConnection};
+
+    use crate::SelectedStream;
+
+    // type ServerRequest = Request<Arc<RwLock<()>>>;
+    type ServerRequest = Request<()>;
+    // type ServerRequest = Request<Arc<RwLock<ServerState>>>;
+
+    static CLIENT_PATH: &'static str = "";
+
+    pub async fn app() -> anyhow::Result<()> {
+        let mut app = tide::new();
+
+        app.at("")
+            .serve_dir(format!("{}/pkg", CLIENT_PATH))
+            .expect("Cannot serve directory");
+
+        app.at("/updates").get(WebSocket::new(socket_handler));
+
+        let addr = "0.0.0.0:9001";
+        log::info!("Congrats! Server is up and running at http://{}", addr);
+        app.listen(addr).await?;
+
+        Ok(())
+    }
+
+    async fn socket_handler(
+        request: ServerRequest,
+        stream: WebSocketConnection,
+    ) -> tide::Result<()> {
+        // clone the receiver channel
+        // subscribe to any updates
+        // let receiver = request.state().read().await.receiver.clone();
+        // while let Ok(evt) = receiver.recv().await {
+        //     let response_msg = serde_json::to_string(&evt)?;
+        //     stream.send_string(response_msg).await?;
+        // }
+
+        Ok(())
+    }
+
+    use dioxus_core::prelude::*;
+
+    #[derive(PartialEq, Props)]
+    struct SreamListProps {
+        selected_stream: SelectedStream,
+    }
+
+    static STREAM_LIST: FC<SreamListProps> = |ctx, props| {
+        //
+        let g = match props.selected_stream {
+            SelectedStream::Football => ctx.render(rsx! {
+                li {
+                    "watch football!"
+                }
+            }),
+            SelectedStream::Hockey => ctx.render(rsx! {
+                li {
+                    "watch football!"
+                }
+            }),
+            SelectedStream::Socker => ctx.render(rsx! {
+                li {
+                    "watch football!"
+                }
+            }),
+        };
+
+        ctx.render(rsx! {
+            div {
+
+            }
+        })
+    };
+}

+ 109 - 24
packages/liveview/examples/tide.rs

@@ -6,18 +6,21 @@ pub fn main() {
     async_std::task::block_on(async { server::app().await.expect("") });
 }
 
-#[derive(strum::EnumIter, strum::Display, strum::AsRefStr)]
+/// ===============================
+///  Common code (shared types)
+/// ===============================
+#[derive(PartialEq, strum::EnumIter, strum::Display, strum::AsRefStr, strum::EnumString)]
 pub enum SelectedStream {
     Football,
     Hockey,
     Socker,
 }
 
+/// Client-specific code
 #[cfg(feature = "client")]
 mod client {
     use super::*;
     use dioxus_core::prelude::*;
-    use dioxus_web::WebsysRenderer;
     use strum::IntoEnumIterator;
 
     pub async fn app() -> anyhow::Result<()> {
@@ -25,26 +28,26 @@ mod client {
     }
 
     static APP: FC<()> = |ctx, props| {
-        let (selected_stream, set_stream) = use_state(&ctx, || SelectedStream::Football);
-
-        let options = SelectedStream::iter().map(|name| {
-            rsx! { option { "{name}", value: "{name}" } }
-        });
-
-        ctx.render(rsx! {
-            div {
-                h1 { "Tide basic CRUD app" }
-                h2 { "Chosen stream: {selected_stream}" }
-                select {
-                    value: {selected_stream.as_ref()}
-                    "{selected_stream}"
-                    {options}
-                }
-            }
-        })
+        todo!()
+        // let (selected_stream, set_stream) = use_state(&ctx, || SelectedStream::Football);
+
+        // let opts = SelectedStream::iter().map(|name| rsx! { option { "{name}", value: "{name}" } });
+
+        // ctx.render(rsx! {
+        //     div {
+        //         h1 { "Tide basic CRUD app" }
+        //         h2 { "Chosen stream: {selected_stream}" }
+        //         select {
+        //             value: {selected_stream.as_ref()}
+        //             "{selected_stream}"
+        //             {opts}
+        //         }
+        //     }
+        // })
     };
 }
 
+/// Server-specific code
 #[cfg(feature = "server")]
 mod server {
     use async_std::sync::RwLock;
@@ -53,6 +56,8 @@ mod server {
     use tide::Request;
     use tide_websockets::{Message, WebSocket, WebSocketConnection};
 
+    use crate::SelectedStream;
+
     // type ServerRequest = Request<Arc<RwLock<()>>>;
     type ServerRequest = Request<()>;
     // type ServerRequest = Request<Arc<RwLock<ServerState>>>;
@@ -61,11 +66,6 @@ mod server {
 
     pub async fn app() -> anyhow::Result<()> {
         let mut app = tide::new();
-        // let mut app = tide::with_state(Arc::new(RwLock::new(())));
-        // let mut app = tide::with_state(ServerState::new());
-
-        // for all examples:
-        // assume the bundle exists at ../public
 
         app.at("")
             .serve_dir(format!("{}/pkg", CLIENT_PATH))
@@ -94,4 +94,89 @@ mod server {
 
         Ok(())
     }
+
+    use dioxus_core::prelude::*;
+
+    #[derive(PartialEq, Props)]
+    struct SreamListProps {
+        selected_stream: SelectedStream,
+    }
+
+    static STREAM_LIST: FC<SreamListProps> = |ctx, props| {
+        //
+        match props.selected_stream {
+            SelectedStream::Football => ctx.render(rsx! {
+                li {
+                    "watch football!"
+                }
+            }),
+
+            _ => unimplemented!()
+            // .render(ctx),
+            // SelectedStream::Hockey => rsx! {
+            //     li {
+            //         "watch football!"
+            //     }
+            // }
+            // .render(ctx),
+            // SelectedStream::Socker => rsx! {
+            //     li {
+            //         "watch football!"
+            //     }
+            // }
+            // .render(ctx),
+        }
+    };
+}
+
+mod ergorsx {
+
+    // struct Ncx {}
+
+    // struct VVNode {}
+    // struct DTree {
+    //     // struct DTree<F: Fn(&Ncx) -> VVNode> {
+    //     caller: F,
+    // }
+    // impl<F: Fn(&Ncx) -> VVNode> DTree<F> {
+    //     fn new(f: F) -> Self {
+    //         Self { caller: f }
+    //     }
+    // }
+
+    // trait Renderable {
+    //     fn render(self, nodectx: &Ncx) -> VVNode;
+    // }
+
+    // impl<F: Fn(&Ncx) -> VVNode> Renderable for DTree<F> {
+    //     fn render(self, nodectx: &Ncx) -> VVNode {
+    //         (self.caller)(nodectx)
+    //     }
+    // }
+
+    // fn test() {
+    //     let t = 123;
+    //     let r = match t {
+    //         123 => DTree::new(|f| VVNode {}).render(ctx),
+    //         456 => DTree::new(|f| VVNode {}).render(ctx),
+    //         789 => DTree::new(|f| VVNode {}).render(ctx),
+    //         _ => unreachable!(),
+    //     };
+    // }
+
+    // fn example() {
+    //     rsx! {
+    //         div {
+
+    //         }
+    //     }.render(ctx)
+    // }
+
+    // fn example() {
+    //     ctx.render(rsx!{
+    //         div {
+
+    //         }
+    //     })
+    // }
 }