diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 6eb7f4c893..008aa35c04 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -46,17 +46,16 @@ pub enum DeepLinkAction { mode: RecordingMode, }, StopRecording, - #[cfg(debug_assertions)] PauseRecording, - #[cfg(debug_assertions)] ResumeRecording, - #[cfg(debug_assertions)] + SwitchMicrophone { + mic_label: Option, + }, OpenCamera { camera: DeviceOrModelID, }, - #[cfg(debug_assertions)] SetCameraPreviewState { - state: CameraPreviewState, + state: crate::camera::CameraPreviewState, }, OpenEditor { project_path: PathBuf, @@ -164,21 +163,51 @@ impl TryFrom<&Url> for DeepLinkAction { .map_err(|_| ActionParseFromUrlError::Invalid); } - match url.domain() { - Some("action") => {} - Some(_) => return Err(ActionParseFromUrlError::NotAction), - None => return Err(ActionParseFromUrlError::Invalid), + if !matches!(url.scheme(), "cap" | "cap-desktop") { + return Err(ActionParseFromUrlError::NotAction); } - let params = url - .query_pairs() - .collect::>(); - let json_value = params - .get("value") - .ok_or(ActionParseFromUrlError::Invalid)?; - let action: Self = serde_json::from_str(json_value) - .map_err(|e| ActionParseFromUrlError::ParseFailed(e.to_string()))?; - Ok(action) + match url.host_str() { + Some("action") => { + let params = url + .query_pairs() + .collect::>(); + let json_value = params + .get("value") + .ok_or(ActionParseFromUrlError::Invalid)?; + let action: Self = serde_json::from_str(json_value) + .map_err(|e| ActionParseFromUrlError::ParseFailed(e.to_string()))?; + Ok(action) + } + Some("stop" | "stop-recording") => Ok(Self::StopRecording), + Some("pause" | "pause-recording") => Ok(Self::PauseRecording), + Some("resume" | "resume-recording") => Ok(Self::ResumeRecording), + Some("switch-mic" | "switch-microphone") => { + let params = url + .query_pairs() + .collect::>(); + let mic_label = params + .get("mic_label") + .or_else(|| params.get("label")) + .and_then(|s| (!s.is_empty()).then(|| s.to_string())); + Ok(Self::SwitchMicrophone { mic_label }) + } + Some("open-camera" | "switch-camera") => { + let params = url + .query_pairs() + .collect::>(); + let device_id = params + .get("id") + .or_else(|| params.get("camera")) + .and_then(|s| (!s.is_empty()).then(|| s.to_string())) + .ok_or(ActionParseFromUrlError::Invalid)?; + Ok(Self::OpenCamera { + camera: DeviceOrModelID::DeviceID(device_id), + }) + } + Some(_) => Err(ActionParseFromUrlError::NotAction), + None => Err(ActionParseFromUrlError::Invalid), + } } } @@ -244,15 +273,18 @@ impl DeepLinkAction { DeepLinkAction::StopRecording => { crate::recording::stop_recording(app.clone(), app.state()).await } - #[cfg(debug_assertions)] DeepLinkAction::PauseRecording => { crate::recording::pause_recording(app.clone(), app.state()).await } - #[cfg(debug_assertions)] DeepLinkAction::ResumeRecording => { crate::recording::resume_recording(app.clone(), app.state()).await } - #[cfg(debug_assertions)] + DeepLinkAction::SwitchMicrophone { mic_label } => { + let state = app.state::>(); + crate::set_mic_input(state, mic_label) + .await + .map_err(|e| e.to_string()) + } DeepLinkAction::OpenCamera { camera } => { crate::set_camera_input( app.clone(), @@ -282,7 +314,6 @@ impl DeepLinkAction { Ok(()) } - #[cfg(debug_assertions)] DeepLinkAction::SetCameraPreviewState { state } => { crate::set_camera_preview_state(app.state(), state).await } @@ -358,7 +389,6 @@ mod tests { ); } - #[cfg(debug_assertions)] #[test] fn parses_pause_and_resume_action_urls() { let pause_url = Url::parse("cap-desktop://action?value=%22pause_recording%22").unwrap(); @@ -374,6 +404,35 @@ mod tests { ); } + #[test] + fn parses_direct_scheme_urls() { + let pause_url = Url::parse("cap://pause").unwrap(); + let resume_url = Url::parse("cap://resume").unwrap(); + let mic_url = Url::parse("cap://switch-mic?label=Shure%20MV7%2B").unwrap(); + let camera_url = Url::parse("cap://switch-camera?id=cam-1").unwrap(); + + assert_eq!( + DeepLinkAction::try_from(&pause_url), + Ok(DeepLinkAction::PauseRecording) + ); + assert_eq!( + DeepLinkAction::try_from(&resume_url), + Ok(DeepLinkAction::ResumeRecording) + ); + assert_eq!( + DeepLinkAction::try_from(&mic_url), + Ok(DeepLinkAction::SwitchMicrophone { + mic_label: Some("Shure MV7+".to_string()) + }) + ); + assert_eq!( + DeepLinkAction::try_from(&camera_url), + Ok(DeepLinkAction::OpenCamera { + camera: DeviceOrModelID::DeviceID("cam-1".to_string()) + }) + ); + } + #[cfg(debug_assertions)] #[test] fn parses_area_recording_action_url() { diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 9af8659e42..fd6c01b612 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -36,7 +36,7 @@ "updater": { "active": false, "pubkey": "" }, "deep-link": { "desktop": { - "schemes": ["cap-desktop"] + "schemes": ["cap-desktop", "cap"] } } },