Skip to content

Commit

Permalink
Merge branch 'main' into more-fullstack-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ealmloff committed Oct 4, 2024
2 parents e2daa13 + 9ffd4b8 commit 6c3f48e
Show file tree
Hide file tree
Showing 28 changed files with 1,510 additions and 1,372 deletions.
1,729 changes: 652 additions & 1,077 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
<a href="https://github.com/DioxusLabs/dioxus/blob/main/translations/ja-jp/README.md"> 日本語 </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/dioxus/blob/main/translations/tr-tr"> Türkçe </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/dioxus/blob/main/translations/ko-kr"> 한국어 </a>
</h3>
</div>
<br>
Expand Down
2 changes: 0 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ cargo run --example hello_world

[dog_app](./dog_app.rs) - Accesses dog API

[file_explorer](./file_explorer.rs) - File browser that uses `use_ref` to interact with the model

[todomvc](./todomvc.rs) - Todo task list example

# TODO
Expand Down
2 changes: 1 addition & 1 deletion examples/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn app() -> Element {
let mut emails_sent = use_signal(|| Vec::new() as Vec<String>);

// Wait for responses to the compose channel, and then push them to the emails_sent signal.
let handle = use_coroutine(|mut rx: UnboundedReceiver<String>| async move {
let handle = use_coroutine(move |mut rx: UnboundedReceiver<String>| async move {
use futures_util::StreamExt;
while let Some(message) = rx.next().await {
emails_sent.write().push(message);
Expand Down
3 changes: 2 additions & 1 deletion examples/spread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ fn app() -> Element {
extra_data: "hello{1}",
extra_data2: "hello{2}",
height: "10px",
left: 1
left: 1,
"data-custom-attribute": "value",
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions packages/core-macro/src/props/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,21 +1017,21 @@ Finally, call `.build()` to create the instance of `{name}`.
impl #impl_generics dioxus_core::prelude::HasAttributes for #builder_name < #( #ty_generics ),* > #where_clause {
fn push_attribute(
mut self,
name: &'static str,
ns: Option<&'static str>,
attr: impl dioxus_core::prelude::IntoAttributeValue,
volatile: bool
____name: &'static str,
____ns: Option<&'static str>,
____attr: impl dioxus_core::prelude::IntoAttributeValue,
____volatile: bool
) -> Self {
let ( #(#descructuring,)* ) = self.fields;
self.#field_name.push(
dioxus_core::Attribute::new(
name,
____name,
{
use dioxus_core::prelude::IntoAttributeValue;
attr.into_value()
____attr.into_value()
},
ns,
volatile,
____ns,
____volatile,
)
);
#builder_name {
Expand Down
12 changes: 9 additions & 3 deletions packages/core/src/global_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
/// This can be used to clean up side effects from the component
/// (created with [`use_effect`](dioxus::prelude::use_effect)).
///
/// Note:
/// Effects do not run on the server, but use_drop **DOES**. It runs any time the component is dropped including during SSR rendering on the server. If your clean up logic targets web, the logic has to be gated by a feature, see the below example for details.
///
/// Example:
/// ```rust
/// use dioxus::prelude::*;
Expand Down Expand Up @@ -346,9 +349,12 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
/// });
///
/// use_drop(move || {
/// /// restore scroll to the top of the page
/// let window = web_sys::window().unwrap();
/// window.scroll_with_x_and_y(original_scroll_position(), 0.0);
/// // This only make sense to web and hence the `web!` macro
/// web! {
/// /// restore scroll to the top of the page
/// let window = web_sys::window().unwrap();
/// window.scroll_with_x_and_y(original_scroll_position(), 0.0);
/// }
/// });
///
/// rsx! {
Expand Down
5 changes: 0 additions & 5 deletions packages/desktop/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,6 @@ name = "errors"
path = "../../examples/errors.rs"
doc-scrape-examples = true
[[example]]
name = "file_explorer"
path = "../../examples/file_explorer.rs"
doc-scrape-examples = true
[[example]]
name = "future"
path = "../../examples/future.rs"
Expand Down
19 changes: 6 additions & 13 deletions packages/desktop/src/assets.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use dioxus_core::prelude::{Runtime, ScopeId};
use dioxus_core::prelude::Callback;
use rustc_hash::FxHashMap;
use std::{cell::RefCell, rc::Rc};
use wry::{http::Request, RequestAsyncResponder};
Expand All @@ -7,20 +7,17 @@ use wry::{http::Request, RequestAsyncResponder};
pub type AssetRequest = Request<Vec<u8>>;

pub struct AssetHandler {
f: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
scope: ScopeId,
f: Callback<(AssetRequest, RequestAsyncResponder)>,
}

#[derive(Clone)]
pub struct AssetHandlerRegistry {
dom_rt: Rc<Runtime>,
handlers: Rc<RefCell<FxHashMap<String, AssetHandler>>>,
}

impl AssetHandlerRegistry {
pub fn new(dom_rt: Rc<Runtime>) -> Self {
pub fn new() -> Self {
AssetHandlerRegistry {
dom_rt,
handlers: Default::default(),
}
}
Expand All @@ -37,20 +34,16 @@ impl AssetHandlerRegistry {
) {
if let Some(handler) = self.handlers.borrow().get(name) {
// And run the handler in the scope of the component that created it
self.dom_rt
.on_scope(handler.scope, || (handler.f)(request, responder));
handler.f.call((request, responder));
}
}

pub fn register_handler(
&self,
name: String,
f: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
scope: ScopeId,
f: Callback<(AssetRequest, RequestAsyncResponder)>,
) {
self.handlers
.borrow_mut()
.insert(name, AssetHandler { f, scope });
self.handlers.borrow_mut().insert(name, AssetHandler { f });
}

pub fn remove_handler(&self, name: &str) -> Option<AssetHandler> {
Expand Down
12 changes: 4 additions & 8 deletions packages/desktop/src/desktop_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
AssetRequest, Config, WryEventHandler,
};
use dioxus_core::{
prelude::{current_scope_id, ScopeId},
prelude::{Callback, ScopeId},
VirtualDom,
};
use std::rc::{Rc, Weak};
Expand Down Expand Up @@ -236,14 +236,10 @@ impl DesktopService {
pub fn register_asset_handler(
&self,
name: String,
handler: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
scope: Option<ScopeId>,
handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static,
) {
self.asset_handlers.register_handler(
name,
handler,
scope.unwrap_or(current_scope_id().unwrap_or(ScopeId(0))),
)
self.asset_handlers
.register_handler(name, Callback::new(move |(req, resp)| handler(req, resp)))
}

/// Removes an asset handler by its identifier.
Expand Down
10 changes: 4 additions & 6 deletions packages/desktop/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
ShortcutHandle, ShortcutRegistryError, WryEventHandler,
};
use dioxus_core::{
prelude::{consume_context, current_scope_id, use_hook_with_cleanup, RuntimeGuard},
prelude::{consume_context, use_hook_with_cleanup, RuntimeGuard},
use_hook, Runtime,
};

Expand Down Expand Up @@ -69,11 +69,9 @@ pub fn use_asset_handler(

use_hook_with_cleanup(
|| {
crate::window().asset_handlers.register_handler(
name.to_string(),
Box::new(move |asset, responder| cb((asset, responder))),
current_scope_id().unwrap(),
);
crate::window()
.asset_handlers
.register_handler(name.to_string(), cb);

Rc::new(name.to_string())
},
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl WebviewInstance {

let mut web_context = WebContext::new(cfg.data_dir.clone());
let edit_queue = WryQueue::default();
let asset_handlers = AssetHandlerRegistry::new(dom.runtime());
let asset_handlers = AssetHandlerRegistry::new();
let edits = WebviewEdits::new(dom.runtime(), edit_queue.clone());
let file_hover = NativeFileHover::default();
let headless = !cfg.window.window.visible;
Expand Down
38 changes: 37 additions & 1 deletion packages/fullstack/src/document/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,15 @@ impl Document for ServerDocument {
http_equiv: props.http_equiv,
content: props.content,
property: props.property,
..props.additional_attributes
}
});
}

fn create_script(&self, props: ScriptProps) {
self.warn_if_streaming();
self.serialize_for_hydration();
let children = props.script_contents();
let children = props.script_contents().ok();
self.0.borrow_mut().script.push(rsx! {
script {
src: props.src,
Expand All @@ -110,11 +111,46 @@ impl Document for ServerDocument {
nonce: props.nonce,
referrerpolicy: props.referrerpolicy,
r#type: props.r#type,
..props.additional_attributes,
{children}
}
});
}

fn create_style(&self, props: StyleProps) {
self.warn_if_streaming();
self.serialize_for_hydration();
match (&props.href, props.style_contents()) {
// The style has inline contents, render it as a style tag
(_, Ok(contents)) => self.0.borrow_mut().script.push(rsx! {
style {
media: props.media,
nonce: props.nonce,
title: props.title,
..props.additional_attributes,
{contents}
}
}),
// The style has a href, render it as a link tag
(Some(_), _) => {
self.0.borrow_mut().script.push(rsx! {
link {
rel: "stylesheet",
href: props.href,
media: props.media,
nonce: props.nonce,
title: props.title,
..props.additional_attributes,
}
});
}
// The style has neither contents nor src, log an error
(None, Err(err)) => {
err.log("Style");
}
}
}

fn create_link(&self, props: head::LinkProps) {
self.warn_if_streaming();
self.serialize_for_hydration();
Expand Down
49 changes: 17 additions & 32 deletions packages/hooks/src/use_coroutine.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ::warnings::Warning;
use dioxus_core::prelude::{consume_context, provide_context, spawn, use_hook};
use crate::{use_context_provider, use_future, UseFuture};
use dioxus_core::prelude::{consume_context, use_hook};
use dioxus_core::Task;
use dioxus_signals::*;
pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
Expand Down Expand Up @@ -69,35 +69,24 @@ use std::future::Future;
/// };
/// ```
#[doc = include_str!("../docs/rules_of_hooks.md")]
pub fn use_coroutine<M, G, F>(init: G) -> Coroutine<M>
pub fn use_coroutine<M, G, F>(mut init: G) -> Coroutine<M>
where
M: 'static,
G: FnOnce(UnboundedReceiver<M>) -> F,
G: FnMut(UnboundedReceiver<M>) -> F + 'static,
F: Future<Output = ()> + 'static,
{
let mut coroutine = use_hook(|| {
provide_context(Coroutine {
needs_regen: Signal::new(true),
tx: CopyValue::new(None),
task: CopyValue::new(None),
})
});
let mut tx_copy_value = use_hook(|| CopyValue::new(None));

// We do this here so we can capture data with FnOnce
// this might not be the best API
dioxus_signals::warnings::signal_read_and_write_in_reactive_scope::allow(|| {
dioxus_signals::warnings::signal_write_in_component_body::allow(|| {
if *coroutine.needs_regen.peek() {
let (tx, rx) = futures_channel::mpsc::unbounded();
let task = spawn(init(rx));
coroutine.tx.set(Some(tx));
coroutine.task.set(Some(task));
coroutine.needs_regen.set(false);
}
})
let future = use_future(move || {
let (tx, rx) = futures_channel::mpsc::unbounded();
tx_copy_value.set(Some(tx));
init(rx)
});

coroutine
use_context_provider(|| Coroutine {
tx: tx_copy_value,
future,
})
}

/// Get a handle to a coroutine higher in the tree
Expand All @@ -111,15 +100,14 @@ pub fn use_coroutine_handle<M: 'static>() -> Coroutine<M> {
}

pub struct Coroutine<T: 'static> {
needs_regen: Signal<bool>,
tx: CopyValue<Option<UnboundedSender<T>>>,
task: CopyValue<Option<Task>>,
future: UseFuture,
}

impl<T> Coroutine<T> {
/// Get the underlying task handle
pub fn task(&self) -> Task {
(*self.task.read()).unwrap()
self.future.task()
}

/// Send a message to the coroutine
Expand All @@ -132,11 +120,8 @@ impl<T> Coroutine<T> {
}

/// Restart this coroutine
///
/// Forces the component to re-render, which will re-invoke the coroutine.
pub fn restart(&mut self) {
self.needs_regen.set(true);
self.task().cancel();
self.future.restart();
}
}

Expand All @@ -151,6 +136,6 @@ impl<T> Clone for Coroutine<T> {

impl<T> PartialEq for Coroutine<T> {
fn eq(&self, other: &Self) -> bool {
self.needs_regen == other.needs_regen && self.tx == other.tx && self.task == other.task
self.tx == other.tx && self.future == other.future
}
}
2 changes: 1 addition & 1 deletion packages/hooks/src/use_future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ where
}
}

#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub struct UseFuture {
task: CopyValue<Task>,
state: Signal<UseFutureState>,
Expand Down
Loading

0 comments on commit 6c3f48e

Please sign in to comment.