mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-23 06:49:24 +01:00
Compare commits
1 Commits
main
...
steve/fix/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1759e0e56e |
@@ -17,6 +17,8 @@ Information about release notes of Coco App is provided here.
|
|||||||
|
|
||||||
### ✈️ Improvements
|
### ✈️ Improvements
|
||||||
|
|
||||||
|
- refactor: add a timeout to open() #1025
|
||||||
|
|
||||||
## 0.10.0 (2025-12-19)
|
## 0.10.0 (2025-12-19)
|
||||||
|
|
||||||
### ❌ Breaking changes
|
### ❌ Breaking changes
|
||||||
|
|||||||
@@ -148,152 +148,171 @@ pub(crate) async fn open(
|
|||||||
extra_args: Option<HashMap<String, Json>>,
|
extra_args: Option<HashMap<String, Json>>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
use crate::util::open as homemade_tauri_shell_open;
|
use crate::util::open as homemade_tauri_shell_open;
|
||||||
use std::process::Command;
|
use tokio::process::Command;
|
||||||
|
use tokio::time::Duration;
|
||||||
|
use tokio::time::timeout;
|
||||||
|
|
||||||
match on_opened {
|
let on_opened_clone = on_opened.clone();
|
||||||
OnOpened::Application { app_path } => {
|
// Put the main logic in an async closure so that we can `time::timeout()`
|
||||||
log::debug!("open application [{}]", app_path);
|
// it
|
||||||
|
let async_closure = async move {
|
||||||
|
match on_opened_clone {
|
||||||
|
OnOpened::Application { app_path } => {
|
||||||
|
log::debug!("open application [{}]", app_path);
|
||||||
|
|
||||||
homemade_tauri_shell_open(tauri_app_handle.clone(), app_path).await?
|
homemade_tauri_shell_open(tauri_app_handle.clone(), app_path).await?
|
||||||
}
|
}
|
||||||
OnOpened::Document { url } => {
|
OnOpened::Document { url } => {
|
||||||
log::debug!("open document [{}]", url);
|
log::debug!("open document [{}]", url);
|
||||||
|
|
||||||
homemade_tauri_shell_open(tauri_app_handle.clone(), url).await?
|
homemade_tauri_shell_open(tauri_app_handle.clone(), url).await?
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
OnOpened::WindowManagementAction { action } => {
|
OnOpened::WindowManagementAction { action } => {
|
||||||
log::debug!("perform Window Management action [{:?}]", action);
|
log::debug!("perform Window Management action [{:?}]", action);
|
||||||
|
|
||||||
crate::extension::built_in::window_management::perform_action_on_main_thread(
|
crate::extension::built_in::window_management::perform_action_on_main_thread(
|
||||||
&tauri_app_handle,
|
&tauri_app_handle,
|
||||||
action,
|
action,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
OnOpened::Extension(ext_on_opened) => {
|
OnOpened::Extension(ext_on_opened) => {
|
||||||
// Apply the settings that would affect open behavior
|
// Apply the settings that would affect open behavior
|
||||||
if let Some(settings) = ext_on_opened.settings {
|
if let Some(settings) = ext_on_opened.settings {
|
||||||
if let Some(should_hide) = settings.hide_before_open {
|
if let Some(should_hide) = settings.hide_before_open {
|
||||||
if should_hide {
|
if should_hide {
|
||||||
crate::hide_coco(tauri_app_handle.clone()).await;
|
crate::hide_coco(tauri_app_handle.clone()).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
let permission = ext_on_opened.permission;
|
||||||
let permission = ext_on_opened.permission;
|
|
||||||
|
|
||||||
match ext_on_opened.ty {
|
match ext_on_opened.ty {
|
||||||
ExtensionOnOpenedType::Command { action } => {
|
ExtensionOnOpenedType::Command { action } => {
|
||||||
log::debug!("open (execute) command [{:?}]", action);
|
log::debug!("open (execute) command [{:?}]", action);
|
||||||
|
|
||||||
let mut cmd = Command::new(action.exec);
|
let mut cmd = Command::new(action.exec);
|
||||||
if let Some(args) = action.args {
|
if let Some(args) = action.args {
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
}
|
}
|
||||||
let output = cmd.output().map_err(|e| e.to_string())?;
|
let output = cmd.output().await.map_err(|e| e.to_string())?;
|
||||||
// Sometimes, we wanna see the result in logs even though it doesn't fail.
|
// Sometimes, we wanna see the result in logs even though it doesn't fail.
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"executing open(Command) result, exit code: [{}], stdout: [{}], stderr: [{}]",
|
"executing open(Command) result, exit code: [{}], stdout: [{}], stderr: [{}]",
|
||||||
output.status,
|
|
||||||
String::from_utf8_lossy(&output.stdout),
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
if !output.status.success() {
|
|
||||||
log::warn!(
|
|
||||||
"executing open(Command) failed, exit code: [{}], stdout: [{}], stderr: [{}]",
|
|
||||||
output.status,
|
output.status,
|
||||||
String::from_utf8_lossy(&output.stdout),
|
String::from_utf8_lossy(&output.stdout),
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
);
|
);
|
||||||
|
if !output.status.success() {
|
||||||
return Err(format!(
|
log::warn!(
|
||||||
"Command failed, stderr [{}]",
|
"executing open(Command) failed, exit code: [{}], stdout: [{}], stderr: [{}]",
|
||||||
String::from_utf8_lossy(&output.stderr)
|
output.status,
|
||||||
));
|
String::from_utf8_lossy(&output.stdout),
|
||||||
}
|
|
||||||
}
|
|
||||||
ExtensionOnOpenedType::Quicklink {
|
|
||||||
link,
|
|
||||||
open_with: opt_open_with,
|
|
||||||
} => {
|
|
||||||
let url = link.concatenate_url(&extra_args);
|
|
||||||
|
|
||||||
log::debug!("open quicklink [{}] with [{:?}]", url, opt_open_with);
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
// The `open_with` functionality is only supported on macOS, provided
|
|
||||||
// by the `open -a` command.
|
|
||||||
if #[cfg(target_os = "macos")] {
|
|
||||||
let mut cmd = Command::new("open");
|
|
||||||
if let Some(ref open_with) = opt_open_with {
|
|
||||||
cmd.arg("-a");
|
|
||||||
cmd.arg(open_with.as_str());
|
|
||||||
}
|
|
||||||
cmd.arg(&url);
|
|
||||||
|
|
||||||
let output = cmd.output().map_err(|e| format!("failed to spawn [open] due to error [{}]", e))?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(format!(
|
|
||||||
"failed to open with app {:?}: {}",
|
|
||||||
opt_open_with,
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
));
|
);
|
||||||
}
|
|
||||||
} else {
|
return Err(format!(
|
||||||
homemade_tauri_shell_open(tauri_app_handle.clone(), url).await?
|
"Command failed, stderr [{}]",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ExtensionOnOpenedType::Quicklink {
|
||||||
ExtensionOnOpenedType::View {
|
link,
|
||||||
name,
|
open_with: opt_open_with,
|
||||||
icon,
|
} => {
|
||||||
page,
|
let url = link.concatenate_url(&extra_args);
|
||||||
ui,
|
|
||||||
} => {
|
|
||||||
let page_path = Utf8Path::new(&page);
|
|
||||||
let directory = page_path.parent().unwrap_or_else(|| {
|
|
||||||
panic!("View extension page path should have a parent, i.e., it should be under a directory, but [{}] does not", page);
|
|
||||||
});
|
|
||||||
let mut url = serve_files_in(directory.as_ref()).await;
|
|
||||||
|
|
||||||
/*
|
log::debug!("open quicklink [{}] with [{:?}]", url, opt_open_with);
|
||||||
* Emit an event to let the frontend code open this extension.
|
|
||||||
*
|
|
||||||
* Payload `view_extension_opened` contains the information needed
|
|
||||||
* to do that.
|
|
||||||
*
|
|
||||||
* See "src/pages/main/index.tsx" for more info.
|
|
||||||
*/
|
|
||||||
use camino::Utf8Path;
|
|
||||||
use serde_json::Value as Json;
|
|
||||||
use serde_json::to_value;
|
|
||||||
|
|
||||||
let html_filename = page_path
|
cfg_if::cfg_if! {
|
||||||
.file_name()
|
// The `open_with` functionality is only supported on macOS, provided
|
||||||
.unwrap_or_else(|| {
|
// by the `open -a` command.
|
||||||
panic!("View extension page path should have a file name, but [{}] does not have one", page);
|
if #[cfg(target_os = "macos")] {
|
||||||
}).to_string();
|
let mut cmd = Command::new("open");
|
||||||
url.push('/');
|
if let Some(ref open_with) = opt_open_with {
|
||||||
url.push_str(&html_filename);
|
cmd.arg("-a");
|
||||||
|
cmd.arg(open_with.as_str());
|
||||||
|
}
|
||||||
|
cmd.arg(&url);
|
||||||
|
|
||||||
let html_file_url = url;
|
let output = cmd.output().await.map_err(|e| format!("failed to spawn [open] due to error [{}]", e))?;
|
||||||
debug!("View extension listening on: {}", html_file_url);
|
|
||||||
let view_extension_opened: [Json; 5] = [
|
if !output.status.success() {
|
||||||
Json::String(name),
|
return Err(format!(
|
||||||
Json::String(icon),
|
"failed to open with app {:?}: {}",
|
||||||
Json::String(html_file_url),
|
opt_open_with,
|
||||||
to_value(permission).unwrap(),
|
String::from_utf8_lossy(&output.stderr)
|
||||||
to_value(ui).unwrap(),
|
));
|
||||||
];
|
}
|
||||||
tauri_app_handle
|
} else {
|
||||||
.emit("open_view_extension", view_extension_opened)
|
homemade_tauri_shell_open(tauri_app_handle.clone(), url).await?
|
||||||
.unwrap();
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExtensionOnOpenedType::View {
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
page,
|
||||||
|
ui,
|
||||||
|
} => {
|
||||||
|
let page_path = Utf8Path::new(&page);
|
||||||
|
let directory = page_path.parent().unwrap_or_else(|| {
|
||||||
|
panic!("View extension page path should have a parent, i.e., it should be under a directory, but [{}] does not", page);
|
||||||
|
});
|
||||||
|
let mut url = serve_files_in(directory.as_ref()).await;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit an event to let the frontend code open this extension.
|
||||||
|
*
|
||||||
|
* Payload `view_extension_opened` contains the information needed
|
||||||
|
* to do that.
|
||||||
|
*
|
||||||
|
* See "src/pages/main/index.tsx" for more info.
|
||||||
|
*/
|
||||||
|
use camino::Utf8Path;
|
||||||
|
use serde_json::Value as Json;
|
||||||
|
use serde_json::to_value;
|
||||||
|
|
||||||
|
let html_filename = page_path
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!("View extension page path should have a file name, but [{}] does not have one", page);
|
||||||
|
}).to_string();
|
||||||
|
url.push('/');
|
||||||
|
url.push_str(&html_filename);
|
||||||
|
|
||||||
|
let html_file_url = url;
|
||||||
|
debug!("View extension listening on: {}", html_file_url);
|
||||||
|
let view_extension_opened: [Json; 5] = [
|
||||||
|
Json::String(name),
|
||||||
|
Json::String(icon),
|
||||||
|
Json::String(html_file_url),
|
||||||
|
to_value(permission).unwrap(),
|
||||||
|
to_value(ui).unwrap(),
|
||||||
|
];
|
||||||
|
tauri_app_handle
|
||||||
|
.emit("open_view_extension", view_extension_opened)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
match timeout(Duration::from_millis(500), async_closure).await {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_timed_out) => {
|
||||||
|
log::warn!("executing open(on_opened: [{:?}]) timed out", on_opened);
|
||||||
|
|
||||||
|
Err(format!(
|
||||||
|
"executing open(on_opened: {:?}) timed out",
|
||||||
|
on_opened
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
|||||||
Reference in New Issue
Block a user