fixed WS race condition issue
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::time::{sleep, Instant};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
services::{
|
||||
auth::get_tokens,
|
||||
auth::{get_access_token, get_tokens},
|
||||
scene::{close_splash_window, open_scene_window, open_splash_window},
|
||||
ws::init_ws_client,
|
||||
},
|
||||
state::init_app_data,
|
||||
system_tray::init_system_tray,
|
||||
@@ -16,30 +18,31 @@ pub async fn start_fdoll() {
|
||||
bootstrap().await;
|
||||
}
|
||||
|
||||
async fn init_ws_after_auth() {
|
||||
const MAX_ATTEMPTS: u8 = 5;
|
||||
const BACKOFF: Duration = Duration::from_millis(300);
|
||||
|
||||
for attempt in 1..=MAX_ATTEMPTS {
|
||||
if get_access_token().await.is_some() {
|
||||
init_ws_client().await;
|
||||
return;
|
||||
}
|
||||
|
||||
sleep(BACKOFF).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn construct_app() {
|
||||
open_splash_window();
|
||||
|
||||
// Record start time for minimum splash duration
|
||||
let start = Instant::now();
|
||||
|
||||
// Spawn initialization tasks in parallel
|
||||
// We want to wait for them to finish, but they run concurrently
|
||||
let init_data = tauri::async_runtime::spawn(async {
|
||||
init_app_data().await;
|
||||
});
|
||||
// Initialize app data first so we only start WebSocket after auth is fully available
|
||||
init_app_data().await;
|
||||
|
||||
let init_ws = tauri::async_runtime::spawn(async {
|
||||
// init_ws_client calls get_access_token().await.
|
||||
// During a fresh login, this token might be in the process of being saved/refreshed
|
||||
// or the client initialization might be racing.
|
||||
// However, construct_app is called after auth success, so tokens should be there.
|
||||
// The issue might be that init_ws_client is idempotent but if called twice or early...
|
||||
// Actually, init_ws_client handles creating the socket.
|
||||
crate::services::ws::init_ws_client().await;
|
||||
});
|
||||
|
||||
// Wait for both to complete
|
||||
let _ = tokio::join!(init_data, init_ws);
|
||||
// Initialize WebSocket client after we know auth is present
|
||||
init_ws_after_auth().await;
|
||||
|
||||
// Ensure splash stays visible for at least 3 seconds
|
||||
let elapsed = start.elapsed();
|
||||
@@ -54,16 +57,15 @@ async fn construct_app() {
|
||||
|
||||
pub async fn bootstrap() {
|
||||
match get_tokens().await {
|
||||
Some(_) => {
|
||||
info!("User session restored");
|
||||
Some(tokens) => {
|
||||
info!("Tokens found in keyring - restoring user session");
|
||||
construct_app().await;
|
||||
}
|
||||
None => {
|
||||
info!("No active session, user needs to authenticate");
|
||||
info!("No active session found - user needs to authenticate");
|
||||
match crate::services::auth::init_auth_code_retrieval(|| {
|
||||
info!("Authentication successful, creating scene...");
|
||||
tauri::async_runtime::spawn(async {
|
||||
info!("Creating scene after auth success...");
|
||||
construct_app().await;
|
||||
});
|
||||
}) {
|
||||
|
||||
@@ -60,11 +60,10 @@ fn on_initialized(payload: Payload, _socket: RawClient) {
|
||||
if let Some(first_value) = values.first() {
|
||||
info!("Received initialized event: {:?}", first_value);
|
||||
|
||||
// Mark WebSocket as initialized
|
||||
// Mark WebSocket as initialized and reset backoff timer
|
||||
let mut guard = lock_w!(FDOLL);
|
||||
if let Some(clients) = guard.clients.as_mut() {
|
||||
clients.is_ws_initialized = true;
|
||||
info!("WebSocket marked as initialized and ready for business");
|
||||
}
|
||||
} else {
|
||||
info!("Received initialized event with empty payload");
|
||||
@@ -239,8 +238,6 @@ fn on_friend_active_doll_changed(payload: Payload, _socket: RawClient) {
|
||||
get_app_handle().emit(WS_EVENT::FRIEND_ACTIVE_DOLL_CHANGED, first_value)
|
||||
{
|
||||
error!("Failed to emit friend-active-doll-changed event: {:?}", e);
|
||||
} else {
|
||||
info!("Emitted friend-active-doll-changed to frontend");
|
||||
}
|
||||
|
||||
// Refresh friends list only (optimized - friend's active doll is part of friends data)
|
||||
@@ -353,20 +350,23 @@ fn on_doll_deleted(payload: Payload, _socket: RawClient) {
|
||||
pub async fn report_cursor_data(cursor_position: CursorPosition) {
|
||||
// Only attempt to get clients if lock_r succeeds (it should, but safety first)
|
||||
// and if clients are actually initialized.
|
||||
let client_opt = {
|
||||
let (client_opt, is_initialized) = {
|
||||
let guard = lock_r!(FDOLL);
|
||||
if let Some(clients) = &guard.clients {
|
||||
if clients.is_ws_initialized {
|
||||
clients.ws_client.as_ref().cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(
|
||||
clients.ws_client.as_ref().cloned(),
|
||||
clients.is_ws_initialized,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
(None, false)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(client) = client_opt {
|
||||
if !is_initialized {
|
||||
return;
|
||||
}
|
||||
|
||||
match async_runtime::spawn_blocking(move || {
|
||||
client.emit(
|
||||
WS_EVENT::CURSOR_REPORT_POSITION,
|
||||
@@ -379,8 +379,6 @@ pub async fn report_cursor_data(cursor_position: CursorPosition) {
|
||||
Ok(Err(e)) => error!("Failed to emit cursor report: {}", e),
|
||||
Err(e) => error!("Failed to execute blocking task for cursor report: {}", e),
|
||||
}
|
||||
} else {
|
||||
// Quietly fail if client is not ready (e.g. during startup/shutdown or before initialization)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,8 +393,8 @@ pub async fn init_ws_client() {
|
||||
let mut guard = lock_w!(FDOLL);
|
||||
if let Some(clients) = guard.clients.as_mut() {
|
||||
clients.ws_client = Some(ws_client);
|
||||
clients.is_ws_initialized = false; // wait for initialized event
|
||||
}
|
||||
info!("WebSocket client initialized after authentication");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to initialize WebSocket client: {}", e);
|
||||
@@ -415,8 +413,6 @@ pub async fn build_ws_client(
|
||||
None => return Err("No access token available for WebSocket connection".to_string()),
|
||||
};
|
||||
|
||||
info!("Building WebSocket client with authentication");
|
||||
|
||||
let api_base_url = app_config
|
||||
.api_base_url
|
||||
.clone()
|
||||
|
||||
Reference in New Issue
Block a user