implement launch builder for the desktop and web platforms

This commit is contained in:
Evan Almloff committed 2024-01-16 08:42:16 -06:00
1 parent ef553a2cb2
commit 7e4d2debe0
70 files changed
+448 -431

No files matched your search

+7 -5
View File
@@ -13,6 +13,7 @@ mod fragment;
mod global_context;
mod mutations;
mod nodes;
mod platform;
mod properties;
mod runtime;
mod scheduler;
@@ -30,6 +31,7 @@ pub(crate) mod innerlude {
pub use crate::global_context::*;
pub use crate::mutations::*;
pub use crate::nodes::*;
pub use crate::platform::*;
pub use crate::properties::*;
pub use crate::runtime::{Runtime, RuntimeGuard};
pub use crate::scheduler::*;
@@ -73,11 +75,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, CapturedError, Component, ComponentFunction, DynamicNode,
Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, Mutation, Mutations,
NoOpMutations, Properties, RenderReturn, ScopeId, ScopeState, Task, Template,
TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner, VPlaceholder, VText,
VirtualDom, WriteMutations,
AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component, ComponentFunction,
CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, HasAttributes,
IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, RenderReturn,
ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode,
VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
};
/// The purpose of this module is to alleviate imports of many common types
+106
View File
@@ -0,0 +1,106 @@
use std::{any::Any, marker::PhantomData};
use crate::{ComponentFunction, VirtualDom};
/// A boxed object that can be injected into a component's context.
pub struct BoxedContext(Box<dyn ClonableAny>);
impl BoxedContext {
/// Create a new boxed context.
pub fn new(value: impl Any + Clone + 'static) -> Self {
Self(Box::new(value))
}
/// Unwrap the boxed context into its inner value.
pub fn into_inner(self) -> Box<dyn Any> {
self.0.into_inner()
}
}
impl Clone for BoxedContext {
fn clone(&self) -> Self {
Self(self.0.clone_box())
}
}
trait ClonableAny: Any {
fn clone_box(&self) -> Box<dyn ClonableAny>;
fn into_inner(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any + Clone> ClonableAny for T {
fn clone_box(&self) -> Box<dyn ClonableAny> {
Box::new(self.clone())
}
fn into_inner(self: Box<Self>) -> Box<dyn Any> {
self
}
}
/// The platform-independent part of the config needed to launch an application.
pub struct CrossPlatformConfig<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
> {
/// The root component function.
pub component: Component,
/// The props for the root component.
pub props: Props,
/// The contexts to provide to the root component.
pub root_contexts: Vec<BoxedContext>,
_phantom: PhantomData<Phantom>,
}
impl<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
> CrossPlatformConfig<Component, Props, Phantom>
{
/// Create a new cross-platform config.
pub fn new(component: Component, props: Props, root_contexts: Vec<BoxedContext>) -> Self {
Self {
component,
props,
root_contexts,
_phantom: PhantomData,
}
}
/// Build a virtual dom from the config.
pub fn build_vdom(self) -> VirtualDom {
let mut vdom = VirtualDom::new_with_props(self.component, self.props);
for context in self.root_contexts {
vdom.insert_boxed_root_context(context);
}
vdom
}
}
/// A builder to launch a specific platform.
pub trait PlatformBuilder<Props: Clone + 'static> {
/// The platform-specific config needed to launch an application.
type Config: Default;
/// Launch the app.
fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
config: CrossPlatformConfig<Component, Props, Phantom>,
platform_config: Self::Config,
);
}
impl<Props: Clone + 'static> PlatformBuilder<Props> for () {
type Config = ();
fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
_: CrossPlatformConfig<Component, Props, Phantom>,
_: Self::Config,
) {
panic!("No platform is currently enabled. Please enable a platform feature for the dioxus crate.");
}
}
+1 -2
View File
@@ -309,12 +309,11 @@ impl ScopeContext {
impl Drop for ScopeContext {
fn drop(&mut self) {
// Drop all spawned tasks
with_runtime(|rt| {
_ = with_runtime(|rt| {
for id in self.spawned_tasks.borrow().iter() {
rt.remove_task(*id);
}
})
.expect("Runtime to exist")
}
}
+10 -4
View File
@@ -227,7 +227,7 @@ impl VirtualDom {
///
/// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
pub fn new(app: fn() -> Element) -> Self {
Self::new_with_props(|app| app(), app)
Self::new_with_props(app, ())
}
/// Create a new virtualdom and build it immediately
@@ -267,7 +267,14 @@ impl VirtualDom {
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
/// let mutations = dom.rebuild();
/// ```
pub fn new_with_props<P: Clone + 'static>(root: fn(P) -> Element, root_props: P) -> Self {
pub fn new_with_props<
F: crate::ComponentFunction<Phantom, Props = P>,
P: Clone + 'static,
Phantom: 'static,
>(
root: F,
root_props: P,
) -> Self {
let (tx, rx) = futures_channel::mpsc::unbounded();
let mut dom = Self {
@@ -328,11 +335,10 @@ impl VirtualDom {
/// Build the virtualdom with a global context inserted into the base scope
///
/// This method is useful for when you want to provide a context in your app without knowing its type
pub fn with_boxed_root_context(self, context: BoxedContext) -> Self {
pub fn insert_boxed_root_context(&mut self, context: BoxedContext) {
self.base_scope()
.context()
.provide_any_context(context.into_inner());
self
}
/// Manually mark a scope as requiring a re-render
+24 -14
View File
@@ -10,7 +10,7 @@ use crate::{
webview::WebviewInstance,
};
use crossbeam_channel::Receiver;
use dioxus_core::{Component, ElementId, VirtualDom};
use dioxus_core::{ComponentFunction, CrossPlatformConfig, ElementId};
use dioxus_html::{
native_bind::NativeFileEngine, FileEngine, HasFileData, HasFormData, HtmlEvent,
PlatformEventData,
@@ -18,6 +18,7 @@ use dioxus_html::{
use std::{
cell::{Cell, RefCell},
collections::HashMap,
marker::PhantomData,
rc::Rc,
sync::Arc,
};
@@ -28,14 +29,17 @@ use tao::{
};
/// The single top-level object that manages all the running windows, assets, shortcuts, etc
pub(crate) struct App<P> {
pub(crate) struct App<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
> {
// 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) props: Cell<Option<P>>,
pub(crate) dioxus_config: Cell<Option<CrossPlatformConfig<Component, Props, Phantom>>>,
pub(crate) cfg: Cell<Option<Config>>,
// Stuff we need mutable access to
pub(crate) root: Component<P>,
pub(crate) control_flow: ControlFlow,
pub(crate) is_visible_before_start: bool,
pub(crate) window_behavior: WindowCloseBehaviour,
@@ -45,6 +49,8 @@ pub(crate) struct App<P> {
///
/// This includes stuff like the event handlers, shortcuts, etc as well as ways to modify *other* windows
pub(crate) shared: Rc<SharedContext>,
phantom: PhantomData<Phantom>,
}
/// A bundle of state shared between all the windows, providing a way for us to communicate with running webview.
@@ -59,17 +65,24 @@ pub struct SharedContext {
pub(crate) target: EventLoopWindowTarget<UserWindowEvent>,
}
impl<P: 'static + Clone> App<P> {
pub fn new(cfg: Config, props: P, root: Component<P>) -> (EventLoop<UserWindowEvent>, Self) {
impl<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
> App<Component, Props, Phantom>
{
pub fn new(
cfg: Config,
dioxus_config: CrossPlatformConfig<Component, Props, Phantom>,
) -> (EventLoop<UserWindowEvent>, Self) {
let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
let app = Self {
root,
window_behavior: cfg.last_window_close_behaviour,
is_visible_before_start: true,
webviews: HashMap::new(),
control_flow: ControlFlow::Wait,
props: Cell::new(Some(props)),
dioxus_config: Cell::new(Some(dioxus_config)),
cfg: Cell::new(Some(cfg)),
shared: Rc::new(SharedContext {
event_handlers: WindowEventHandlers::default(),
@@ -79,6 +92,7 @@ impl<P: 'static + Clone> App<P> {
proxy: event_loop.create_proxy(),
target: event_loop.clone(),
}),
phantom: PhantomData,
};
// Set the event converter
@@ -164,16 +178,12 @@ impl<P: 'static + Clone> App<P> {
}
pub fn handle_start_cause_init(&mut self) {
let props = self.props.take().unwrap();
let dioxus_config = self.dioxus_config.take().unwrap();
let cfg = self.cfg.take().unwrap();
self.is_visible_before_start = cfg.window.window.visible;
let webview = WebviewInstance::new(
cfg,
VirtualDom::new_with_props(self.root, props),
self.shared.clone(),
);
let webview = WebviewInstance::new(cfg, dioxus_config.build_vdom(), self.shared.clone());
let id = webview.desktop_context.window.id();
self.webviews.insert(id, webview);
-14
View File
@@ -71,20 +71,6 @@ impl Config {
}
}
/// Launch a Dioxus app using the given component and config
///
/// See the [`crate::launch::launch`] function for more details.
pub fn launch(self, app: fn() -> Element) {
self.launch_with_props(|props| props(), app)
}
/// Launch a Dioxus app using the given component, config, and props
///
/// See the [`crate::launch::launch_with_props`] function for more details.
pub fn launch_with_props<P: 'static + Clone>(self, root: Component<P>, props: P) {
crate::launch::launch_with_props(root, props, self)
}
/// Set whether the default menu bar should be enabled.
///
/// > Note: `enable` is `true` by default. To disable the default menu bar pass `false`.
+33 -86
View File
@@ -6,96 +6,19 @@ use crate::{
use dioxus_core::*;
use tao::event::{Event, StartCause, WindowEvent};
/// Launch the WebView and run the event loop.
///
/// This function will start a multithreaded Tokio runtime as well the WebView event loop.
///
/// ```rust, no_run
/// use dioxus::prelude::*;
///
/// fn main() {
/// dioxus_desktop::launch(app);
/// }
///
/// fn app() -> Element {
/// rsx!{
/// h1 {"hello world!"}
/// })
/// }
/// ```
pub fn launch(app: fn() -> Element) {
launch_with_props(|root| root(), app, Config::default())
}
/// Launch the WebView and run the event loop, with configuration.
///
/// This function will start a multithreaded Tokio runtime as well the WebView event loop.
///
/// You can configure the WebView window with a configuration closure
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// use dioxus_desktop::*;
///
/// fn main() {
/// dioxus_desktop::launch_cfg(app, Config::default().with_window(WindowBuilder::new().with_title("My App")));
/// }
///
/// fn app() -> Element {
/// rsx!{
/// h1 {"hello world!"}
/// })
/// }
/// ```
pub fn launch_cfg(app: fn() -> Element, config_builder: Config) {
launch_with_props(|props| props(), app, config_builder)
}
/// Launch the WebView and run the event loop, with configuration and root props.
///
/// If the [`tokio`] feature is enabled, this will also startup and block a tokio runtime using the unconstrained task.
/// This function will start a multithreaded Tokio runtime as well the WebView event loop. This will block the current thread.
///
/// You can configure the WebView window with a configuration closure
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// use dioxus_desktop::Config;
///
/// fn main() {
/// dioxus_desktop::launch_with_props(app, AppProps { name: "asd" }, Config::default());
/// }
///
/// struct AppProps {
/// name: &'static str
/// }
///
/// fn app(cx: Scope<AppProps>) -> Element {
/// rsx!{
/// h1 {"hello {cx.props.name}!"}
/// })
/// }
/// ```
pub fn launch_with_props<P: 'static + Clone>(root: Component<P>, props: P, cfg: Config) {
#[cfg(feature = "tokio")]
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(tokio::task::unconstrained(async move {
launch_with_props_blocking(root, props, cfg);
}));
#[cfg(not(feature = "tokio"))]
launch_with_props_blocking(root, props, cfg);
}
/// Launch the WebView and run the event loop, with configuration and root props.
///
/// 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<P: 'static + Clone>(root: Component<P>, props: P, cfg: Config) {
let (event_loop, mut app) = App::new(cfg, props, root);
pub fn launch_with_props_blocking<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
>(
dioxus_cfg: CrossPlatformConfig<Component, Props, Phantom>,
desktop_config: Config,
) {
let (event_loop, mut app) = App::new(desktop_config, dioxus_cfg);
event_loop.run(move |window_event, _, control_flow| {
app.tick(&window_event);
@@ -130,3 +53,27 @@ pub fn launch_with_props_blocking<P: 'static + Clone>(root: Component<P>, props:
*control_flow = app.control_flow;
})
}
/// The desktop renderer platform
pub struct DesktopPlatform;
impl<Props: Clone + 'static> PlatformBuilder<Props> for DesktopPlatform {
type Config = Config;
fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
config: dioxus_core::CrossPlatformConfig<Component, Props, Phantom>,
platform_config: Self::Config,
) {
#[cfg(feature = "tokio")]
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(tokio::task::unconstrained(async move {
launch_with_props_blocking(config, platform_config)
}));
#[cfg(not(feature = "tokio"))]
launch_with_props_blocking(config, platform_config)
}
}
+68 -62
View File
@@ -5,98 +5,104 @@ use std::any::Any;
use crate::prelude::*;
use dioxus_core::prelude::*;
use dioxus_core::ComponentFunction;
pub trait ClonableAny: Any {
fn clone_box(&self) -> Box<dyn ClonableAny>;
}
impl<T: Any + Clone> ClonableAny for T {
fn clone_box(&self) -> Box<dyn ClonableAny> {
Box::new(self.clone())
}
}
/// The platform-independent part of the config needed to launch an application.
pub struct CrossPlatformConfig<F: ComponentFunction<P>, P> {
/// The root component function.
pub component: F,
/// The props for the root component.
pub props: P,
/// The contexts to provide to the root component.
pub root_contexts: Vec<Box<dyn ClonableAny>>,
}
pub trait PlatformBuilder<P> {
type Config;
/// Launch the app.
fn launch<F: ComponentFunction<P>>(config: CrossPlatformConfig<F, P>, config: Self::Config);
}
impl<P> PlatformBuilder<P> for () {
type Config = ();
fn launch<F: ComponentFunction<P>>(config: CrossPlatformConfig<F, P>, _: ()) {}
}
use dioxus_core::{BoxedContext, CrossPlatformConfig, PlatformBuilder};
/// A builder for a fullstack app.
pub struct LaunchBuilder<F: ComponentFunction<P>, P> {
cross_platform_config: CrossPlatformConfig<F, P>,
pub struct LaunchBuilder<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
> {
cross_platform_config: CrossPlatformConfig<Component, Props, Phantom>,
platform_config: Option<<CurrentPlatform as PlatformBuilder<Props>>::Config>,
}
impl<F: ComponentFunction<P>, P> LaunchBuilder<F, P> {
impl<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
> LaunchBuilder<Component, Props, Phantom>
{
/// Create a new builder for your application.
pub fn new(component: F) -> Self
pub fn new(component: Component) -> Self
where
P: Default,
Props: Default,
{
Self {
cross_platform_config: CrossPlatformConfig {
cross_platform_config: CrossPlatformConfig::new(
component,
props: Default::default(),
root_contexts: vec![],
},
Default::default(),
Default::default(),
),
platform_config: None,
}
}
/// Pass some props to your application.
pub fn props(mut self, props: P) -> Self {
pub fn props(mut self, props: Props) -> Self {
self.cross_platform_config.props = props;
self
}
/// Inject state into the root component's context.
pub fn context(mut self, state: impl ClonableAny + 'static) -> Self {
pub fn context(mut self, state: impl Any + Clone + 'static) -> Self {
self.cross_platform_config
.root_contexts
.push(Box::new(state));
.push(BoxedContext::new(state));
self
}
/// Provide a platform-specific config to the builder.
pub fn platform_config(
self,
config: Option<<CurrentPlatform as PlatformBuilder<P>>::Config>,
pub fn cfg(
mut self,
config: impl Into<Option<<CurrentPlatform as PlatformBuilder<Props>>::Config>>,
) -> Self {
if let Some(config) = config.into() {
self.platform_config = Some(config);
}
self
}
#[allow(clippy::unit_arg)]
/// Launch the app.
pub fn launch(self) {}
}
#[cfg(feature = "router")]
impl<R: Routable> LaunchBuilder<crate::router::FullstackRouterConfig<R>>
where
<R as std::str::FromStr>::Err: std::fmt::Display,
R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
{
/// Create a new launch builder for the given router.
pub fn router() -> Self {
let component = crate::router::RouteWithCfg::<R>;
let props = crate::router::FullstackRouterConfig::default();
Self::new_with_props(component, props)
pub fn launch(self) {
CurrentPlatform::launch(
self.cross_platform_config,
self.platform_config.unwrap_or_default(),
);
}
}
// #[cfg(feature = "router")]
// impl<R: Routable> LaunchBuilder<crate::router::FullstackRouterConfig<R>>
// where
// <R as std::str::FromStr>::Err: std::fmt::Display,
// R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
// {
// /// Create a new launch builder for the given router.
// pub fn router() -> Self {
// let component = crate::router::RouteWithCfg::<R>;
// let props = crate::router::FullstackRouterConfig::default();
// Self::new_with_props(component, props)
// }
// }
#[cfg(feature = "desktop")]
type CurrentPlatform = dioxus_desktop::DesktopPlatform;
#[cfg(feature = "web")]
type CurrentPlatform = dioxus_web::WebPlatform;
#[cfg(not(any(feature = "desktop", feature = "web")))]
type CurrentPlatform = ();
/// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options.
pub fn launch<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
>(
component: Component,
) where
Props: Default,
{
LaunchBuilder::new(component).launch()
}
+3 -2
View File
@@ -6,8 +6,6 @@ pub use dioxus_core;
#[cfg(feature = "launch")]
mod launch;
#[cfg(feature = "launch")]
pub use launch::*;
#[cfg(feature = "hooks")]
pub use dioxus_hooks as hooks;
@@ -30,6 +28,9 @@ pub use dioxus_rsx as rsx;
pub use dioxus_core_macro as core_macro;
pub mod prelude {
#[cfg(feature = "launch")]
pub use crate::launch::*;
#[cfg(feature = "hooks")]
pub use crate::hooks::*;
+13 -91
View File
@@ -60,7 +60,7 @@ use std::rc::Rc;
pub use crate::cfg::Config;
#[cfg(feature = "file_engine")]
pub use crate::file_engine::WebFileEngineExt;
use dioxus_core::{Element, VirtualDom};
use dioxus_core::{ComponentFunction, CrossPlatformConfig, VirtualDom};
use futures_util::{
future::{select, Either},
pin_mut, FutureExt, StreamExt,
@@ -72,7 +72,9 @@ mod dom;
mod eval;
mod event;
mod mutations;
mod platform;
pub use event::*;
pub use platform::*;
#[cfg(feature = "file_engine")]
mod file_engine;
#[cfg(all(feature = "hot_reload", debug_assertions))]
@@ -85,89 +87,6 @@ mod rehydrate;
// mod ric_raf;
// mod rehydrate;
/// Launch the VirtualDOM given a root component and a configuration.
///
/// This function expects the root component to not have root props. To launch the root component with root props, use
/// `launch_with_props` instead.
///
/// This method will block the thread with `spawn_local` from wasm_bindgen_futures.
///
/// If you need to run the VirtualDOM in its own thread, use `run_with_props` instead and await the future.
///
/// # Example
///
/// ```rust, ignore
/// fn main() {
/// dioxus_web::launch(App);
/// }
///
/// static App: Component = |cx| {
/// render!(div {"hello world"})
/// }
/// ```
pub fn launch(root_component: fn() -> Element) {
launch_with_props(
|root_component| root_component(),
root_component,
Config::default(),
);
}
/// Launch your app and run the event loop, with configuration.
///
/// This function will start your web app on the main web thread.
///
/// You can configure the WebView window with a configuration closure
///
/// ```rust, ignore
/// use dioxus::prelude::*;
///
/// fn main() {
/// dioxus_web::launch_with_props(App, Config::new().pre_render(true));
/// }
///
/// fn app() -> Element {
/// rsx!{
/// h1 {"hello world!"}
/// })
/// }
/// ```
pub fn launch_cfg(root: fn(()) -> Element, config: Config) {
launch_with_props(root, (), config)
}
/// Launches the VirtualDOM from the specified component function and props.
///
/// This method will block the thread with `spawn_local`
///
/// # Example
///
/// ```rust, ignore
/// fn main() {
/// dioxus_web::launch_with_props(
/// App,
/// RootProps { name: String::from("joe") },
/// Config::new()
/// );
/// }
///
/// #[derive(ParitalEq, Props)]
/// struct RootProps {
/// name: String
/// }
///
/// static App: Component<RootProps> = |cx| {
/// render!(div {"hello {cx.props.name}"})
/// }
/// ```
pub fn launch_with_props<T: Clone + 'static>(
root_component: fn(T) -> Element,
root_properties: T,
config: Config,
) {
wasm_bindgen_futures::spawn_local(run_with_props(root_component, root_properties, config));
}
/// Runs the app as a future that can be scheduled around the main thread.
///
/// Polls futures internal to the VirtualDOM, hence the async nature of this function.
@@ -180,14 +99,17 @@ pub fn launch_with_props<T: Clone + 'static>(
/// wasm_bindgen_futures::spawn_local(app_fut);
/// }
/// ```
pub async fn run_with_props<T: Clone + 'static>(
root: fn(T) -> Element,
root_props: T,
cfg: Config,
pub async fn run_with_props<
Component: ComponentFunction<Phantom, Props = Props>,
Props: Clone + 'static,
Phantom: 'static,
>(
dioxus_config: CrossPlatformConfig<Component, Props, Phantom>,
web_config: Config,
) {
tracing::info!("Starting up");
let mut dom = VirtualDom::new_with_props(root, root_props);
let mut dom = dioxus_config.build_vdom();
#[cfg(feature = "eval")]
{
@@ -198,7 +120,7 @@ pub async fn run_with_props<T: Clone + 'static>(
}
#[cfg(feature = "panic_hook")]
if cfg.default_panic_hook {
if web_config.default_panic_hook {
console_error_panic_hook::set_once();
}
@@ -212,7 +134,7 @@ pub async fn run_with_props<T: Clone + 'static>(
#[cfg(not(feature = "hydrate"))]
let should_hydrate = false;
let mut websys_dom = dom::WebsysDom::new(cfg, tx);
let mut websys_dom = dom::WebsysDom::new(web_config, tx);
tracing::info!("rebuilding app");
+19
View File
@@ -0,0 +1,19 @@
use dioxus_core::*;
use crate::Config;
/// The web renderer platform
pub struct WebPlatform;
impl<Props: Clone + 'static> PlatformBuilder<Props> for WebPlatform {
type Config = Config;
fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
config: CrossPlatformConfig<Component, Props, Phantom>,
platform_config: Self::Config,
) {
wasm_bindgen_futures::spawn_local(async move {
crate::run_with_props(config, platform_config).await;
});
}
}