state management refactoring

This commit is contained in:
2026-01-21 20:28:31 +08:00
parent 7f37a3f8c2
commit 8173f10937
18 changed files with 255 additions and 186 deletions

View File

@@ -25,10 +25,10 @@ const SERVICE_NAME: &str = "friendolls";
pub fn cancel_auth_flow() {
let mut guard = lock_w!(FDOLL);
if let Some(token) = guard.oauth_flow.cancel_token.take() {
if let Some(token) = guard.auth.oauth_flow.cancel_token.take() {
token.cancel();
}
guard.oauth_flow = Default::default();
guard.auth.oauth_flow = Default::default();
}
/// Errors that can occur during OAuth authentication flow.
@@ -116,13 +116,13 @@ fn generate_code_challenge(code_verifier: &str) -> String {
/// Automatically refreshes if expired.
pub async fn get_tokens() -> Option<AuthPass> {
info!("Retrieving tokens");
let Some(auth_pass) = ({ lock_r!(FDOLL).auth_pass.clone() }) else {
let Some(auth_pass) = ({ lock_r!(FDOLL).auth.auth_pass.clone() }) else {
return None;
};
let Some(issued_at) = auth_pass.issued_at else {
warn!("Auth pass missing issued_at timestamp, clearing");
lock_w!(FDOLL).auth_pass = None;
lock_w!(FDOLL).auth.auth_pass = None;
return None;
};
@@ -137,7 +137,7 @@ pub async fn get_tokens() -> Option<AuthPass> {
if refresh_expired {
info!("Refresh token expired, clearing auth state");
lock_w!(FDOLL).auth_pass = None;
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear expired auth pass: {}", e);
}
@@ -148,7 +148,7 @@ pub async fn get_tokens() -> Option<AuthPass> {
let _guard = REFRESH_LOCK.lock().await;
// Double-check after acquiring lock
let auth_pass = lock_r!(FDOLL).auth_pass.clone()?;
let auth_pass = lock_r!(FDOLL).auth.auth_pass.clone()?;
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).ok()?.as_secs();
let expired = current_time - auth_pass.issued_at? >= auth_pass.expires_in;
@@ -162,7 +162,7 @@ pub async fn get_tokens() -> Option<AuthPass> {
Ok(new_pass) => Some(new_pass),
Err(e) => {
error!("Failed to refresh token: {}", e);
lock_w!(FDOLL).auth_pass = None;
lock_w!(FDOLL).auth.auth_pass = None;
if let Err(e) = clear_auth_pass() {
error!("Failed to clear auth pass after refresh failure: {}", e);
}
@@ -367,11 +367,11 @@ pub fn clear_auth_pass() -> Result<(), OAuthError> {
/// ```
pub fn logout() -> Result<(), OAuthError> {
info!("Logging out user");
lock_w!(FDOLL).auth_pass = None;
lock_w!(FDOLL).auth.auth_pass = None;
clear_auth_pass()?;
// Clear OAuth flow state as well
lock_w!(FDOLL).oauth_flow = Default::default();
lock_w!(FDOLL).auth.oauth_flow = Default::default();
// TODO: Call OAuth provider's revocation endpoint
// This would require adding a revoke_token() function that calls:
@@ -386,8 +386,8 @@ pub async fn logout_and_restart() -> Result<(), OAuthError> {
let (refresh_token, session_state, base_url) = {
let guard = lock_r!(FDOLL);
(
guard.auth_pass.as_ref().map(|p| p.refresh_token.clone()),
guard.auth_pass.as_ref().map(|p| p.session_state.clone()),
guard.auth.auth_pass.as_ref().map(|p| p.refresh_token.clone()),
guard.auth.auth_pass.as_ref().map(|p| p.session_state.clone()),
guard
.app_config
.api_base_url
@@ -460,7 +460,7 @@ pub async fn exchange_code_for_auth_pass(
) -> Result<AuthPass, OAuthError> {
let (app_config, http_client) = {
let guard = lock_r!(FDOLL);
let clients = guard.clients.as_ref();
let clients = guard.network.clients.as_ref();
if clients.is_none() {
error!("Clients not initialized yet!");
return Err(OAuthError::InvalidConfig);
@@ -581,10 +581,10 @@ where
{
let mut guard = lock_w!(FDOLL);
guard.oauth_flow.state = Some(state.clone());
guard.oauth_flow.code_verifier = Some(code_verifier.clone());
guard.oauth_flow.initiated_at = Some(current_time);
guard.oauth_flow.cancel_token = Some(cancel_token.clone());
guard.auth.oauth_flow.state = Some(state.clone());
guard.auth.oauth_flow.code_verifier = Some(code_verifier.clone());
guard.auth.oauth_flow.initiated_at = Some(current_time);
guard.auth.oauth_flow.cancel_token = Some(cancel_token.clone());
}
let mut url = match url::Url::parse(&format!("{}/auth", &app_config.auth.auth_url)) {
@@ -655,8 +655,8 @@ where
let (stored_state, stored_verifier) = {
let guard = lock_r!(FDOLL);
(
guard.oauth_flow.state.clone(),
guard.oauth_flow.code_verifier.clone(),
guard.auth.oauth_flow.state.clone(),
guard.auth.oauth_flow.code_verifier.clone(),
)
};
@@ -675,8 +675,8 @@ where
Ok(auth_pass) => {
{
let mut guard = lock_w!(FDOLL);
guard.auth_pass = Some(auth_pass.clone());
guard.oauth_flow = Default::default();
guard.auth.auth_pass = Some(auth_pass.clone());
guard.auth.oauth_flow = Default::default();
}
if let Err(e) = save_auth_pass(&auth_pass) {
error!("Failed to save auth pass: {}", e);
@@ -720,7 +720,7 @@ pub async fn refresh_token(refresh_token: &str) -> Result<AuthPass, OAuthError>
(
guard.app_config.clone(),
guard
.clients
.network.clients
.as_ref()
.expect("clients present")
.http_client
@@ -765,7 +765,7 @@ pub async fn refresh_token(refresh_token: &str) -> Result<AuthPass, OAuthError>
);
// Update state and storage
lock_w!(FDOLL).auth_pass = Some(auth_pass.clone());
lock_w!(FDOLL).auth.auth_pass = Some(auth_pass.clone());
if let Err(e) = save_auth_pass(&auth_pass) {
error!("Failed to save refreshed auth pass: {}", e);
} else {

View File

@@ -40,8 +40,8 @@ pub fn get_latest_cursor_position() -> Option<CursorPosition> {
/// Convert absolute screen coordinates to normalized coordinates (0.0 - 1.0)
pub fn absolute_to_normalized(pos: &CursorPosition) -> CursorPosition {
let guard = lock_r!(FDOLL);
let screen_w = guard.app_data.scene.display.screen_width as f64;
let screen_h = guard.app_data.scene.display.screen_height as f64;
let screen_w = guard.ui.app_data.scene.display.screen_width as f64;
let screen_h = guard.ui.app_data.scene.display.screen_height as f64;
CursorPosition {
x: (pos.x / screen_w).clamp(0.0, 1.0),
@@ -52,8 +52,8 @@ pub fn absolute_to_normalized(pos: &CursorPosition) -> CursorPosition {
/// Convert normalized coordinates to absolute screen coordinates
pub fn normalized_to_absolute(normalized: &CursorPosition) -> CursorPosition {
let guard = lock_r!(FDOLL);
let screen_w = guard.app_data.scene.display.screen_width as f64;
let screen_h = guard.app_data.scene.display.screen_height as f64;
let screen_w = guard.ui.app_data.scene.display.screen_width as f64;
let screen_h = guard.ui.app_data.scene.display.screen_height as f64;
CursorPosition {
x: (normalized.x * screen_w).round(),
@@ -124,7 +124,7 @@ async fn init_cursor_tracking() -> Result<(), String> {
#[cfg(target_os = "windows")]
let scale_factor = {
let guard = lock_r!(FDOLL);
guard.app_data.scene.display.monitor_scale_factor
guard.ui.app_data.scene.display.monitor_scale_factor
};
// The producer closure moves `tx` into it.

View File

@@ -117,7 +117,7 @@ pub fn close_health_manager_window() {
} else {
info!("Health manager window closed");
let guard = lock_r!(FDOLL);
let is_logged_in = guard.app_data.user.is_some();
let is_logged_in = guard.ui.app_data.user.is_some();
drop(guard);
update_system_tray(is_logged_in);
}

View File

@@ -9,7 +9,7 @@ pub async fn send_interaction(dto: SendInteractionDto) -> Result<(), String> {
// Check if WS is initialized
let client = {
let guard = lock_r!(FDOLL);
if let Some(clients) = &guard.clients {
if let Some(clients) = &guard.network.clients {
if clients.is_ws_initialized {
clients.ws_client.clone()
} else {

View File

@@ -33,7 +33,7 @@ fn on_initialized(payload: Payload, _socket: RawClient) {
// Mark WebSocket as initialized and reset backoff timer
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.clients.as_mut() {
if let Some(clients) = guard.network.clients.as_mut() {
clients.is_ws_initialized = true;
}
@@ -57,7 +57,7 @@ pub async fn init_ws_client() {
match build_ws_client(&app_config).await {
Ok(ws_client) => {
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.clients.as_mut() {
if let Some(clients) = guard.network.clients.as_mut() {
clients.ws_client = Some(ws_client);
clients.is_ws_initialized = false; // wait for initialized event
}
@@ -66,7 +66,7 @@ pub async fn init_ws_client() {
error!("Failed to initialize WebSocket client: {}", e);
// If we failed because no token, clear the WS client to avoid stale retries
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.clients.as_mut() {
if let Some(clients) = guard.network.clients.as_mut() {
clients.ws_client = None;
clients.is_ws_initialized = false;
}

View File

@@ -15,7 +15,7 @@ pub async fn report_cursor_data(cursor_position: CursorPosition) {
// and if clients are actually initialized.
let (client_opt, is_initialized) = {
let guard = lock_r!(FDOLL);
if let Some(clients) = &guard.clients {
if let Some(clients) = &guard.network.clients {
(
clients.ws_client.as_ref().cloned(),
clients.is_ws_initialized,

View File

@@ -36,6 +36,7 @@ pub fn on_doll_updated(payload: Payload, _socket: RawClient) {
let is_active_doll = if let Some(id) = doll_id {
let guard = lock_r!(FDOLL);
guard
.ui
.app_data
.user
.as_ref()
@@ -74,6 +75,7 @@ pub fn on_doll_deleted(payload: Payload, _socket: RawClient) {
let is_active_doll = if let Some(id) = doll_id {
let guard = lock_r!(FDOLL);
guard
.ui
.app_data
.user
.as_ref()