From acf4b94a1f7f8a4eabbd2882bb2266ee31ecfc3b Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Sun, 25 Jan 2026 16:06:12 +0800 Subject: [PATCH] `commands` refactor --- src-tauri/src/commands/dolls.rs | 75 +++++++------------------------ src-tauri/src/commands/friends.rs | 32 ++++++++++--- src-tauri/src/commands/mod.rs | 58 ++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 65 deletions(-) diff --git a/src-tauri/src/commands/dolls.rs b/src-tauri/src/commands/dolls.rs index 1b9f291..e204cf3 100644 --- a/src-tauri/src/commands/dolls.rs +++ b/src-tauri/src/commands/dolls.rs @@ -1,13 +1,12 @@ use crate::{ - lock_r, models::dolls::{CreateDollDto, DollDto, UpdateDollDto}, remotes::{ dolls::DollsRemote, user::UserRemote, }, - state::{init_app_data_scoped, AppDataRefreshScope, FDOLL}, + state::AppDataRefreshScope, + commands::{refresh_app_data, refresh_app_data_conditionally, is_active_doll}, }; -use tauri::async_runtime; #[tauri::command] pub async fn get_dolls() -> Result, String> { @@ -32,10 +31,7 @@ pub async fn create_doll(dto: CreateDollDto) -> Result { .await .map_err(|e| e.to_string())?; - // Refresh dolls list in background (deduped inside init_app_data_scoped) - async_runtime::spawn(async { - init_app_data_scoped(AppDataRefreshScope::Dolls).await; - }); + refresh_app_data(&[AppDataRefreshScope::Dolls]).await; Ok(result) } @@ -48,60 +44,32 @@ pub async fn update_doll(id: String, dto: UpdateDollDto) -> Result Result<(), String> { - DollsRemote::new() + let result = DollsRemote::new() .delete_doll(&id) .await .map_err(|e| e.to_string())?; // Check if this was the active doll (after delete completes to avoid stale reads) - let is_active_doll = { - let guard = lock_r!(FDOLL); - guard - .ui - .app_data - .user - .as_ref() - .and_then(|u| u.active_doll_id.as_ref()) - .map(|active_id| active_id == &id) - .unwrap_or(false) - }; + let is_active = is_active_doll(&id); - // Refresh dolls list + User/Friends if the deleted doll was active - async_runtime::spawn(async move { - init_app_data_scoped(AppDataRefreshScope::Dolls).await; - if is_active_doll { - init_app_data_scoped(AppDataRefreshScope::User).await; - init_app_data_scoped(AppDataRefreshScope::Friends).await; - } - }); + refresh_app_data_conditionally( + &[AppDataRefreshScope::Dolls], + is_active.then_some(&[AppDataRefreshScope::User, AppDataRefreshScope::Friends]), + ).await; - Ok(()) + Ok(result) } #[tauri::command] @@ -111,12 +79,7 @@ pub async fn set_active_doll(doll_id: String) -> Result<(), String> { .await .map_err(|e| e.to_string())?; - // Refresh User (for active_doll_id) + Friends (so friends see your active doll) - // We don't need to refresh Dolls since the doll itself hasn't changed - async_runtime::spawn(async { - init_app_data_scoped(AppDataRefreshScope::User).await; - init_app_data_scoped(AppDataRefreshScope::Friends).await; - }); + refresh_app_data(&[AppDataRefreshScope::User, AppDataRefreshScope::Friends]).await; Ok(()) } @@ -128,11 +91,7 @@ pub async fn remove_active_doll() -> Result<(), String> { .await .map_err(|e| e.to_string())?; - // Refresh User (for active_doll_id) + Friends (so friends see your doll is gone) - async_runtime::spawn(async { - init_app_data_scoped(AppDataRefreshScope::User).await; - init_app_data_scoped(AppDataRefreshScope::Friends).await; - }); + refresh_app_data(&[AppDataRefreshScope::User, AppDataRefreshScope::Friends]).await; Ok(()) } diff --git a/src-tauri/src/commands/friends.rs b/src-tauri/src/commands/friends.rs index 4c78cf7..2ce1764 100644 --- a/src-tauri/src/commands/friends.rs +++ b/src-tauri/src/commands/friends.rs @@ -2,6 +2,8 @@ use crate::remotes::friends::FriendRemote; use crate::models::friends::{ FriendRequestResponseDto, FriendshipResponseDto, SendFriendRequestDto, UserBasicDto, }; +use crate::state::AppDataRefreshScope; +use crate::commands::refresh_app_data; #[tauri::command] pub async fn list_friends() -> Result, String> { @@ -31,10 +33,14 @@ pub async fn search_users(username: Option) -> Result, pub async fn send_friend_request( request: SendFriendRequestDto, ) -> Result { - FriendRemote::new() + let result = FriendRemote::new() .send_friend_request(request) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + refresh_app_data(&[AppDataRefreshScope::Friends]).await; + + Ok(result) } #[tauri::command] @@ -55,18 +61,26 @@ pub async fn sent_friend_requests() -> Result, Str #[tauri::command] pub async fn accept_friend_request(request_id: String) -> Result { - FriendRemote::new() + let result = FriendRemote::new() .accept_friend_request(&request_id) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + refresh_app_data(&[AppDataRefreshScope::Friends]).await; + + Ok(result) } #[tauri::command] pub async fn deny_friend_request(request_id: String) -> Result { - FriendRemote::new() + let result = FriendRemote::new() .deny_friend_request(&request_id) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + refresh_app_data(&[AppDataRefreshScope::Friends]).await; + + Ok(result) } #[tauri::command] @@ -74,5 +88,9 @@ pub async fn unfriend(friend_id: String) -> Result<(), String> { FriendRemote::new() .unfriend(&friend_id) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + refresh_app_data(&[AppDataRefreshScope::Friends]).await; + + Ok(()) } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 51e288b..cacb98f 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -6,3 +6,61 @@ pub mod dolls; pub mod friends; pub mod interaction; pub mod sprite; + +use crate::state::{init_app_data_scoped, AppDataRefreshScope, FDOLL}; +use crate::lock_r; +use tauri::async_runtime; + +/// Helper to execute a mutation operation and refresh app data scopes in the background. +/// Returns the result of the operation after mapping errors to strings. +/// +/// # Example +/// ```ignore +/// pub async fn create_doll(dto: CreateDollDto) -> Result { +/// let result = DollsRemote::new().create_doll(dto).await.map_err(|e| e.to_string())?; +/// refresh_app_data(&[AppDataRefreshScope::Dolls]).await; +/// Ok(result) +/// } +/// ``` +pub async fn refresh_app_data(scopes: &[AppDataRefreshScope]) { + let scopes = scopes.to_vec(); + async_runtime::spawn(async move { + for scope in scopes { + init_app_data_scoped(scope).await; + } + }); +} + +/// Helper to execute a mutation operation with conditional refresh. +/// +/// # Example +/// ```ignore +/// pub async fn delete_doll(id: String) -> Result<(), String> { +/// let result = DollsRemote::new().delete_doll(&id).await.map_err(|e| e.to_string())?; +/// let is_active = is_active_doll(&id); +/// refresh_app_data_conditionally(&[AppDataRefreshScope::Dolls], +/// is_active.then_some(&[AppDataRefreshScope::User, AppDataRefreshScope::Friends])); +/// Ok(result) +/// } +/// ``` +pub async fn refresh_app_data_conditionally(base_scopes: &[AppDataRefreshScope], conditional_scopes: Option<&[AppDataRefreshScope]>) { + let mut all_scopes = base_scopes.to_vec(); + if let Some(extra_scopes) = conditional_scopes { + all_scopes.extend_from_slice(extra_scopes); + } + refresh_app_data(&all_scopes).await; +} + +/// Helper to check if a doll is currently the active doll. +/// Used in doll mutation operations to determine if additional refreshes are needed. +pub fn is_active_doll(doll_id: &str) -> bool { + let guard = lock_r!(FDOLL); + guard + .ui + .app_data + .user + .as_ref() + .and_then(|u| u.active_doll_id.as_ref()) + .map(|active_id| active_id == doll_id) + .unwrap_or(false) +}