allow returning values from use_eval
This commit is contained in:
1 parent
1e99e35572
commit
e863ef29b8
6 files changed
+100
-13
No files matched your search
+14
-1
@@ -1,4 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::EvalResult;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
@@ -7,6 +8,15 @@ fn main() {
|
||||
fn app(cx: Scope) -> Element {
|
||||
let script = use_state(&cx, String::new);
|
||||
let eval = dioxus_desktop::use_eval(&cx);
|
||||
let future: &UseRef<Option<EvalResult>> = use_ref(&cx, || None);
|
||||
if future.read().is_some() {
|
||||
let future_clone = future.clone();
|
||||
cx.spawn(async move {
|
||||
if let Some(fut) = future_clone.with_mut(|o| o.take()) {
|
||||
println!("{:?}", fut.await)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
@@ -16,7 +26,10 @@ fn app(cx: Scope) -> Element {
|
||||
oninput: move |e| script.set(e.value.clone()),
|
||||
}
|
||||
button {
|
||||
onclick: move |_| eval(script),
|
||||
onclick: move |_| {
|
||||
let fut = eval(script);
|
||||
future.set(Some(fut));
|
||||
},
|
||||
"Execute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::desktop_context::{DesktopContext, UserWindowEvent};
|
||||
use dioxus_core::*;
|
||||
#[cfg(target_os = "ios")]
|
||||
use objc::runtime::Object;
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::Arc,
|
||||
@@ -17,6 +18,7 @@ use wry::{
|
||||
pub(super) struct DesktopController {
|
||||
pub(super) webviews: HashMap<WindowId, WebView>,
|
||||
pub(super) sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||
pub(super) eval_sender: tokio::sync::mpsc::UnboundedSender<Value>,
|
||||
pub(super) pending_edits: Arc<Mutex<Vec<String>>>,
|
||||
pub(super) quit_app_on_close: bool,
|
||||
pub(super) is_ready: Arc<AtomicBool>,
|
||||
@@ -38,6 +40,7 @@ impl DesktopController {
|
||||
let pending_edits = edit_queue.clone();
|
||||
let return_sender = sender.clone();
|
||||
let desktop_context_proxy = proxy.clone();
|
||||
let (eval_sender, eval_reciever) = tokio::sync::mpsc::unbounded_channel::<Value>();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
// We create the runtime as multithreaded, so you can still "spawn" onto multiple threads
|
||||
@@ -50,7 +53,7 @@ impl DesktopController {
|
||||
let mut dom =
|
||||
VirtualDom::new_with_props_and_scheduler(root, props, (sender, receiver));
|
||||
|
||||
let window_context = DesktopContext::new(desktop_context_proxy);
|
||||
let window_context = DesktopContext::new(desktop_context_proxy, eval_reciever);
|
||||
|
||||
dom.base_scope().provide_context(window_context);
|
||||
|
||||
@@ -88,6 +91,7 @@ impl DesktopController {
|
||||
Self {
|
||||
pending_edits,
|
||||
sender: return_sender,
|
||||
eval_sender,
|
||||
webviews: HashMap::new(),
|
||||
is_ready: Arc::new(AtomicBool::new(false)),
|
||||
quit_app_on_close: true,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use crate::controller::DesktopController;
|
||||
use dioxus_core::ScopeState;
|
||||
use serde::de::Error;
|
||||
use serde_json::Value;
|
||||
use std::future::Future;
|
||||
use std::future::IntoFuture;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use wry::application::event_loop::ControlFlow;
|
||||
use wry::application::event_loop::EventLoopProxy;
|
||||
#[cfg(target_os = "ios")]
|
||||
@@ -33,11 +39,18 @@ pub fn use_window(cx: &ScopeState) -> &DesktopContext {
|
||||
pub struct DesktopContext {
|
||||
/// The wry/tao proxy to the current window
|
||||
pub proxy: ProxyType,
|
||||
pub(super) eval_reciever: Rc<tokio::sync::Mutex<tokio::sync::mpsc::UnboundedReceiver<Value>>>,
|
||||
}
|
||||
|
||||
impl DesktopContext {
|
||||
pub(crate) fn new(proxy: ProxyType) -> Self {
|
||||
Self { proxy }
|
||||
pub(crate) fn new(
|
||||
proxy: ProxyType,
|
||||
eval_reciever: tokio::sync::mpsc::UnboundedReceiver<Value>,
|
||||
) -> Self {
|
||||
Self {
|
||||
proxy,
|
||||
eval_reciever: Rc::new(tokio::sync::Mutex::new(eval_reciever)),
|
||||
}
|
||||
}
|
||||
|
||||
/// trigger the drag-window event
|
||||
@@ -245,7 +258,13 @@ pub(super) fn handler(
|
||||
}
|
||||
|
||||
Eval(code) => {
|
||||
if let Err(e) = webview.evaluate_script(code.as_str()) {
|
||||
let script = format!(
|
||||
r#"window.ipc.postMessage(JSON.stringify({{"method":"eval_result", params: (function(){{
|
||||
{}
|
||||
}})()}}));"#,
|
||||
code
|
||||
);
|
||||
if let Err(e) = webview.evaluate_script(&script) {
|
||||
// we can't panic this error.
|
||||
log::warn!("Eval script error: {e}");
|
||||
}
|
||||
@@ -280,10 +299,36 @@ pub(super) fn handler(
|
||||
}
|
||||
|
||||
/// Get a closure that executes any JavaScript in the WebView context.
|
||||
pub fn use_eval<S: std::string::ToString>(cx: &ScopeState) -> &dyn Fn(S) {
|
||||
pub fn use_eval<S: std::string::ToString>(cx: &ScopeState) -> &dyn Fn(S) -> EvalResult {
|
||||
let desktop = use_window(cx).clone();
|
||||
cx.use_hook(|| {
|
||||
move |script| {
|
||||
desktop.eval(script);
|
||||
let recv = desktop.eval_reciever.clone();
|
||||
EvalResult { reciever: recv }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cx.use_hook(|| move |script| desktop.eval(script))
|
||||
/// A future that resolves to the result of a JavaScript evaluation.
|
||||
pub struct EvalResult {
|
||||
reciever: Rc<tokio::sync::Mutex<tokio::sync::mpsc::UnboundedReceiver<serde_json::Value>>>,
|
||||
}
|
||||
|
||||
impl IntoFuture for EvalResult {
|
||||
type Output = Result<serde_json::Value, serde_json::Error>;
|
||||
|
||||
type IntoFuture = Pin<Box<dyn Future<Output = Result<serde_json::Value, serde_json::Error>>>>;
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
Box::pin(async move {
|
||||
let mut reciever = self.reciever.lock().await;
|
||||
match reciever.recv().await {
|
||||
Some(result) => Ok(result),
|
||||
None => Err(serde_json::Error::custom("No result returned")),
|
||||
}
|
||||
}) as Pin<Box<dyn Future<Output = Result<serde_json::Value, serde_json::Error>>>>
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
|
||||
@@ -13,7 +13,7 @@ mod hot_reload;
|
||||
mod protocol;
|
||||
|
||||
use desktop_context::UserWindowEvent;
|
||||
pub use desktop_context::{use_eval, use_window, DesktopContext};
|
||||
pub use desktop_context::{use_eval, use_window, DesktopContext, EvalResult};
|
||||
pub use wry;
|
||||
pub use wry::application as tao;
|
||||
|
||||
@@ -126,7 +126,11 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
||||
let window = builder.build(event_loop).unwrap();
|
||||
let window_id = window.id();
|
||||
|
||||
let (is_ready, sender) = (desktop.is_ready.clone(), desktop.sender.clone());
|
||||
let (is_ready, sender, eval_sender) = (
|
||||
desktop.is_ready.clone(),
|
||||
desktop.sender.clone(),
|
||||
desktop.eval_sender.clone(),
|
||||
);
|
||||
|
||||
let proxy = proxy.clone();
|
||||
|
||||
@@ -143,6 +147,10 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
||||
.with_ipc_handler(move |_window: &Window, payload: String| {
|
||||
parse_ipc_message(&payload)
|
||||
.map(|message| match message.method() {
|
||||
"eval_result" => {
|
||||
let result = message.params();
|
||||
eval_sender.send(result).unwrap();
|
||||
}
|
||||
"user_event" => {
|
||||
let event = trigger_from_serialized(message.params());
|
||||
log::trace!("User event: {:?}", event);
|
||||
|
||||
@@ -30,6 +30,7 @@ futures-util = "0.3.19"
|
||||
smallstr = "0.2.0"
|
||||
futures-channel = "0.3.21"
|
||||
serde_json = { version = "1.0" }
|
||||
serde = { version = "1.0" }
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.56"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
//! Utilities specific to websys
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use dioxus_core::*;
|
||||
use serde::de::Error;
|
||||
use serde_json::Value;
|
||||
|
||||
/// Get a closure that executes any JavaScript in the webpage.
|
||||
///
|
||||
@@ -15,12 +19,24 @@ use dioxus_core::*;
|
||||
///
|
||||
/// The closure will panic if the provided script is not valid JavaScript code
|
||||
/// or if it returns an uncaught error.
|
||||
pub fn use_eval<S: std::string::ToString>(cx: &ScopeState) -> &dyn Fn(S) {
|
||||
pub fn use_eval<S: std::string::ToString>(
|
||||
cx: &ScopeState,
|
||||
) -> &dyn Fn(S) -> Result<Value, serde_json::Error> {
|
||||
cx.use_hook(|| {
|
||||
|script: S| {
|
||||
js_sys::Function::new_no_args(&script.to_string())
|
||||
.call0(&wasm_bindgen::JsValue::NULL)
|
||||
.expect("failed to eval script");
|
||||
let body = script.to_string();
|
||||
if let Ok(value) =
|
||||
js_sys::Function::new_no_args(&body).call0(&wasm_bindgen::JsValue::NULL)
|
||||
{
|
||||
if let Ok(stringified) = js_sys::JSON::stringify(&value) {
|
||||
let string: String = stringified.into();
|
||||
Value::from_str(&string)
|
||||
} else {
|
||||
Err(serde_json::Error::custom("Failed to stringify result"))
|
||||
}
|
||||
} else {
|
||||
Err(serde_json::Error::custom("Failed to execute script"))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user