fix desktop hot reloading

This commit is contained in:
Evan Almloff 2023-07-18 11:55:56 -07:00
parent 0c5025ffa0
commit 2c1c147828
15 changed files with 130 additions and 115 deletions

View file

@ -48,7 +48,7 @@ fn BlogList(cx: Scope) -> Element {
fn BlogPost(cx: Scope) -> Element {
let Some(id) = use_route(cx).segment("id") else {
return cx.render(rsx! { div { "No blog post id" } })
return cx.render(rsx! { div { "No blog post id" } });
};
log::debug!("rendering blog post {}", id);

View file

@ -364,6 +364,7 @@ pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<BuildResul
.display()
);
}
println!("build desktop done");
Ok(BuildResult {
warnings: vec![],

View file

@ -57,21 +57,7 @@ impl Serve {
.await?;
}
cfg::Platform::Desktop => {
crate::builder::build_desktop(&crate_config, true)?;
match &crate_config.executable {
crate::ExecutableType::Binary(name)
| crate::ExecutableType::Lib(name)
| crate::ExecutableType::Example(name) => {
let mut file = crate_config.out_dir.join(name);
if cfg!(windows) {
file.set_extension("exe");
}
Command::new(file.to_str().unwrap())
.stdout(Stdio::inherit())
.output()?;
}
}
server::desktop::startup(crate_config.clone()).await?;
}
}
Ok(())

View file

@ -3,7 +3,7 @@ use crate::{
serve::Serve,
server::{
output::{print_console_info, PrettierOptions, WebServerInfo},
setup_file_watcher, setup_file_watcher_hot_reload, BuildManager,
setup_file_watcher, setup_file_watcher_hot_reload,
},
BuildResult, CrateConfig, Result,
};
@ -72,7 +72,15 @@ pub async fn serve_default(config: CrateConfig) -> Result<()> {
// We got to own watcher so that it exists for the duration of serve
// Otherwise full reload won't work.
let _watcher = setup_file_watcher(crate::cfg::Platform::Desktop, &config, None, None).await?;
let _watcher = setup_file_watcher(
{
let config = config.clone();
move || start_desktop(&config)
},
&config,
None,
)
.await?;
// Print serve info
print_console_info(
@ -107,15 +115,13 @@ pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
}
let file_map = Arc::new(Mutex::new(map));
let build_manager = Arc::new(BuildManager {
platform: crate::cfg::Platform::Web,
config: config.clone(),
reload_tx: None,
});
let (hot_reload_tx, mut hot_reload_rx) = broadcast::channel(100);
// States
// The open interprocess sockets
let channels = Arc::new(Mutex::new(Vec::new()));
// Setup file watcher
// We got to own watcher so that it exists for the duration of serve
// Otherwise hot reload won't work.
@ -123,7 +129,17 @@ pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
&config,
hot_reload_tx,
file_map.clone(),
build_manager,
{
let config = config.clone();
let channels = channels.clone();
move || {
for channel in &mut *channels.lock().unwrap() {
send_msg(HotReloadMsg::Shutdown, channel);
}
start_desktop(&config)
}
},
None,
)
.await?;
@ -153,8 +169,6 @@ pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
}
}
let channels = Arc::new(Mutex::new(Vec::new()));
match LocalSocketListener::bind("@dioxusin") {
Ok(local_socket_stream) => {
let aborted = Arc::new(Mutex::new(false));
@ -196,10 +210,6 @@ pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
}
});
// for channel in &mut *channels.lock().unwrap() {
// send_msg(HotReloadMsg::Shutdown, channel);
// }
while let Ok(template) = hot_reload_rx.recv().await {
let channels = &mut *channels.lock().unwrap();
let mut i = 0;

View file

@ -39,18 +39,11 @@ pub mod desktop;
pub mod web;
/// Sets up a file watcher
async fn setup_file_watcher(
platform: Platform,
async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
build_with: F,
config: &CrateConfig,
reload_tx: Option<Sender<()>>,
web_info: Option<WebServerInfo>,
) -> Result<RecommendedWatcher> {
let build_manager = BuildManager {
platform,
config: config.clone(),
reload_tx,
};
let mut last_update_time = chrono::Local::now().timestamp();
// file watcher: check file change
@ -67,7 +60,7 @@ async fn setup_file_watcher(
let config = watcher_config.clone();
if let Ok(e) = info {
if chrono::Local::now().timestamp() > last_update_time {
match build_manager.rebuild() {
match build_with() {
Ok(res) => {
last_update_time = chrono::Local::now().timestamp();
@ -108,11 +101,11 @@ async fn setup_file_watcher(
// Todo: reduce duplication and merge with setup_file_watcher()
/// Sets up a file watcher with hot reload
async fn setup_file_watcher_hot_reload(
async fn setup_file_watcher_hot_reload<F: Fn() -> Result<BuildResult> + Send + 'static>(
config: &CrateConfig,
hot_reload_tx: Sender<Template<'static>>,
file_map: Arc<Mutex<FileMap<HtmlCtx>>>,
build_manager: Arc<BuildManager>,
build_with: F,
web_info: Option<WebServerInfo>,
) -> Result<RecommendedWatcher> {
// file watcher: check file change
@ -138,7 +131,7 @@ async fn setup_file_watcher_hot_reload(
for path in evt.paths.clone() {
// if this is not a rust file, rebuild the whole project
if path.extension().and_then(|p| p.to_str()) != Some("rs") {
match build_manager.rebuild() {
match build_with() {
Ok(res) => {
print_console_info(
&config,
@ -164,7 +157,7 @@ async fn setup_file_watcher_hot_reload(
messages.extend(msgs);
}
Ok(UpdateResult::NeedsRebuild) => {
match build_manager.rebuild() {
match build_with() {
Ok(res) => {
print_console_info(
&config,
@ -209,35 +202,3 @@ async fn setup_file_watcher_hot_reload(
Ok(watcher)
}
pub struct BuildManager {
platform: Platform,
config: CrateConfig,
reload_tx: Option<broadcast::Sender<()>>,
}
impl BuildManager {
fn rebuild(&self) -> Result<BuildResult> {
log::info!("🪁 Rebuild project");
match self.platform {
Platform::Web => {
let result = builder::build(&self.config, true)?;
// change the websocket reload state to true;
// the page will auto-reload.
if self
.config
.dioxus_config
.web
.watcher
.reload_html
.unwrap_or(false)
{
let _ = Serve::regen_dev_page(&self.config);
}
let _ = self.reload_tx.as_ref().map(|tx| tx.send(()));
Ok(result)
}
Platform::Desktop => start_desktop(&self.config),
}
}
}

View file

@ -10,12 +10,10 @@ use dioxus_html::HtmlCtx;
use dioxus_rsx::hot_reload::FileMap;
use tokio::sync::broadcast;
use super::BuildManager;
use crate::CrateConfig;
pub struct HotReloadState {
pub messages: broadcast::Sender<Template<'static>>,
pub build_manager: Arc<BuildManager>,
pub file_map: Arc<Mutex<FileMap<HtmlCtx>>>,
pub watcher_config: CrateConfig,
}

View file

@ -3,7 +3,7 @@ use crate::{
serve::Serve,
server::{
output::{print_console_info, PrettierOptions, WebServerInfo},
setup_file_watcher, setup_file_watcher_hot_reload, BuildManager,
setup_file_watcher, setup_file_watcher_hot_reload,
},
BuildResult, CrateConfig, Result,
};
@ -86,9 +86,12 @@ pub async fn serve_default(
// We got to own watcher so that it exists for the duration of serve
// Otherwise full reload won't work.
let _watcher = setup_file_watcher(
crate::cfg::Platform::Web,
{
let config = config.clone();
let reload_tx = reload_tx.clone();
move || build(&config, &reload_tx)
},
&config,
Some(reload_tx.clone()),
Some(WebServerInfo {
ip: ip.clone(),
port,
@ -148,18 +151,12 @@ pub async fn serve_hot_reload(
}
let file_map = Arc::new(Mutex::new(map));
let build_manager = Arc::new(BuildManager {
platform: crate::cfg::Platform::Web,
config: config.clone(),
reload_tx: Some(reload_tx.clone()),
});
let hot_reload_tx = broadcast::channel(100).0;
// States
let hot_reload_state = Arc::new(HotReloadState {
messages: hot_reload_tx.clone(),
build_manager: build_manager.clone(),
file_map: file_map.clone(),
watcher_config: config.clone(),
});
@ -175,7 +172,11 @@ pub async fn serve_hot_reload(
&config,
hot_reload_tx,
file_map,
build_manager,
{
let config = config.clone();
let reload_tx = reload_tx.clone();
move || build(&config, &reload_tx)
},
Some(WebServerInfo {
ip: ip.clone(),
port,
@ -473,3 +474,20 @@ async fn ws_handler(
reload_watcher.await.unwrap();
})
}
fn build(config: &CrateConfig, reload_tx: &Sender<()>) -> Result<BuildResult> {
let result = builder::build(&config, true)?;
// change the websocket reload state to true;
// the page will auto-reload.
if config
.dioxus_config
.web
.watcher
.reload_html
.unwrap_or(false)
{
let _ = Serve::regen_dev_page(&config);
}
let _ = reload_tx.send(());
Ok(result)
}

View file

@ -378,7 +378,7 @@ impl ServerFnHandler {
.to_vec()
.into();
let body = hyper::body::to_bytes(req.body_mut().unwrap()).await;
let Ok(body)=body else {
let Ok(body) = body else {
handle_error(body.err().unwrap(), res);
return;
};

View file

@ -56,10 +56,9 @@ where
{
use serde::Deserialize;
let Ok(file_engine) =
SerializedFileEngine::deserialize(deserializer) else{
return Ok(None);
};
let Ok(file_engine) = SerializedFileEngine::deserialize(deserializer) else {
return Ok(None);
};
let file_engine = std::sync::Arc::new(file_engine);
Ok(Some(file_engine))

View file

@ -82,7 +82,9 @@ impl Button {
fn write_value(&self, rdom: &mut RealDom) {
if let Some(mut text) = rdom.get_mut(self.text_id) {
let node_type = text.node_type_mut();
let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Text(mut text) = node_type else {
panic!("input must be an element")
};
*text.text_mut() = self.value.clone();
}
}
@ -111,7 +113,9 @@ impl CustomElement for Button {
fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
let node_type = root.node_type();
let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
let NodeType::Element(el) = &*node_type else {
panic!("input must be an element")
};
let value = el
.attributes
@ -146,7 +150,9 @@ impl CustomElement for Button {
AttributeMask::All => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
self.update_value_attr(&el);
self.update_size_attr(&mut el);
}
@ -155,7 +161,9 @@ impl CustomElement for Button {
AttributeMask::Some(attrs) => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
if attrs.contains("width") || attrs.contains("height") {
self.update_size_attr(&mut el);
}

View file

@ -94,14 +94,18 @@ impl CheckBox {
fn write_value(&self, mut root: NodeMut) {
let single_char = {
let node_type = root.node_type_mut();
let NodeTypeMut::Element( el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(el) = node_type else {
panic!("input must be an element")
};
Self::width(&el) == "1px" || Self::height(&el) == "1px"
};
let rdom = root.real_dom_mut();
if let Some(mut text) = rdom.get_mut(self.text_id) {
let node_type = text.node_type_mut();
let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Text(mut text) = node_type else {
panic!("input must be an element")
};
let value = if single_char {
if self.checked {
""
@ -156,7 +160,9 @@ impl CustomElement for CheckBox {
fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
let node_type = root.node_type();
let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
let NodeType::Element(el) = &*node_type else {
panic!("input must be an element")
};
let value = el
.attributes
@ -197,7 +203,9 @@ impl CustomElement for CheckBox {
AttributeMask::All => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
self.update_value_attr(&el);
self.update_size_attr(&mut el);
self.update_checked_attr(&el);
@ -207,7 +215,9 @@ impl CustomElement for CheckBox {
AttributeMask::Some(attrs) => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
if attrs.contains("width") || attrs.contains("height") {
self.update_size_attr(&mut el);
}

View file

@ -56,7 +56,9 @@ impl CustomElement for Input {
}
let node_type = root.node_type();
let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
let NodeType::Element(el) = &*node_type else {
panic!("input must be an element")
};
let input_type = el
.attributes
.get(&OwnedAttributeDiscription {

View file

@ -163,7 +163,9 @@ impl Slider {
if let Some(mut div) = rdom.get_mut(self.pre_cursor_div) {
let node_type = div.node_type_mut();
let NodeTypeMut::Element(mut element) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut element) = node_type else {
panic!("input must be an element")
};
element.set_attribute(
OwnedAttributeDiscription {
name: "width".to_string(),
@ -175,7 +177,9 @@ impl Slider {
if let Some(mut div) = rdom.get_mut(self.post_cursor_div) {
let node_type = div.node_type_mut();
let NodeTypeMut::Element(mut element) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut element) = node_type else {
panic!("input must be an element")
};
element.set_attribute(
OwnedAttributeDiscription {
name: "width".to_string(),
@ -259,7 +263,9 @@ impl CustomElement for Slider {
fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
let node_type = root.node_type();
let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
let NodeType::Element(el) = &*node_type else {
panic!("input must be an element")
};
let value = el.attributes.get(&OwnedAttributeDiscription {
name: "value".to_string(),
@ -390,7 +396,9 @@ impl CustomElement for Slider {
AttributeMask::All => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
self.update_value_attr(&el);
self.update_size_attr(&mut el);
self.update_max_attr(&el);
@ -403,7 +411,9 @@ impl CustomElement for Slider {
AttributeMask::Some(attrs) => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
if attrs.contains("width") || attrs.contains("height") {
self.update_size_attr(&mut el);
}

View file

@ -143,19 +143,25 @@ impl<C: TextLikeController> TextLike<C> {
if let Some(mut text) = rdom.get_mut(self.pre_cursor_text) {
let node_type = text.node_type_mut();
let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Text(mut text) = node_type else {
panic!("input must be an element")
};
*text.text_mut() = self.controller.display_text(text_before_first_cursor);
}
if let Some(mut text) = rdom.get_mut(self.highlighted_text) {
let node_type = text.node_type_mut();
let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Text(mut text) = node_type else {
panic!("input must be an element")
};
*text.text_mut() = self.controller.display_text(text_highlighted);
}
if let Some(mut text) = rdom.get_mut(self.post_cursor_text) {
let node_type = text.node_type_mut();
let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Text(mut text) = node_type else {
panic!("input must be an element")
};
*text.text_mut() = self.controller.display_text(text_after_second_cursor);
}
@ -288,7 +294,9 @@ impl<C: TextLikeController + Send + Sync + Default + 'static> CustomElement for
fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
let node_type = root.node_type();
let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
let NodeType::Element(el) = &*node_type else {
panic!("input must be an element")
};
let value = el
.attributes
@ -370,7 +378,9 @@ impl<C: TextLikeController + Send + Sync + Default + 'static> CustomElement for
AttributeMask::All => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
self.update_value_attr(&el);
self.update_size_attr(&mut el);
self.update_max_width_attr(&el);
@ -381,7 +391,9 @@ impl<C: TextLikeController + Send + Sync + Default + 'static> CustomElement for
AttributeMask::Some(attrs) => {
{
let node_type = root.node_type_mut();
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
let NodeTypeMut::Element(mut el) = node_type else {
panic!("input must be an element")
};
if attrs.contains("width") || attrs.contains("height") {
self.update_size_attr(&mut el);
}

View file

@ -38,7 +38,7 @@ fn BlogList(cx: Scope) -> Element {
fn BlogPost(cx: Scope) -> Element {
let Some(id) = use_route(cx).segment("id") else {
return cx.render(rsx! { div { "No blog post id" } })
return cx.render(rsx! { div { "No blog post id" } });
};
log::trace!("rendering blog post {}", id);