diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 2884eca..4e53a6f 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -7,7 +7,8 @@ "control-panel-action-*", "debug", "onboarding", - "scene" + "scene", + "splashscreen" ], "permissions": [ "core:default", diff --git a/src-tauri/src/application/mod.rs b/src-tauri/src/application/mod.rs index ff42539..44fb1b3 100644 --- a/src-tauri/src/application/mod.rs +++ b/src-tauri/src/application/mod.rs @@ -14,6 +14,8 @@ pub async fn init(handle: &AppHandle) -> Result<(), String> { .await .map_err(crate::db::command_error)?; + crate::ui::splashscreen::close(handle).await?; + if settings.onboarding_done { start(handle).await; if cfg!(target_os = "macos") && !accessibility_permission_granted { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5026e3d..ba2fd0a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -19,8 +19,7 @@ mod ui; mod updater; mod user; -async fn launch_app(app: &tauri::App) -> Result<(), Box> { - let handle = app.handle(); +async fn launch_app(handle: &tauri::AppHandle) -> Result<(), Box> { updater::init(handle).await; db::init(handle).await?; scene_configuration::init(handle).await?; @@ -54,7 +53,16 @@ pub fn run() { .invoke_handler(specta_builder.invoke_handler()) .setup(move |app| { specta_builder.mount_events(app); - tauri::async_runtime::block_on(launch_app(app))?; + let handle = app.handle().clone(); + ui::splashscreen::open(&handle)?; + tauri::async_runtime::spawn(async move { + let launch_result = launch_app(&handle).await.map_err(|error| error.to_string()); + + if let Err(error) = launch_result { + eprintln!("failed to launch application: {error}"); + handle.exit(1); + } + }); Ok(()) }) .build(tauri::generate_context!()) diff --git a/src-tauri/src/ui/mod.rs b/src-tauri/src/ui/mod.rs index 259e091..6425497 100644 --- a/src-tauri/src/ui/mod.rs +++ b/src-tauri/src/ui/mod.rs @@ -5,6 +5,7 @@ pub mod control_panel; mod debug; pub mod onboarding; pub mod scene; +pub mod splashscreen; mod tray; pub fn init(app_handle: &AppHandle) { diff --git a/src-tauri/src/ui/splashscreen/mod.rs b/src-tauri/src/ui/splashscreen/mod.rs new file mode 100644 index 0000000..fc70bf9 --- /dev/null +++ b/src-tauri/src/ui/splashscreen/mod.rs @@ -0,0 +1,42 @@ +use std::time::{Duration, Instant}; + +use tauri::{AppHandle, Manager, WebviewUrl}; + +const WINDOW_LABEL: &str = "splashscreen"; +const MINIMUM_DISPLAY_DURATION: Duration = Duration::from_secs(2); + +struct SplashscreenState { + opened_at: Instant, +} + +pub fn open(handle: &AppHandle) -> Result<(), String> { + tauri::WebviewWindowBuilder::new(handle, WINDOW_LABEL, WebviewUrl::App("/splash.html".into())) + .title("Friendolls") + .inner_size(500.0, 290.0) + .resizable(false) + .minimizable(false) + .maximizable(false) + .decorations(false) + .shadow(false) + .skip_taskbar(true) + .always_on_top(true) + .center() + .build() + .map_err(|error| error.to_string())?; + + handle.manage(SplashscreenState { + opened_at: Instant::now(), + }); + Ok(()) +} + +pub async fn close(handle: &AppHandle) -> Result<(), String> { + let opened_at = handle.state::().opened_at; + tokio::time::sleep(MINIMUM_DISPLAY_DURATION.saturating_sub(opened_at.elapsed())).await; + + if let Some(window) = handle.get_webview_window(WINDOW_LABEL) { + window.close().map_err(|error| error.to_string())?; + } + + Ok(()) +} diff --git a/static/splash.html b/static/splash.html new file mode 100644 index 0000000..aff674a --- /dev/null +++ b/static/splash.html @@ -0,0 +1,31 @@ + + + + + Friendolls Splash + + + +
+ + + + + diff --git a/static/splash.png b/static/splash.png new file mode 100644 index 0000000..0dac2b3 Binary files /dev/null and b/static/splash.png differ