diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs index 389438de3..11d4afbfd 100644 --- a/packages/cli-config/src/lib.rs +++ b/packages/cli-config/src/lib.rs @@ -67,6 +67,7 @@ pub const APP_TITLE_ENV: &str = "DIOXUS_APP_TITLE"; #[deprecated(since = "0.6.0", note = "The CLI currently does not set this.")] #[doc(hidden)] pub const OUT_DIR: &str = "DIOXUS_OUT_DIR"; +pub const SESSION_CACHE_DIR: &str = "DIOXUS_SESSION_CACHE_DIR"; /// Reads an environment variable at runtime in debug mode or at compile time in /// release mode. When bundling in release mode, we will not be running under the @@ -277,3 +278,15 @@ pub fn out_dir() -> Option { std::env::var(OUT_DIR).ok().map(PathBuf::from) } } + +/// Get the directory where this app can write to for this session that's guaranteed to be stable +/// between reloads of the same app. This is useful for emitting state like window position and size +/// so the app can restore it when it's next opened. +/// +/// Note that this cache dir is really only useful for platforms that can access it. Web/Android +/// don't have access to this directory, so it's not useful for them. +/// +/// This is designed with desktop executables in mind. +pub fn session_cache_dir() -> Option { + std::env::var(SESSION_CACHE_DIR).ok().map(PathBuf::from) +} diff --git a/packages/cli/src/build/bundle.rs b/packages/cli/src/build/bundle.rs index 21373fb57..4aea94757 100644 --- a/packages/cli/src/build/bundle.rs +++ b/packages/cli/src/build/bundle.rs @@ -730,23 +730,7 @@ impl AppBundle { if let Platform::Android = self.build.build.platform() { self.build.status_running_gradle(); - // make sure we can execute the gradlew script - #[cfg(unix)] - { - use std::os::unix::prelude::PermissionsExt; - std::fs::set_permissions( - self.build.root_dir().join("gradlew"), - std::fs::Permissions::from_mode(0o755), - )?; - } - - let gradle_exec_name = match cfg!(windows) { - true => "gradlew.bat", - false => "gradlew", - }; - let gradle_exec = self.build.root_dir().join(gradle_exec_name); - - let output = Command::new(gradle_exec) + let output = Command::new(self.gradle_exe()?) .arg("assembleDebug") .current_dir(self.build.root_dir()) .stderr(std::process::Stdio::piped()) @@ -762,6 +746,62 @@ impl AppBundle { Ok(()) } + /// Run bundleRelease and return the path to the `.aab` file + /// + /// https://stackoverflow.com/questions/57072558/whats-the-difference-between-gradlewassemblerelease-gradlewinstallrelease-and + pub(crate) async fn android_gradle_bundle(&self) -> Result { + let output = Command::new(self.gradle_exe()?) + .arg("bundleRelease") + .current_dir(self.build.root_dir()) + .output() + .await + .context("Failed to run gradle bundleRelease")?; + + if !output.status.success() { + return Err(anyhow::anyhow!("Failed to bundleRelease: {output:?}").into()); + } + + let app_release = self + .build + .root_dir() + .join("app") + .join("build") + .join("outputs") + .join("bundle") + .join("release"); + + // Rename it to Name-arch.aab + let from = app_release.join("app-release.aab"); + let to = app_release.join(format!( + "{}-{}.aab", + self.build.krate.bundled_app_name(), + self.build.build.target_args.arch() + )); + + std::fs::rename(from, &to).context("Failed to rename aab")?; + + Ok(to) + } + + fn gradle_exe(&self) -> Result { + // make sure we can execute the gradlew script + #[cfg(unix)] + { + use std::os::unix::prelude::PermissionsExt; + std::fs::set_permissions( + self.build.root_dir().join("gradlew"), + std::fs::Permissions::from_mode(0o755), + )?; + } + + let gradle_exec_name = match cfg!(windows) { + true => "gradlew.bat", + false => "gradlew", + }; + + Ok(self.build.root_dir().join(gradle_exec_name)) + } + pub(crate) fn apk_path(&self) -> PathBuf { self.build .root_dir() diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 09587ddd5..751fc9bce 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -76,11 +76,12 @@ impl Bundle { Platform::Server => bundles.push(bundle.build.root_dir()), Platform::Liveview => bundles.push(bundle.build.root_dir()), - // todo(jon): we can technically create apks (already do...) just need to expose it Platform::Android => { - return Err(Error::UnsupportedFeature( - "Android bundles are not yet supported".into(), - )); + let aab = bundle + .android_gradle_bundle() + .await + .context("Failed to run gradle bundleRelease")?; + bundles.push(aab); } }; diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs index 2c0e83d5b..b9cd10027 100644 --- a/packages/cli/src/cli/run.rs +++ b/packages/cli/src/cli/run.rs @@ -46,7 +46,7 @@ impl RunArgs { tracing::info!("[{platform}]: {msg}") } ServeUpdate::ProcessExited { platform, status } => { - runner.kill(platform).await; + runner.cleanup().await; tracing::info!("[{platform}]: process exited with status: {status:?}"); break; } diff --git a/packages/cli/src/cli/target.rs b/packages/cli/src/cli/target.rs index e5febd656..b0099fb2d 100644 --- a/packages/cli/src/cli/target.rs +++ b/packages/cli/src/cli/target.rs @@ -217,3 +217,15 @@ impl TryFrom for Arch { } } } + +impl std::fmt::Display for Arch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Arch::Arm => "armv7l", + Arch::Arm64 => "aarch64", + Arch::X86 => "i386", + Arch::X64 => "x86_64", + } + .fmt(f) + } +} diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index ba5d4066c..ad62f250d 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -149,6 +149,16 @@ impl DioxusCrate { files } + /// Get the directory where this app can write to for this session that's guaranteed to be stable + /// for the same app. This is useful for emitting state like window position and size. + /// + /// The directory is specific for this app and might be + pub(crate) fn session_cache_dir(&self) -> PathBuf { + self.internal_out_dir() + .join(self.executable_name()) + .join("session-cache") + } + /// Get the outdir specified by the Dioxus.toml, relative to the crate directory. /// We don't support workspaces yet since that would cause a collision of bundles per project. pub(crate) fn crate_out_dir(&self) -> Option { diff --git a/packages/cli/src/error.rs b/packages/cli/src/error.rs index 9a7c5942d..69e0096ab 100644 --- a/packages/cli/src/error.rs +++ b/packages/cli/src/error.rs @@ -33,6 +33,7 @@ pub(crate) enum Error { #[error("Failed to bundle project: {0}")] BundleFailed(#[from] tauri_bundler::Error), + #[allow(unused)] #[error("Unsupported feature: {0}")] UnsupportedFeature(String), diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index 2901455aa..bab073a6d 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -38,6 +38,11 @@ pub(crate) struct AppHandle { pub(crate) server_stdout: Option>>, pub(crate) server_stderr: Option>>, + /// The executables but with some extra entropy in their name so we can run two instances of the + /// same app without causing collisions on the filesystem. + pub(crate) entropy_app_exe: Option, + pub(crate) entropy_server_exe: Option, + /// The virtual directory that assets will be served from /// Used mostly for apk/ipa builds since they live in simulator pub(crate) runtime_asst_dir: Option, @@ -54,6 +59,8 @@ impl AppHandle { server_child: None, server_stdout: None, server_stderr: None, + entropy_app_exe: None, + entropy_server_exe: None, }) } @@ -79,6 +86,15 @@ impl AppHandle { // unset the cargo dirs in the event we're running `dx` locally // since the child process will inherit the env vars, we don't want to confuse the downstream process ("CARGO_MANIFEST_DIR", "".to_string()), + ( + dioxus_cli_config::SESSION_CACHE_DIR, + self.app + .build + .krate + .session_cache_dir() + .display() + .to_string(), + ), ]; if let Some(base_path) = &self.app.build.krate.config.web.app.base_path { @@ -87,7 +103,7 @@ impl AppHandle { // Launch the server if we were given an address to start it on, and the build includes a server. After we // start the server, consume its stdout/stderr. - if let (Some(addr), Some(server)) = (start_fullstack_on_address, self.app.server_exe()) { + if let (Some(addr), Some(server)) = (start_fullstack_on_address, self.server_exe()) { tracing::debug!("Proxying fullstack server from port {:?}", addr); envs.push((dioxus_cli_config::SERVER_IP_ENV, addr.ip().to_string())); envs.push((dioxus_cli_config::SERVER_PORT_ENV, addr.port().to_string())); @@ -147,6 +163,70 @@ impl AppHandle { Ok(()) } + /// Gracefully kill the process and all of its children + /// + /// Uses the `SIGTERM` signal on unix and `taskkill` on windows. + /// This complex logic is necessary for things like window state preservation to work properly. + /// + /// Also wipes away the entropy executables if they exist. + pub(crate) async fn cleanup(&mut self) { + tracing::debug!("Cleaning up process"); + + // Soft-kill the process by sending a sigkill, allowing the process to clean up + self.soft_kill().await; + + // Wipe out the entropy executables if they exist + if let Some(entropy_app_exe) = self.entropy_app_exe.take() { + _ = std::fs::remove_file(entropy_app_exe); + } + + if let Some(entropy_server_exe) = self.entropy_server_exe.take() { + _ = std::fs::remove_file(entropy_server_exe); + } + } + + /// Kill the app and server exes + pub(crate) async fn soft_kill(&mut self) { + use futures_util::FutureExt; + + // Kill any running executables on Windows + let server_process = self.server_child.take(); + let client_process = self.app_child.take(); + let processes = [server_process, client_process] + .into_iter() + .flatten() + .collect::>(); + + for mut process in processes { + let Some(pid) = process.id() else { + _ = process.kill().await; + continue; + }; + + // on unix, we can send a signal to the process to shut down + #[cfg(unix)] + { + _ = Command::new("kill") + .args(["-s", "TERM", &pid.to_string()]) + .spawn(); + } + + // on windows, use the `taskkill` command + #[cfg(windows)] + { + _ = Command::new("taskkill") + .args(["/F", "/PID", &pid.to_string()]) + .spawn(); + } + + // join the wait with a 100ms timeout + futures_util::select! { + _ = process.wait().fuse() => {} + _ = tokio::time::sleep(std::time::Duration::from_millis(1000)).fuse() => {} + }; + } + } + /// Hotreload an asset in the running app. /// /// This will modify the build dir in place! Be careful! We generally assume you want all bundles @@ -236,12 +316,15 @@ impl AppHandle { /// /// Server/liveview/desktop are all basically the same, though fn open_with_main_exe(&mut self, envs: Vec<(&str, String)>) -> Result { - let child = Command::new(self.app.main_exe()) + // Create a new entropy app exe if we need to + let main_exe = self.app_exe(); + let child = Command::new(main_exe) .envs(envs) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .kill_on_drop(true) .spawn()?; + Ok(child) } @@ -650,4 +733,72 @@ We checked the folder: {} }; }); } + + fn make_entropy_path(exe: &PathBuf) -> PathBuf { + let id = uuid::Uuid::new_v4(); + let name = id.to_string(); + let some_entropy = name.split('-').next().unwrap(); + + // Make a copy of the server exe with a new name + let entropy_server_exe = exe.with_file_name(format!( + "{}-{}", + exe.file_name().unwrap().to_str().unwrap(), + some_entropy + )); + + std::fs::copy(exe, &entropy_server_exe).unwrap(); + + entropy_server_exe + } + + fn server_exe(&mut self) -> Option { + let mut server = self.app.server_exe()?; + + // Create a new entropy server exe if we need to + if cfg!(target_os = "windows") || cfg!(target_os = "linux") { + // If we already have an entropy server exe, return it - this is useful for re-opening the same app + if let Some(existing_server) = self.entropy_server_exe.clone() { + return Some(existing_server); + } + + // Otherwise, create a new entropy server exe and save it for re-opning + let entropy_server_exe = Self::make_entropy_path(&server); + self.entropy_server_exe = Some(entropy_server_exe.clone()); + server = entropy_server_exe; + } + + Some(server) + } + + fn app_exe(&mut self) -> PathBuf { + let mut main_exe = self.app.main_exe(); + + // The requirement here is based on the platform, not necessarily our current architecture. + let requires_entropy = match self.app.build.build.platform() { + // When running "bundled", we don't need entropy + Platform::Web => false, + Platform::MacOS => false, + Platform::Ios => false, + Platform::Android => false, + + // But on platforms that aren't running as "bundled", we do. + Platform::Windows => true, + Platform::Linux => true, + Platform::Server => true, + Platform::Liveview => true, + }; + + if requires_entropy || std::env::var("DIOXUS_ENTROPY").is_ok() { + // If we already have an entropy app exe, return it - this is useful for re-opening the same app + if let Some(existing_app_exe) = self.entropy_app_exe.clone() { + return existing_app_exe; + } + + let entropy_app_exe = Self::make_entropy_path(&main_exe); + self.entropy_app_exe = Some(entropy_app_exe.clone()); + main_exe = entropy_app_exe; + } + + main_exe + } } diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 5f960bc98..f19eff965 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -107,11 +107,6 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> { } else if runner.should_full_rebuild { tracing::info!(dx_src = ?TraceSrc::Dev, "Full rebuild: {}", file); - // Kill any running executables on Windows - if cfg!(windows) { - runner.kill_all().await; - } - // We're going to kick off a new build, interrupting the current build if it's ongoing builder.rebuild(args.build_arguments.clone()); @@ -199,8 +194,6 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> { - To exit the server, press `ctrl+c`"# ); } - - runner.kill(platform).await; } ServeUpdate::StdoutReceived { platform, msg } => { @@ -221,11 +214,6 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> { // `Hotreloading:` to keep the alignment during long edit sessions tracing::info!("Full rebuild: triggered manually"); - // Kill any running executables on Windows - if cfg!(windows) { - runner.kill_all().await; - } - builder.rebuild(args.build_arguments.clone()); runner.file_map.force_rebuild(); devserver.send_reload_start().await; @@ -261,9 +249,10 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> { } }; + _ = runner.cleanup().await; _ = devserver.shutdown().await; - _ = screen.shutdown(); builder.abort_all(); + _ = screen.shutdown(); if let Err(err) = err { eprintln!("Exiting with error: {}", err); diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 5c0a744eb..058e46556 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -5,17 +5,16 @@ use crate::{ use dioxus_core::internal::TemplateGlobalKey; use dioxus_devtools_types::HotReloadMsg; use dioxus_html::HtmlCtx; -use futures_util::{future::OptionFuture, stream::FuturesUnordered, FutureExt}; +use futures_util::future::OptionFuture; use ignore::gitignore::Gitignore; use std::{ collections::{HashMap, HashSet}, net::SocketAddr, path::PathBuf, }; -use tokio_stream::StreamExt; pub(crate) struct AppRunner { - pub(crate) running: HashMap, + pub(crate) running: Option, pub(crate) krate: DioxusCrate, pub(crate) file_map: HotreloadFilemap, pub(crate) ignore: Gitignore, @@ -44,52 +43,47 @@ impl AppRunner { runner.fill_filemap(krate); } + // Ensure the session cache dir exists and is empty + runner.flush_session_cache(); + runner } pub(crate) async fn wait(&mut self) -> ServeUpdate { // If there are no running apps, we can just return pending to avoid deadlocking - if self.running.is_empty() { + let Some(handle) = self.running.as_mut() else { return futures_util::future::pending().await; - } + }; - self.running - .iter_mut() - .map(|(platform, handle)| async { - use ServeUpdate::*; - let platform = *platform; - tokio::select! { - Some(Ok(Some(msg))) = OptionFuture::from(handle.app_stdout.as_mut().map(|f| f.next_line())) => { - StdoutReceived { platform, msg } - }, - Some(Ok(Some(msg))) = OptionFuture::from(handle.app_stderr.as_mut().map(|f| f.next_line())) => { - StderrReceived { platform, msg } - }, - Some(status) = OptionFuture::from(handle.app_child.as_mut().map(|f| f.wait())) => { - match status { - Ok(status) => ProcessExited { status, platform }, - Err(_err) => todo!("handle error in process joining?"), - } - } - Some(Ok(Some(msg))) = OptionFuture::from(handle.server_stdout.as_mut().map(|f| f.next_line())) => { - StdoutReceived { platform: Platform::Server, msg } - }, - Some(Ok(Some(msg))) = OptionFuture::from(handle.server_stderr.as_mut().map(|f| f.next_line())) => { - StderrReceived { platform: Platform::Server, msg } - }, - Some(status) = OptionFuture::from(handle.server_child.as_mut().map(|f| f.wait())) => { - match status { - Ok(status) => ProcessExited { status, platform }, - Err(_err) => todo!("handle error in process joining?"), - } - } - else => futures_util::future::pending().await + use ServeUpdate::*; + let platform = handle.app.build.build.platform(); + tokio::select! { + Some(Ok(Some(msg))) = OptionFuture::from(handle.app_stdout.as_mut().map(|f| f.next_line())) => { + StdoutReceived { platform, msg } + }, + Some(Ok(Some(msg))) = OptionFuture::from(handle.app_stderr.as_mut().map(|f| f.next_line())) => { + StderrReceived { platform, msg } + }, + Some(status) = OptionFuture::from(handle.app_child.as_mut().map(|f| f.wait())) => { + match status { + Ok(status) => ProcessExited { status, platform }, + Err(_err) => todo!("handle error in process joining?"), } - }) - .collect::>() - .next() - .await - .expect("Stream to pending if not empty") + } + Some(Ok(Some(msg))) = OptionFuture::from(handle.server_stdout.as_mut().map(|f| f.next_line())) => { + StdoutReceived { platform: Platform::Server, msg } + }, + Some(Ok(Some(msg))) = OptionFuture::from(handle.server_stderr.as_mut().map(|f| f.next_line())) => { + StderrReceived { platform: Platform::Server, msg } + }, + Some(status) = OptionFuture::from(handle.server_child.as_mut().map(|f| f.wait())) => { + match status { + Ok(status) => ProcessExited { status, platform }, + Err(_err) => todo!("handle error in process joining?"), + } + } + else => futures_util::future::pending().await + } } /// Finally "bundle" this app and return a handle to it @@ -100,16 +94,9 @@ impl AppRunner { fullstack_address: Option, should_open_web: bool, ) -> Result<&AppHandle> { - let platform = app.build.build.platform(); - // Drop the old handle - // todo(jon): we should instead be sending the kill signal rather than dropping the process - // This would allow a more graceful shutdown and fix bugs like desktop not retaining its size - self.kill(platform).await; - - // wait a tiny sec for the processes to die so we don't have fullstack servers on top of each other - // todo(jon): we should allow rebinding to the same port in fullstack itself - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + // This is a more forceful kill than soft_kill since the app entropy will be wiped + self.cleanup().await; // Add some cute logging if self.builds_opened == 0 { @@ -132,81 +119,32 @@ impl AppRunner { .await?; self.builds_opened += 1; - self.running.insert(platform, handle); + self.running = Some(handle); - Ok(self.running.get(&platform).unwrap()) - } - - /// Gracefully kill the process and all of its children - /// - /// Uses the `SIGTERM` signal on unix and `taskkill` on windows. - /// This complex logic is necessary for things like window state preservation to work properly. - pub(crate) async fn kill(&mut self, platform: Platform) { - use tokio::process::Command; - - let Some(mut process) = self.running.remove(&platform) else { - return; - }; - - let server_process = process.server_child.take(); - let client_process = process.app_child.take(); - let processes = [server_process, client_process] - .into_iter() - .flatten() - .collect::>(); - - for mut process in processes { - let Some(pid) = process.id() else { - _ = process.kill().await; - continue; - }; - - // on unix, we can send a signal to the process to shut down - #[cfg(unix)] - { - _ = Command::new("kill") - .args(["-s", "TERM", &pid.to_string()]) - .spawn(); - } - - // on windows, use the `taskkill` command - #[cfg(windows)] - { - _ = Command::new("taskkill") - .args(["/F", "/PID", &pid.to_string()]) - .spawn(); - } - - // join the wait with a 100ms timeout - futures_util::select! { - _ = process.wait().fuse() => {} - _ = tokio::time::sleep(std::time::Duration::from_millis(1000)).fuse() => {} - }; - } - } - - pub(crate) async fn kill_all(&mut self) { - let keys = self.running.keys().cloned().collect::>(); - for platform in keys { - self.kill(platform).await; - } + Ok(self.running.as_ref().unwrap()) } /// Open an existing app bundle, if it exists pub(crate) async fn open_existing(&mut self, devserver: &WebServer) -> Result<()> { let fullstack_address = devserver.proxied_server_address(); - if let Some((_, app)) = self - .running - .iter_mut() - .find(|(platform, _)| **platform != Platform::Server) - { - app.open(devserver.devserver_address(), fullstack_address, true) + if let Some(runner) = self.running.as_mut() { + runner.soft_kill().await; + runner + .open(devserver.devserver_address(), fullstack_address, true) .await?; } + Ok(()) } + /// Shutdown all the running processes + pub(crate) async fn cleanup(&mut self) { + if let Some(mut process) = self.running.take() { + process.cleanup().await; + } + } + pub(crate) async fn attempt_hot_reload( &mut self, modified_files: Vec, @@ -237,7 +175,7 @@ impl AppRunner { } // Otherwise, it might be an asset and we should look for it in all the running apps - for runner in self.running.values() { + if let Some(runner) = self.running.as_mut() { if let Some(bundled_name) = runner.hotreload_bundled_asset(&path).await { // todo(jon): don't hardcode this here let asset_relative = PathBuf::from("/assets/").join(bundled_name); @@ -329,27 +267,29 @@ impl AppRunner { } pub(crate) async fn client_connected(&mut self) { - for (platform, runner) in self.running.iter_mut() { - // Assign the runtime asset dir to the runner - if *platform == Platform::Ios { - // xcrun simctl get_app_container booted com.dioxuslabs - let res = tokio::process::Command::new("xcrun") - .arg("simctl") - .arg("get_app_container") - .arg("booted") - .arg(runner.app.build.krate.bundle_identifier()) - .output() - .await; + let Some(handle) = self.running.as_mut() else { + return; + }; - if let Ok(res) = res { - tracing::trace!("Using runtime asset dir: {:?}", res); + // Assign the runtime asset dir to the runner + if handle.app.build.build.platform() == Platform::Ios { + // xcrun simctl get_app_container booted com.dioxuslabs + let res = tokio::process::Command::new("xcrun") + .arg("simctl") + .arg("get_app_container") + .arg("booted") + .arg(handle.app.build.krate.bundle_identifier()) + .output() + .await; - if let Ok(out) = String::from_utf8(res.stdout) { - let out = out.trim(); + if let Ok(res) = res { + tracing::trace!("Using runtime asset dir: {:?}", res); - tracing::trace!("Setting Runtime asset dir: {out:?}"); - runner.runtime_asst_dir = Some(PathBuf::from(out)); - } + if let Ok(out) = String::from_utf8(res.stdout) { + let out = out.trim(); + + tracing::trace!("Setting Runtime asset dir: {out:?}"); + handle.runtime_asst_dir = Some(PathBuf::from(out)); } } } @@ -388,4 +328,10 @@ impl AppRunner { } } } + + fn flush_session_cache(&self) { + let cache_dir = self.krate.session_cache_dir(); + _ = std::fs::remove_dir_all(&cache_dir); + _ = std::fs::create_dir_all(&cache_dir); + } } diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 555399906..a23672793 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -593,20 +593,6 @@ fn hide_last_window(window: &Window) { /// Return the location of a tempfile with our window state in it such that we can restore it later fn restore_file() -> std::path::PathBuf { - /// Get the name of the program or default to "dioxus" so we can hash it - fn get_prog_name_or_default() -> Option { - Some( - std::env::current_exe() - .ok()? - .file_name()? - .to_str()? - .to_string(), - ) - } - - let name = get_prog_name_or_default().unwrap_or_else(|| "dioxus".to_string()); - let hashed_id = name.chars().map(|c| c as usize).sum::(); - let mut path = std::env::temp_dir(); - path.push(format!("{}-window-state.json", hashed_id)); - path + let dir = dioxus_cli_config::session_cache_dir().unwrap_or_else(std::env::temp_dir); + dir.join("window-state.json") }