Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions apps/desktop/src-tauri/src/deeplink_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ pub enum DeepLinkAction {
OpenSettings {
page: Option<String>,
},
PauseRecording,
ResumeRecording,
SwitchMicrophone {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For SwitchMicrophone / SwitchCamera, using Option means a missing/null field in the deeplink will remove the device. If that’s not intended, consider making these fields required or treating None as a no-op/error to avoid accidental device disable.

mic_label: Option<String>,
},
SwitchCamera {
camera: Option<DeviceOrModelID>,
},
}

pub fn handle(app_handle: &AppHandle, urls: Vec<Url>) {
Expand Down Expand Up @@ -152,6 +160,41 @@ impl DeepLinkAction {
DeepLinkAction::OpenSettings { page } => {
crate::show_window(app.clone(), ShowCapWindow::Settings { page }).await
}
DeepLinkAction::PauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}
Comment on lines 163 to 165
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding the state.read() guard across an .await can deadlock / block other actions. Consider grabbing the recording handle first so the lock is dropped before calling pause().

Suggested change
DeepLinkAction::PauseRecording => {
let state = app.state::<ArcLock<App>>();
let guard = state.read().await;
if let Some(recording) = guard.current_recording() {
recording.pause().await.map_err(|e| e.to_string())
} else {
Ok(())
}
}
DeepLinkAction::PauseRecording => {
let state = app.state::<ArcLock<App>>();
let recording = {
let guard = state.read().await;
guard.current_recording()
};
if let Some(recording) = recording {
recording.pause().await.map_err(|e| e.to_string())
} else {
Ok(())
}
}

DeepLinkAction::ResumeRecording => {
crate::recording::resume_recording(app.clone(), app.state()).await
}
Comment on lines 166 to 168
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same idea here: drop the read lock before awaiting resume().

Suggested change
DeepLinkAction::ResumeRecording => {
let state = app.state::<ArcLock<App>>();
let guard = state.read().await;
if let Some(recording) = guard.current_recording() {
recording.resume().await.map_err(|e| e.to_string())
} else {
Ok(())
}
}
DeepLinkAction::ResumeRecording => {
let state = app.state::<ArcLock<App>>();
let recording = {
let guard = state.read().await;
guard.current_recording()
};
if let Some(recording) = recording {
recording.resume().await.map_err(|e| e.to_string())
} else {
Ok(())
}
}

DeepLinkAction::SwitchMicrophone { mic_label } => {
if let Some(ref label) = mic_label {
let available_mics = cap_recording::feeds::microphone::MicrophoneFeed::list();
if !available_mics.contains_key(label) {
return Err(format!("Microphone with label \"{}\" not found", label));
}
}

let state = app.state::<ArcLock<App>>();
crate::set_mic_input(state, mic_label).await
}
DeepLinkAction::SwitchCamera { camera } => {
if let Some(ref id) = camera {
let cameras: Vec<_> = cap_camera::list_cameras().collect();
let exists = cameras.iter().any(|info| match id {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing: you can avoid allocating Vec here by using the iterator directly.

Suggested change
let exists = cameras.iter().any(|info| match id {
let exists = cap_camera::list_cameras().any(|info| match id {
DeviceOrModelID::DeviceID(device_id) => info.device_id() == device_id,
DeviceOrModelID::ModelID(model_id) => {
info.model_id().is_some_and(|existing| existing == model_id)
}
});

DeviceOrModelID::DeviceID(device_id) => info.device_id() == device_id,
DeviceOrModelID::ModelID(model_id) => {
info.model_id().is_some_and(|existing| existing == model_id)
}
});

if !exists {
return Err(format!("Camera with ID \"{:?}\" not found", id));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor UX: {:?} here ends up returning DeviceID(...) / ModelID(...) in the string. A variant-specific message reads nicer.

Suggested change
return Err(format!("Camera with ID \"{:?}\" not found", id));
return Err(match id {
DeviceOrModelID::DeviceID(device_id) => {
format!("Camera with device ID \"{}\" not found", device_id)
}
DeviceOrModelID::ModelID(model_id) => {
format!("Camera with model ID \"{}\" not found", model_id)
}
});

}
}

let state = app.state::<ArcLock<App>>();
crate::set_camera_input(app.clone(), state, camera).await
}
}
}
}