minor refactoring of app startup sequence & some extra trivial matters
This commit is contained in:
@@ -756,8 +756,10 @@ mod windows_impl {
|
||||
|
||||
pub static ACTIVE_APP_CHANGED: &str = "active-app-changed";
|
||||
|
||||
/// Initializes the active app change listener and emits events to the Tauri app on changes.
|
||||
pub fn init_active_app_changes_listener() {
|
||||
/// Initializes the foreground app change listener
|
||||
/// and emits events to the Tauri app on changes.
|
||||
/// Used for app to emit user foreground app to peers.
|
||||
pub fn init_foreground_app_change_listener() {
|
||||
let app_handle = get_app_handle();
|
||||
listen_for_active_app_changes(|app_names: AppMetadata| {
|
||||
if let Err(e) = app_handle.emit(ACTIVE_APP_CHANGED, app_names) {
|
||||
|
||||
@@ -114,7 +114,7 @@ fn generate_code_challenge(code_verifier: &str) -> String {
|
||||
/// Returns the auth pass object, including
|
||||
/// access token, refresh token, expire time etc.
|
||||
/// Automatically refreshes if expired.
|
||||
pub async fn get_tokens() -> Option<AuthPass> {
|
||||
pub async fn get_session_token() -> Option<AuthPass> {
|
||||
info!("Retrieving tokens");
|
||||
let Some(auth_pass) = ({ lock_r!(FDOLL).auth.auth_pass.clone() }) else {
|
||||
return None;
|
||||
@@ -173,7 +173,7 @@ pub async fn get_tokens() -> Option<AuthPass> {
|
||||
|
||||
/// Helper function to get the current access token.
|
||||
pub async fn get_access_token() -> Option<String> {
|
||||
get_tokens().await.map(|pass| pass.access_token)
|
||||
get_session_token().await.map(|pass| pass.access_token)
|
||||
}
|
||||
|
||||
/// Save auth_pass to secure storage (keyring) and update app state.
|
||||
@@ -367,7 +367,7 @@ pub fn clear_auth_pass() -> Result<(), OAuthError> {
|
||||
/// ```
|
||||
pub fn logout() -> Result<(), OAuthError> {
|
||||
info!("Logging out user");
|
||||
lock_w!(FDOLL).auth.auth_pass = None;
|
||||
lock_w!(FDOLL).auth.auth_pass = None;
|
||||
clear_auth_pass()?;
|
||||
|
||||
// Clear OAuth flow state as well
|
||||
@@ -386,8 +386,16 @@ pub async fn logout_and_restart() -> Result<(), OAuthError> {
|
||||
let (refresh_token, session_state, base_url) = {
|
||||
let guard = lock_r!(FDOLL);
|
||||
(
|
||||
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
|
||||
.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
|
||||
@@ -676,7 +684,7 @@ where
|
||||
{
|
||||
let mut guard = lock_w!(FDOLL);
|
||||
guard.auth.auth_pass = Some(auth_pass.clone());
|
||||
guard.auth.oauth_flow = Default::default();
|
||||
guard.auth.oauth_flow = Default::default();
|
||||
}
|
||||
if let Err(e) = save_auth_pass(&auth_pass) {
|
||||
error!("Failed to save auth pass: {}", e);
|
||||
@@ -720,7 +728,8 @@ pub async fn refresh_token(refresh_token: &str) -> Result<AuthPass, OAuthError>
|
||||
(
|
||||
guard.app_config.clone(),
|
||||
guard
|
||||
.network.clients
|
||||
.network
|
||||
.clients
|
||||
.as_ref()
|
||||
.expect("clients present")
|
||||
.http_client
|
||||
|
||||
@@ -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.ui.app_data.scene.display.screen_width as f64;
|
||||
let screen_h = guard.ui.app_data.scene.display.screen_height as f64;
|
||||
let screen_w = guard.user_data.scene.display.screen_width as f64;
|
||||
let screen_h = guard.user_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.ui.app_data.scene.display.screen_width as f64;
|
||||
let screen_h = guard.ui.app_data.scene.display.screen_height as f64;
|
||||
let screen_w = guard.user_data.scene.display.screen_width as f64;
|
||||
let screen_h = guard.user_data.scene.display.screen_height as f64;
|
||||
|
||||
CursorPosition {
|
||||
x: (normalized.x * screen_w).round(),
|
||||
@@ -61,10 +61,9 @@ pub fn normalized_to_absolute(normalized: &CursorPosition) -> CursorPosition {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize cursor tracking - can be called multiple times safely from any window
|
||||
/// Only the first call will actually start tracking, subsequent calls are no-ops
|
||||
#[tauri::command]
|
||||
pub async fn start_cursor_tracking() -> Result<(), String> {
|
||||
/// Initialize cursor tracking. Broadcasts cursor
|
||||
/// position changes via `cursor-position` event.
|
||||
pub async fn init_cursor_tracking() {
|
||||
info!("start_cursor_tracking called");
|
||||
|
||||
// Use OnceCell to ensure this only runs once, even if called from multiple windows
|
||||
@@ -74,17 +73,16 @@ pub async fn start_cursor_tracking() -> Result<(), String> {
|
||||
|
||||
info!("First call to start_cursor_tracking - spawning cursor tracking task");
|
||||
tauri::async_runtime::spawn(async {
|
||||
if let Err(e) = init_cursor_tracking().await {
|
||||
if let Err(e) = init_cursor_tracking_i().await {
|
||||
error!("Failed to initialize cursor tracking: {}", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
info!("Cursor tracking initialization registered");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init_cursor_tracking() -> Result<(), String> {
|
||||
async fn init_cursor_tracking_i() -> Result<(), String> {
|
||||
info!("Initializing cursor tracking...");
|
||||
|
||||
// Create a channel to decouple event generation (producer) from processing (consumer).
|
||||
@@ -124,7 +122,7 @@ async fn init_cursor_tracking() -> Result<(), String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let scale_factor = {
|
||||
let guard = lock_r!(FDOLL);
|
||||
guard.ui.app_data.scene.display.monitor_scale_factor
|
||||
guard.user_data.scene.display.monitor_scale_factor
|
||||
};
|
||||
|
||||
// The producer closure moves `tx` into it.
|
||||
|
||||
@@ -1,36 +1,15 @@
|
||||
use crate::get_app_handle;
|
||||
use crate::{lock_r, state::FDOLL, system_tray::update_system_tray};
|
||||
use tauri::{Emitter, Manager};
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_dialog::{DialogExt, MessageDialogBuilder, MessageDialogKind};
|
||||
use tauri_plugin_positioner::WindowExt;
|
||||
use tracing::{error, info};
|
||||
|
||||
pub static HEALTH_MANAGER_WINDOW_LABEL: &str = "health_manager";
|
||||
pub static HEALTH_MANAGER_EVENT: &str = "health-error";
|
||||
|
||||
fn close_window_if_exists(label: &str) {
|
||||
let app_handle = get_app_handle();
|
||||
if let Some(window) = app_handle.get_window(label) {
|
||||
info!("Closing window with label: {}", label);
|
||||
if let Err(e) = window.close() {
|
||||
error!("Failed to close {} window: {}", label, e);
|
||||
} else {
|
||||
info!("Closed window with label: {}", label);
|
||||
}
|
||||
} else {
|
||||
info!("No window found with label: {}", label);
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes primary UI windows and shows the health manager with an optional error message.
|
||||
pub fn show_health_manager_with_error(error_message: Option<String>) {
|
||||
pub fn open_health_manager_window(error_message: Option<String>) {
|
||||
let app_handle = get_app_handle();
|
||||
// Ensure other windows are closed before showing health manager
|
||||
close_window_if_exists(crate::services::scene::SPLASH_WINDOW_LABEL);
|
||||
close_window_if_exists(crate::services::scene::SCENE_WINDOW_LABEL);
|
||||
close_window_if_exists(crate::services::app_menu::APP_MENU_WINDOW_LABEL);
|
||||
|
||||
update_system_tray(false);
|
||||
|
||||
let existing_webview_window = app_handle.get_window(HEALTH_MANAGER_WINDOW_LABEL);
|
||||
|
||||
@@ -45,12 +24,6 @@ pub fn show_health_manager_with_error(error_message: Option<String>) {
|
||||
.kind(MessageDialogKind::Error)
|
||||
.show(|_| {});
|
||||
}
|
||||
|
||||
if let Some(message) = error_message {
|
||||
if let Err(e) = window.emit(HEALTH_MANAGER_EVENT, message.clone()) {
|
||||
error!("Failed to emit health error event: {}", e);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +31,13 @@ pub fn show_health_manager_with_error(error_message: Option<String>) {
|
||||
let webview_window = match tauri::WebviewWindowBuilder::new(
|
||||
app_handle,
|
||||
HEALTH_MANAGER_WINDOW_LABEL,
|
||||
tauri::WebviewUrl::App("/health-manager".into()),
|
||||
tauri::WebviewUrl::App(
|
||||
format!(
|
||||
"/health-manager?err={}",
|
||||
error_message.unwrap_or(String::from("Something went wrong!"))
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
)
|
||||
.title("Health Manager")
|
||||
.inner_size(420.0, 420.0)
|
||||
@@ -89,12 +68,6 @@ pub fn show_health_manager_with_error(error_message: Option<String>) {
|
||||
error!("Failed to move health manager window to center: {}", e);
|
||||
}
|
||||
|
||||
if let Some(message) = error_message {
|
||||
if let Err(e) = webview_window.emit(HEALTH_MANAGER_EVENT, message.clone()) {
|
||||
error!("Failed to emit health error event: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = webview_window.show() {
|
||||
error!("Failed to show health manager window: {}", e);
|
||||
MessageDialogBuilder::new(
|
||||
@@ -117,7 +90,7 @@ pub fn close_health_manager_window() {
|
||||
} else {
|
||||
info!("Health manager window closed");
|
||||
let guard = lock_r!(FDOLL);
|
||||
let is_logged_in = guard.ui.app_data.user.is_some();
|
||||
let is_logged_in = guard.user_data.user.is_some();
|
||||
drop(guard);
|
||||
update_system_tray(is_logged_in);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
use tauri::Manager;
|
||||
|
||||
use crate::get_app_handle;
|
||||
|
||||
pub mod active_app;
|
||||
pub mod app_menu;
|
||||
pub mod auth;
|
||||
@@ -10,3 +14,11 @@ pub mod scene;
|
||||
pub mod sprite_recolor;
|
||||
pub mod welcome;
|
||||
pub mod ws;
|
||||
|
||||
pub fn close_all_windows() {
|
||||
let app_handle = get_app_handle();
|
||||
let webview_windows = app_handle.webview_windows();
|
||||
for window in webview_windows {
|
||||
window.1.close().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,32 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use rust_socketio::ClientBuilder;
|
||||
use tauri::async_runtime;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::{
|
||||
lock_r, lock_w,
|
||||
services::client_config_manager::AppConfig,
|
||||
services::{auth::get_access_token, client_config_manager::AppConfig},
|
||||
state::FDOLL,
|
||||
};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub async fn establish_websocket_connection() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn init_ws_client() {
|
||||
let app_config = {
|
||||
let guard = lock_r!(FDOLL);
|
||||
@@ -26,16 +43,19 @@ pub async fn init_ws_client() {
|
||||
}
|
||||
Err(e) => {
|
||||
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.network.clients.as_mut() {
|
||||
clients.ws_client = None;
|
||||
clients.is_ws_initialized = false;
|
||||
}
|
||||
clear_ws_client().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clear_ws_client() {
|
||||
let mut guard = lock_w!(FDOLL);
|
||||
if let Some(clients) = guard.network.clients.as_mut() {
|
||||
clients.ws_client = None;
|
||||
clients.is_ws_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn build_ws_client(
|
||||
app_config: &AppConfig,
|
||||
) -> Result<rust_socketio::client::Client, String> {
|
||||
|
||||
@@ -3,8 +3,7 @@ use tauri::async_runtime;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
lock_r,
|
||||
services::{cursor::CursorPosition, health_manager::show_health_manager_with_error},
|
||||
init::lifecycle::handle_disasterous_failure, lock_r, services::cursor::CursorPosition,
|
||||
state::FDOLL,
|
||||
};
|
||||
|
||||
@@ -41,11 +40,11 @@ pub async fn report_cursor_data(cursor_position: CursorPosition) {
|
||||
Ok(Ok(_)) => (),
|
||||
Ok(Err(e)) => {
|
||||
error!("Failed to emit cursor report: {}", e);
|
||||
show_health_manager_with_error(Some(format!("WebSocket emit failed: {}", e)));
|
||||
handle_disasterous_failure(Some(format!("WebSocket emit failed: {}", e))).await;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to execute blocking task for cursor report: {}", e);
|
||||
show_health_manager_with_error(Some(format!("WebSocket task failed: {}", e)));
|
||||
handle_disasterous_failure(Some(format!("WebSocket task failed: {}", e))).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +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_data
|
||||
.user
|
||||
.as_ref()
|
||||
.and_then(|u| u.active_doll_id.as_ref())
|
||||
@@ -75,8 +74,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_data
|
||||
.user
|
||||
.as_ref()
|
||||
.and_then(|u| u.active_doll_id.as_ref())
|
||||
|
||||
@@ -41,7 +41,7 @@ impl WS_EVENT {
|
||||
pub const CLIENT_SEND_INTERACTION: &str = "client-send-interaction";
|
||||
}
|
||||
|
||||
mod client;
|
||||
pub mod client;
|
||||
mod connection;
|
||||
mod cursor;
|
||||
mod doll;
|
||||
@@ -50,6 +50,5 @@ mod handlers;
|
||||
mod interaction;
|
||||
mod user_status;
|
||||
|
||||
pub use client::init_ws_client;
|
||||
pub use cursor::report_cursor_data;
|
||||
pub use user_status::{report_user_status, UserStatusPayload};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use rust_socketio::Payload;
|
||||
use tauri::async_runtime;
|
||||
use tauri::async_runtime::{self};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::Duration;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{lock_r, services::health_manager::show_health_manager_with_error, state::FDOLL};
|
||||
use crate::{init::lifecycle::handle_disasterous_failure, lock_r, state::FDOLL};
|
||||
|
||||
use super::WS_EVENT;
|
||||
|
||||
@@ -66,20 +66,16 @@ pub async fn report_user_status(status: UserStatusPayload) {
|
||||
Ok(Ok(_)) => (),
|
||||
Ok(Err(e)) => {
|
||||
error!("Failed to emit user status report: {}", e);
|
||||
show_health_manager_with_error(Some(format!(
|
||||
"WebSocket emit failed: {}",
|
||||
e
|
||||
)));
|
||||
handle_disasterous_failure(Some(format!("WebSocket emit failed: {}", e)))
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to execute blocking task for user status report: {}",
|
||||
e
|
||||
);
|
||||
show_health_manager_with_error(Some(format!(
|
||||
"WebSocket task failed: {}",
|
||||
e
|
||||
)));
|
||||
handle_disasterous_failure(Some(format!("WebSocket task failed: {}", e)))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user