From 499e81fa821b71f8af87b45a964308e63810e34d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 17 Jan 2024 15:46:29 -0600 Subject: [PATCH] fix fullstack send bound --- packages/core/src/any_props.rs | 13 +++++------ packages/core/src/lib.rs | 11 +++++----- packages/core/src/nodes.rs | 7 +++++- packages/core/src/platform.rs | 37 +++++++++++++++----------------- packages/core/src/virtual_dom.rs | 18 ++++++---------- packages/desktop/src/app.rs | 9 ++++---- packages/desktop/src/launch.rs | 9 +++++--- packages/dioxus/src/launch.rs | 33 ++++++++++++++-------------- packages/fullstack/src/launch.rs | 6 ++++-- 9 files changed, 74 insertions(+), 69 deletions(-) diff --git a/packages/core/src/any_props.rs b/packages/core/src/any_props.rs index 38ce9c92f..739c454d9 100644 --- a/packages/core/src/any_props.rs +++ b/packages/core/src/any_props.rs @@ -3,8 +3,8 @@ use std::{any::Any, panic::AssertUnwindSafe}; pub(crate) type BoxedAnyProps = Box; -/// A trait that essentially allows VComponentProps to be used generically -pub(crate) trait AnyProps { +/// A trait for a component that can be rendered. +pub trait AnyProps: 'static { fn render(&self) -> RenderReturn; fn memoize(&self, other: &dyn Any) -> bool; fn props(&self) -> &dyn Any; @@ -17,17 +17,18 @@ pub(crate) fn new_any_props, P: Clone + 'static, M: ' memo: fn(&P, &P) -> bool, props: P, name: &'static str, -) -> Box { - Box::new(VProps { +) -> VProps { + VProps { render_fn, memo, props, name, phantom: std::marker::PhantomData, - }) + } } -struct VProps, P, M> { +/// A component along with the props the component uses to render. +pub struct VProps, P, M> { render_fn: F, memo: fn(&P, &P) -> bool, props: P, diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 244545387..e01f4c628 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -23,6 +23,7 @@ mod tasks; mod virtual_dom; pub(crate) mod innerlude { + pub use crate::any_props::*; pub use crate::arena::*; pub use crate::dirty_scope::*; pub use crate::error_boundary::*; @@ -75,11 +76,11 @@ pub(crate) mod innerlude { pub use crate::innerlude::{ fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering, - AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component, ComponentFunction, - CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, HasAttributes, - IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, RenderReturn, - Runtime, ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent, - VNode, VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations, + AnyProps, AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component, + ComponentFunction, CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, + HasAttributes, IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, + RenderReturn, Runtime, ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, + VComponent, VNode, VNodeInner, VPlaceholder, VProps, VText, VirtualDom, WriteMutations, }; /// The purpose of this module is to alleviate imports of many common types diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 7aae4200a..063d665ba 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -529,7 +529,12 @@ impl VComponent { P: Properties + 'static, { let render_fn = component.id(); - let props = new_any_props(component,

::memoize, props, fn_name); + let props = Box::new(new_any_props( + component, +

::memoize, + props, + fn_name, + )); VComponent { name: fn_name, diff --git a/packages/core/src/platform.rs b/packages/core/src/platform.rs index 99bf3f0cf..2b8c7d2c4 100644 --- a/packages/core/src/platform.rs +++ b/packages/core/src/platform.rs @@ -1,8 +1,9 @@ use std::any::Any; use crate::{ - properties::{ComponentFunction, RootProps}, - VComponent, VirtualDom, + any_props::{new_any_props, AnyProps, VProps}, + properties::ComponentFunction, + VirtualDom, }; /// A boxed object that can be injected into a component's context. @@ -43,30 +44,26 @@ impl ClonableAny for T { } /// The platform-independent part of the config needed to launch an application. -pub struct CrossPlatformConfig { +pub struct CrossPlatformConfig { /// The root component function. - component: VComponent, + component: P, /// The contexts to provide to the root component. root_contexts: Vec, } -impl CrossPlatformConfig { +impl, Props: Clone + 'static, M: 'static> + CrossPlatformConfig> +{ /// Create a new cross-platform config. - pub fn new( - component: impl ComponentFunction, - props: Props, - root_contexts: Vec, - ) -> Self { - Self { - component: VComponent::new( - move |props: RootProps| component.rebuild(props.0), - RootProps(props), - "root", - ), + pub fn new(component: F, props: Props, root_contexts: Vec) -> Self { + CrossPlatformConfig { + component: new_any_props(component, |_, _| true, props, "root"), root_contexts, } } +} +impl CrossPlatformConfig

{ /// Push a new context into the root component's context. pub fn push_context(&mut self, context: T) { self.root_contexts.push(BoxedContext::new(context)); @@ -85,18 +82,18 @@ impl CrossPlatformConfig { } /// A builder to launch a specific platform. -pub trait PlatformBuilder { +pub trait PlatformBuilder { /// The platform-specific config needed to launch an application. type Config: Default; /// Launch the app. - fn launch(config: CrossPlatformConfig, platform_config: Self::Config); + fn launch(config: CrossPlatformConfig

, platform_config: Self::Config); } -impl PlatformBuilder for () { +impl PlatformBuilder

for () { type Config = (); - fn launch(_: CrossPlatformConfig, _: Self::Config) { + fn launch(_: CrossPlatformConfig

, _: Self::Config) { panic!("No platform is currently enabled. Please enable a platform feature for the dioxus crate."); } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 630f44b14..0480fd7f1 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -3,6 +3,7 @@ //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust. use crate::{ + any_props::{new_any_props, AnyProps}, arena::ElementId, innerlude::{ DirtyScope, ElementRef, ErrorBoundary, NoOpMutations, SchedulerMsg, ScopeState, VNodeMount, @@ -10,10 +11,9 @@ use crate::{ }, nodes::RenderReturn, nodes::{Template, TemplateId}, - properties::RootProps, runtime::{Runtime, RuntimeGuard}, scopes::ScopeId, - AttributeValue, BoxedContext, ComponentFunction, Element, Event, Mutations, Task, VComponent, + AttributeValue, BoxedContext, ComponentFunction, Element, Event, Mutations, Task, }; use futures_util::{pin_mut, StreamExt}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -259,17 +259,11 @@ impl VirtualDom { /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" }); /// let mutations = dom.rebuild(); /// ``` - pub fn new_with_props( + pub fn new_with_props( root: impl ComponentFunction, root_props: P, ) -> Self { - let vcomponent = VComponent::new( - move |props: RootProps

| root.rebuild(props.0), - RootProps(root_props), - "root", - ); - - Self::new_with_component(vcomponent) + Self::new_with_component(new_any_props(root, |_, _| true, root_props, "root")) } /// Create a new virtualdom and build it immediately @@ -309,7 +303,7 @@ impl VirtualDom { /// let mut dom = VirtualDom::new_from_root(VComponent::new(Example, SomeProps { name: "jane" }, "Example")); /// let mutations = dom.rebuild(); /// ``` - pub fn new_with_component(root: VComponent) -> Self { + pub fn new_with_component(root: impl AnyProps + 'static) -> Self { let (tx, rx) = futures_channel::mpsc::unbounded(); let mut dom = Self { @@ -324,7 +318,7 @@ impl VirtualDom { suspended_scopes: Default::default(), }; - let root = dom.new_scope(root.props, "app"); + let root = dom.new_scope(Box::new(root), "app"); // Unlike react, we provide a default error boundary that just renders the error as a string root.context() diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 98cfd93b6..00b1ab4c2 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -10,6 +10,7 @@ use crate::{ webview::WebviewInstance, }; use crossbeam_channel::Receiver; +use dioxus_core::AnyProps; use dioxus_core::{CrossPlatformConfig, ElementId}; use dioxus_html::{ native_bind::NativeFileEngine, FileEngine, HasFileData, HasFormData, HtmlEvent, @@ -28,10 +29,10 @@ use tao::{ }; /// The single top-level object that manages all the running windows, assets, shortcuts, etc -pub(crate) struct App { +pub(crate) struct App { // move the props into a cell so we can pop it out later to create the first window // iOS panics if we create a window before the event loop is started, so we toss them into a cell - pub(crate) dioxus_config: Cell>, + pub(crate) dioxus_config: Cell>>, pub(crate) cfg: Cell>, // Stuff we need mutable access to @@ -58,10 +59,10 @@ pub struct SharedContext { pub(crate) target: EventLoopWindowTarget, } -impl App { +impl App

{ pub fn new( cfg: Config, - dioxus_config: CrossPlatformConfig, + dioxus_config: CrossPlatformConfig

, ) -> (EventLoop, Self) { let event_loop = EventLoopBuilder::::with_user_event().build(); diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index bf2be758a..e30d36b8a 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -10,7 +10,10 @@ use tao::event::{Event, StartCause, WindowEvent}; /// /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime /// and is equivalent to calling launch_with_props with the tokio feature disabled. -pub fn launch_with_props_blocking(dioxus_cfg: CrossPlatformConfig, desktop_config: Config) { +pub fn launch_with_props_blocking( + dioxus_cfg: CrossPlatformConfig

, + desktop_config: Config, +) { let (event_loop, mut app) = App::new(desktop_config, dioxus_cfg); event_loop.run(move |window_event, _, control_flow| { @@ -50,10 +53,10 @@ pub fn launch_with_props_blocking(dioxus_cfg: CrossPlatformConfig, desktop_confi /// The desktop renderer platform pub struct DesktopPlatform; -impl PlatformBuilder for DesktopPlatform { +impl PlatformBuilder

for DesktopPlatform { type Config = Config; - fn launch(config: dioxus_core::CrossPlatformConfig, platform_config: Self::Config) { + fn launch(config: dioxus_core::CrossPlatformConfig

, platform_config: Self::Config) { #[cfg(feature = "tokio")] tokio::runtime::Builder::new_multi_thread() .enable_all() diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index bd2200f5f..351952a5a 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -3,21 +3,22 @@ use std::any::Any; use crate::prelude::*; -use dioxus_core::prelude::*; +use dioxus_core::VProps; +use dioxus_core::{prelude::*, AnyProps}; use dioxus_core::{BoxedContext, CrossPlatformConfig, PlatformBuilder}; /// A builder for a fullstack app. -pub struct LaunchBuilder { - cross_platform_config: CrossPlatformConfig, - platform_config: Option<::Config>, +pub struct LaunchBuilder = CurrentPlatform> { + cross_platform_config: CrossPlatformConfig

, + platform_config: Option<>::Config>, } // Default platform builder -impl LaunchBuilder { +impl, Props: Clone + Default + 'static, M: 'static> + LaunchBuilder> +{ /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. - pub fn new( - component: impl ComponentFunction, - ) -> Self { + pub fn new(component: F) -> Self { Self { cross_platform_config: CrossPlatformConfig::new( component, @@ -29,10 +30,7 @@ impl LaunchBuilder { } /// Create a new builder for your application with some root props. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. - pub fn new_with_props( - component: impl ComponentFunction, - props: Props, - ) -> Self { + pub fn new_with_props(component: F, props: Props) -> Self { Self { cross_platform_config: CrossPlatformConfig::new(component, props, Default::default()), platform_config: None, @@ -40,7 +38,7 @@ impl LaunchBuilder { } } -impl LaunchBuilder { +impl> LaunchBuilder { /// Inject state into the root component's context. pub fn context(mut self, state: impl Any + Clone + 'static) -> Self { self.cross_platform_config @@ -49,7 +47,10 @@ impl LaunchBuilder { } /// Provide a platform-specific config to the builder. - pub fn cfg(mut self, config: impl Into::Config>>) -> Self { + pub fn cfg( + mut self, + config: impl Into>::Config>>, + ) -> Self { if let Some(config) = config.into() { self.platform_config = Some(config); } @@ -67,7 +68,7 @@ impl LaunchBuilder { } #[cfg(feature = "web")] -impl LaunchBuilder { +impl LaunchBuilder { /// Launch your web application. pub fn launch_web(self) { dioxus_web::WebPlatform::launch( @@ -78,7 +79,7 @@ impl LaunchBuilder { } #[cfg(feature = "desktop")] -impl LaunchBuilder { +impl LaunchBuilder { /// Launch your desktop application. pub fn launch_desktop(self) { dioxus_desktop::DesktopPlatform::launch( diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index e3a4cfa3f..9ba94544e 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -1,12 +1,14 @@ //! Launch helper macros for fullstack apps #![allow(unused)] use crate::prelude::*; -use dioxus_lib::prelude::*; +use dioxus_lib::prelude::{dioxus_core::AnyProps, *}; /// The desktop renderer platform pub struct FullstackPlatform; -impl dioxus_core::PlatformBuilder for FullstackPlatform { +impl dioxus_core::PlatformBuilder + for FullstackPlatform +{ type Config = Config; fn launch(config: dioxus_core::CrossPlatformConfig, platform_config: Self::Config) {}