diff --git a/apps/app/Info.plist b/apps/app/Info.plist index c7d4a53b0..49ab096f4 100644 --- a/apps/app/Info.plist +++ b/apps/app/Info.plist @@ -27,7 +27,7 @@ Owner LSItemContentTypes - ModrinthApp-type + com.modrinth.theseus-type NSDocumentClass NSDocument @@ -45,7 +45,7 @@ UTTypeIcons UTTypeIdentifier - ModrinthApp-type + com.modrinth.theseus-type UTTypeTagSpecification public.filename-extension diff --git a/apps/app/src/api/utils.rs b/apps/app/src/api/utils.rs index a483b7b2d..05358cc2a 100644 --- a/apps/app/src/api/utils.rs +++ b/apps/app/src/api/utils.rs @@ -5,7 +5,8 @@ use theseus::{ }; use crate::api::Result; -use std::{env, path::PathBuf}; +use dashmap::DashMap; +use std::path::PathBuf; pub fn init() -> tauri::plugin::TauriPlugin { tauri::plugin::Builder::new("utils") @@ -44,7 +45,7 @@ pub enum OS { // Values provided should not be used directly, as they are not guaranteed to be up-to-date #[tauri::command] pub async fn progress_bars_list( -) -> Result> { +) -> Result> { let res = theseus::EventState::list_progress_bars().await?; Ok(res) } @@ -92,10 +93,28 @@ pub fn show_launcher_logs_folder() { // This should be called once and only when the app is done booting up and ready to receive a command // Returns a Command struct- see events.js #[tauri::command] +#[cfg(target_os = "macos")] +pub async fn get_opening_command( + state: tauri::State<'_, crate::macos::deep_link::InitialPayload>, +) -> Result> { + let payload = state.payload.lock().await; + + return if let Some(payload) = payload.as_ref() { + tracing::info!("opening command {payload}"); + + Ok(Some(handler::parse_command(payload).await?)) + } else { + Ok(None) + }; +} + +#[cfg(not(target_os = "macos"))] pub async fn get_opening_command() -> Result> { // Tauri is not CLI, we use arguments as path to file to call let cmd_arg = env::args_os().nth(1); + tracing::info!("opening command {cmd_arg:?}"); + let cmd_arg = cmd_arg.map(|path| path.to_string_lossy().to_string()); if let Some(cmd) = cmd_arg { tracing::debug!("Opening command: {:?}", cmd); diff --git a/apps/app/src/macos/deep_link.rs b/apps/app/src/macos/deep_link.rs new file mode 100644 index 000000000..5383312f1 --- /dev/null +++ b/apps/app/src/macos/deep_link.rs @@ -0,0 +1,6 @@ +use std::sync::Arc; +use tokio::sync::Mutex; + +pub struct InitialPayload { + pub payload: Arc>>, +} diff --git a/apps/app/src/macos/mod.rs b/apps/app/src/macos/mod.rs index 229c698ce..a645c8def 100644 --- a/apps/app/src/macos/mod.rs +++ b/apps/app/src/macos/mod.rs @@ -1,2 +1,3 @@ +pub mod deep_link; pub mod delegate; pub mod window_ext; diff --git a/apps/app/src/main.rs b/apps/app/src/main.rs index 4f8ab49e6..dbe2630c5 100644 --- a/apps/app/src/main.rs +++ b/apps/app/src/main.rs @@ -89,16 +89,64 @@ fn main() { })) .plugin(tauri_plugin_window_state::Builder::default().build()) .setup(|app| { - // Register deep link handler, allowing reading of modrinth:// links - if let Err(e) = tauri_plugin_deep_link::register( + #[cfg(target_os = "macos")] + let res = { + use macos::deep_link::InitialPayload; + let mtx = std::sync::Arc::new(tokio::sync::Mutex::new(None)); + + app.manage(InitialPayload { + payload: mtx.clone(), + }); + + let mtx_copy = mtx.clone(); + macos::delegate::register_open_file(move |filename| { + let mtx_copy = mtx_copy.clone(); + + tauri::async_runtime::spawn(async move { + tracing::info!("Handling file open {request}"); + + let mut payload = mtx_copy.lock().await; + if payload.is_none() { + *payload = Some(filename.clone()); + } + + let _ = api::utils::handle_command(filename).await; + }); + }) + .unwrap(); + + let mtx_copy = mtx.clone(); + tauri_plugin_deep_link::register( + "modrinth", + move |request: String| { + let mtx_copy = mtx_copy.clone(); + + tauri::async_runtime::spawn(async move { + tracing::info!("Handling deep link {request}"); + + let mut payload = mtx_copy.lock().await; + if payload.is_none() { + *payload = Some(request.clone()); + } + + let _ = api::utils::handle_command(request).await; + }); + }, + ) + }; + + #[cfg(not(target_os = "macos"))] + let res = tauri_plugin_deep_link::register( "modrinth", |request: String| { + tracing::info!("Handling deep link {request}"); tauri::async_runtime::spawn(api::utils::handle_command( request, )); }, - ) { - // Allow it to fail- see https://github.com/FabianLars/tauri-plugin-deep-link/issues/19 + ); + + if let Err(e) = res { tracing::error!("Error registering deep link handler: {}", e); } @@ -117,13 +165,6 @@ fn main() { use macos::window_ext::WindowExt; window.set_transparent_titlebar(true); window.position_traffic_lights(9.0, 16.0); - - macos::delegate::register_open_file(|filename| { - tauri::async_runtime::spawn( - api::utils::handle_command(filename), - ); - }) - .unwrap(); } } diff --git a/packages/app-lib/src/event/emit.rs b/packages/app-lib/src/event/emit.rs index 2b5c025de..ee20683e1 100644 --- a/packages/app-lib/src/event/emit.rs +++ b/packages/app-lib/src/event/emit.rs @@ -66,7 +66,7 @@ pub async fn init_loading_unsafe( let event_state = crate::EventState::get().await?; let key = LoadingBarId(Uuid::new_v4()); - event_state.loading_bars.write().await.insert( + event_state.loading_bars.insert( key.0, LoadingBar { loading_bar_uuid: key.0, @@ -121,7 +121,7 @@ pub async fn edit_loading( ) -> crate::Result<()> { let event_state = crate::EventState::get().await?; - if let Some(bar) = event_state.loading_bars.write().await.get_mut(&id.0) { + if let Some(mut bar) = event_state.loading_bars.get_mut(&id.0) { bar.bar_type = bar_type; bar.total = total; bar.message = title.to_string(); @@ -152,8 +152,7 @@ pub async fn emit_loading( ) -> crate::Result<()> { let event_state = crate::EventState::get().await?; - let mut loading_bar = event_state.loading_bars.write().await; - let loading_bar = match loading_bar.get_mut(&key.0) { + let mut loading_bar = match event_state.loading_bars.get_mut(&key.0) { Some(f) => f, None => { return Err(EventError::NoLoadingBar(key.0).into()); diff --git a/packages/app-lib/src/event/mod.rs b/packages/app-lib/src/event/mod.rs index 974416955..21b3e8a26 100644 --- a/packages/app-lib/src/event/mod.rs +++ b/packages/app-lib/src/event/mod.rs @@ -1,8 +1,8 @@ //! Theseus state management system +use dashmap::DashMap; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use tokio::sync::OnceCell; -use tokio::sync::RwLock; use uuid::Uuid; pub mod emit; @@ -14,7 +14,7 @@ pub struct EventState { /// Tauri app #[cfg(feature = "tauri")] pub app: tauri::AppHandle, - pub loading_bars: RwLock>, + pub loading_bars: DashMap, } impl EventState { @@ -24,7 +24,7 @@ impl EventState { .get_or_try_init(|| async { Ok(Arc::new(Self { app, - loading_bars: RwLock::new(HashMap::new()), + loading_bars: DashMap::new(), })) }) .await @@ -36,7 +36,7 @@ impl EventState { EVENT_STATE .get_or_try_init(|| async { Ok(Arc::new(Self { - loading_bars: RwLock::new(HashMap::new()), + loading_bars: DashMap::new(), })) }) .await @@ -55,17 +55,10 @@ impl EventState { } // Values provided should not be used directly, as they are clones and are not guaranteed to be up-to-date - pub async fn list_progress_bars() -> crate::Result> + pub async fn list_progress_bars() -> crate::Result> { let value = Self::get().await?; - let read = value.loading_bars.read().await; - - let mut display_list: HashMap = HashMap::new(); - for (uuid, loading_bar) in read.iter() { - display_list.insert(*uuid, loading_bar.clone()); - } - - Ok(display_list) + Ok(value.loading_bars.clone()) } #[cfg(feature = "tauri")] @@ -100,10 +93,10 @@ impl Drop for LoadingBarId { let loader_uuid = self.0; tokio::spawn(async move { if let Ok(event_state) = EventState::get().await { - let mut bars = event_state.loading_bars.write().await; - #[cfg(any(feature = "tauri", feature = "cli"))] - if let Some(bar) = bars.remove(&loader_uuid) { + if let Some((_, bar)) = + event_state.loading_bars.remove(&loader_uuid) + { #[cfg(feature = "tauri")] { let loader_uuid = bar.loading_bar_uuid; @@ -135,7 +128,7 @@ impl Drop for LoadingBarId { } #[cfg(not(any(feature = "tauri", feature = "cli")))] - bars.remove(&loader_uuid); + event_state.loading_bars.remove(&loader_uuid); } }); }