refactor: streamline profile import logic and enhance error handling (#5051)
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
- 修复 Windows 深色模式下首次启动客户端标题栏颜色异常
|
- 修复 Windows 深色模式下首次启动客户端标题栏颜色异常
|
||||||
- 修复静默启动不加载完整 WebView 的问题
|
- 修复静默启动不加载完整 WebView 的问题
|
||||||
- 修复 Linux WebKit 网络进程的崩溃
|
- 修复 Linux WebKit 网络进程的崩溃
|
||||||
|
- 修复无法导入订阅
|
||||||
- 修复实际导入成功但显示导入失败的问题
|
- 修复实际导入成功但显示导入失败的问题
|
||||||
- 修复 macOS 连接界面显示异常
|
- 修复 macOS 连接界面显示异常
|
||||||
|
|
||||||
|
|||||||
@@ -97,80 +97,48 @@ pub async fn enhance_profiles() -> CmdResult {
|
|||||||
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
|
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
|
||||||
logging!(info, Type::Cmd, "[导入订阅] 开始导入: {}", url);
|
logging!(info, Type::Cmd, "[导入订阅] 开始导入: {}", url);
|
||||||
|
|
||||||
let import_result = tokio::time::timeout(Duration::from_secs(60), async {
|
// 直接依赖 PrfItem::from_url 自身的超时/重试逻辑,不再使用 tokio::time::timeout 包裹
|
||||||
let item = PrfItem::from_url(&url, None, None, option).await?;
|
let item = match PrfItem::from_url(&url, None, None, option).await {
|
||||||
logging!(info, Type::Cmd, "[导入订阅] 下载完成,开始保存配置");
|
Ok(it) => {
|
||||||
|
logging!(info, Type::Cmd, "[导入订阅] 下载完成,开始保存配置");
|
||||||
let profiles = Config::profiles().await;
|
it
|
||||||
let pre_count = profiles
|
|
||||||
.latest_ref()
|
|
||||||
.items
|
|
||||||
.as_ref()
|
|
||||||
.map_or(0, |items| items.len());
|
|
||||||
|
|
||||||
let result = profiles_append_item_safe(item.clone()).await;
|
|
||||||
result?;
|
|
||||||
|
|
||||||
let post_count = profiles
|
|
||||||
.latest_ref()
|
|
||||||
.items
|
|
||||||
.as_ref()
|
|
||||||
.map_or(0, |items| items.len());
|
|
||||||
if post_count <= pre_count {
|
|
||||||
logging!(error, Type::Cmd, "[导入订阅] 配置未增加,导入可能失败");
|
|
||||||
return Err(anyhow::anyhow!("配置导入后数量未增加"));
|
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
logging!(
|
logging!(error, Type::Cmd, "[导入订阅] 下载失败: {}", e);
|
||||||
info,
|
return Err(format!("导入订阅失败: {}", e));
|
||||||
Type::Cmd,
|
|
||||||
"[导入订阅] 配置保存成功,数量: {} -> {}",
|
|
||||||
pre_count,
|
|
||||||
post_count
|
|
||||||
);
|
|
||||||
|
|
||||||
// 立即发送配置变更通知
|
|
||||||
if let Some(uid) = &item.uid {
|
|
||||||
logging!(info, Type::Cmd, "[导入订阅] 发送配置变更通知: {}", uid);
|
|
||||||
handle::Handle::notify_profile_changed(uid.clone());
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 异步保存配置文件并发送全局通知
|
match profiles_append_item_safe(item.clone()).await {
|
||||||
let uid_clone = item.uid.clone();
|
Ok(_) => match profiles_save_file_safe().await {
|
||||||
crate::process::AsyncHandler::spawn(move || async move {
|
Ok(_) => {
|
||||||
// 使用Send-safe helper函数
|
|
||||||
if let Err(e) = profiles_save_file_safe().await {
|
|
||||||
logging!(error, Type::Cmd, "[导入订阅] 保存配置文件失败: {}", e);
|
|
||||||
} else {
|
|
||||||
logging!(info, Type::Cmd, "[导入订阅] 配置文件保存成功");
|
logging!(info, Type::Cmd, "[导入订阅] 配置文件保存成功");
|
||||||
|
|
||||||
// 发送全局配置更新通知
|
|
||||||
if let Some(uid) = uid_clone {
|
|
||||||
// 延迟发送,确保文件已完全写入
|
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
|
||||||
handle::Handle::notify_profile_changed(uid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
Err(e) => {
|
||||||
|
logging!(error, Type::Cmd, "[导入订阅] 保存配置文件失败: {}", e);
|
||||||
Ok(())
|
}
|
||||||
})
|
},
|
||||||
.await;
|
Err(e) => {
|
||||||
|
logging!(error, Type::Cmd, "[导入订阅] 保存配置失败: {}", e);
|
||||||
match import_result {
|
return Err(format!("导入订阅失败: {}", e));
|
||||||
Ok(Ok(())) => {
|
|
||||||
logging!(info, Type::Cmd, "[导入订阅] 导入完成: {}", url);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
logging!(error, Type::Cmd, "[导入订阅] 导入失败: {}", e);
|
|
||||||
Err(format!("导入订阅失败: {e}"))
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
logging!(error, Type::Cmd, "[导入订阅] 导入超时(60秒): {}", url);
|
|
||||||
Err("导入订阅超时,请检查网络连接".into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 立即发送配置变更通知
|
||||||
|
if let Some(uid) = &item.uid {
|
||||||
|
logging!(info, Type::Cmd, "[导入订阅] 发送配置变更通知: {}", uid);
|
||||||
|
handle::Handle::notify_profile_changed(uid.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步保存配置文件并发送全局通知
|
||||||
|
let uid_clone = item.uid.clone();
|
||||||
|
if let Some(uid) = uid_clone {
|
||||||
|
// 延迟发送,确保文件已完全写入
|
||||||
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
|
handle::Handle::notify_profile_changed(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
logging!(info, Type::Cmd, "[导入订阅] 导入完成: {}", url);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 调整profile的顺序
|
/// 调整profile的顺序
|
||||||
|
|||||||
@@ -81,6 +81,30 @@ impl<T: Clone + ToOwned> Draft<Box<T>> {
|
|||||||
pub fn discard(&self) -> Option<Box<T>> {
|
pub fn discard(&self) -> Option<Box<T>> {
|
||||||
self.inner.write().1.take()
|
self.inner.write().1.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 异步修改正式数据,闭包直接获得 Box<T> 所有权
|
||||||
|
pub async fn with_data_modify<F, Fut, R, E>(&self, f: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
T: Send + Sync + 'static,
|
||||||
|
F: FnOnce(Box<T>) -> Fut + Send,
|
||||||
|
Fut: std::future::Future<Output = Result<(Box<T>, R), E>> + Send,
|
||||||
|
E: From<anyhow::Error>,
|
||||||
|
{
|
||||||
|
// 克隆正式数据
|
||||||
|
let local = {
|
||||||
|
let guard = self.inner.read();
|
||||||
|
guard.0.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 异步闭包执行,返回修改后的 Box<T> 和业务结果 R
|
||||||
|
let (new_local, res) = f(local).await?;
|
||||||
|
|
||||||
|
// 写回正式数据
|
||||||
|
let mut guard = self.inner.write();
|
||||||
|
guard.0 = new_local;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::{PrfOption, prfitem::PrfItem};
|
use super::{PrfOption, prfitem::PrfItem};
|
||||||
use crate::{
|
use crate::{
|
||||||
logging_error,
|
logging_error,
|
||||||
process::AsyncHandler,
|
|
||||||
utils::{dirs, help, logging::Type},
|
utils::{dirs, help, logging::Type},
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
@@ -164,7 +163,8 @@ impl IProfiles {
|
|||||||
items.push(item)
|
items.push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.save_file().await
|
// self.save_file().await
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reorder items
|
/// reorder items
|
||||||
@@ -730,86 +730,66 @@ pub async fn profiles_append_item_with_filedata_safe(
|
|||||||
item: PrfItem,
|
item: PrfItem,
|
||||||
file_data: Option<String>,
|
file_data: Option<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
let item = PrfItem::from(item, file_data).await?;
|
||||||
AsyncHandler::handle().block_on(async {
|
profiles_append_item_safe(item).await
|
||||||
let item = PrfItem::from(item, file_data).await?;
|
|
||||||
let profiles = Config::profiles().await;
|
|
||||||
let mut profiles_guard = profiles.data_mut();
|
|
||||||
profiles_guard.append_item(item).await
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profiles_append_item_safe(item: PrfItem) -> Result<()> {
|
pub async fn profiles_append_item_safe(item: PrfItem) -> Result<()> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
Config::profiles()
|
||||||
AsyncHandler::handle().block_on(async {
|
.await
|
||||||
let profiles = Config::profiles().await;
|
.with_data_modify(|mut profiles| async move {
|
||||||
let mut profiles_guard = profiles.data_mut();
|
profiles.append_item(item).await?;
|
||||||
profiles_guard.append_item(item).await
|
Ok((profiles, ()))
|
||||||
})
|
})
|
||||||
})
|
.await
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profiles_patch_item_safe(index: String, item: PrfItem) -> Result<()> {
|
pub async fn profiles_patch_item_safe(index: String, item: PrfItem) -> Result<()> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
Config::profiles()
|
||||||
AsyncHandler::handle().block_on(async {
|
.await
|
||||||
let profiles = Config::profiles().await;
|
.with_data_modify(|mut profiles| async move {
|
||||||
let mut profiles_guard = profiles.data_mut();
|
profiles.patch_item(index, item).await?;
|
||||||
profiles_guard.patch_item(index, item).await
|
Ok((profiles, ()))
|
||||||
})
|
})
|
||||||
})
|
.await
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profiles_delete_item_safe(index: String) -> Result<bool> {
|
pub async fn profiles_delete_item_safe(index: String) -> Result<bool> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
Config::profiles()
|
||||||
AsyncHandler::handle().block_on(async {
|
.await
|
||||||
let profiles = Config::profiles().await;
|
.with_data_modify(|mut profiles| async move {
|
||||||
let mut profiles_guard = profiles.data_mut();
|
let deleted = profiles.delete_item(index).await?;
|
||||||
profiles_guard.delete_item(index).await
|
Ok((profiles, deleted))
|
||||||
})
|
})
|
||||||
})
|
.await
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profiles_reorder_safe(active_id: String, over_id: String) -> Result<()> {
|
pub async fn profiles_reorder_safe(active_id: String, over_id: String) -> Result<()> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
Config::profiles()
|
||||||
AsyncHandler::handle().block_on(async {
|
.await
|
||||||
let profiles = Config::profiles().await;
|
.with_data_modify(|mut profiles| async move {
|
||||||
let mut profiles_guard = profiles.data_mut();
|
profiles.reorder(active_id, over_id).await?;
|
||||||
profiles_guard.reorder(active_id, over_id).await
|
Ok((profiles, ()))
|
||||||
})
|
})
|
||||||
})
|
.await
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profiles_save_file_safe() -> Result<()> {
|
pub async fn profiles_save_file_safe() -> Result<()> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
Config::profiles()
|
||||||
AsyncHandler::handle().block_on(async {
|
.await
|
||||||
let profiles = Config::profiles().await;
|
.with_data_modify(|profiles| async move {
|
||||||
let profiles_guard = profiles.data_mut();
|
profiles.save_file().await?;
|
||||||
profiles_guard.save_file().await
|
Ok((profiles, ()))
|
||||||
})
|
})
|
||||||
})
|
.await
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn profiles_draft_update_item_safe(index: String, item: PrfItem) -> Result<()> {
|
pub async fn profiles_draft_update_item_safe(index: String, item: PrfItem) -> Result<()> {
|
||||||
AsyncHandler::spawn_blocking(move || {
|
Config::profiles()
|
||||||
AsyncHandler::handle().block_on(async {
|
.await
|
||||||
let profiles = Config::profiles().await;
|
.with_data_modify(|mut profiles| async move {
|
||||||
let mut profiles_guard = profiles.draft_mut();
|
profiles.update_item(index, item).await?;
|
||||||
profiles_guard.update_item(index, item).await
|
Ok((profiles, ()))
|
||||||
})
|
})
|
||||||
})
|
.await
|
||||||
.await
|
|
||||||
.map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ use tauri::{async_runtime, async_runtime::JoinHandle};
|
|||||||
pub struct AsyncHandler;
|
pub struct AsyncHandler;
|
||||||
|
|
||||||
impl AsyncHandler {
|
impl AsyncHandler {
|
||||||
pub fn handle() -> async_runtime::RuntimeHandle {
|
// pub fn handle() -> async_runtime::RuntimeHandle {
|
||||||
async_runtime::handle()
|
// async_runtime::handle()
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn<F, Fut>(f: F) -> JoinHandle<()>
|
pub fn spawn<F, Fut>(f: F) -> JoinHandle<()>
|
||||||
|
|||||||
Reference in New Issue
Block a user