Files
friendolls-desktop/src-tauri/src/services/ws.rs

182 lines
5.7 KiB
Rust

use rust_socketio::{ClientBuilder, Payload, RawClient};
use serde_json::json;
use tauri::{async_runtime, Emitter};
use tracing::{error, info};
use crate::{
get_app_handle, lock_r, lock_w, models::app_config::AppConfig,
services::cursor::CursorPosition, state::FDOLL,
};
#[allow(non_camel_case_types)] // pretend to be a const like in js
pub struct WS_EVENT;
impl WS_EVENT {
pub const CURSOR_REPORT_POSITION: &str = "cursor-report-position";
pub const FRIEND_REQUEST_RECEIVED: &str = "friend-request-received";
pub const FRIEND_REQUEST_ACCEPTED: &str = "friend-request-accepted";
pub const FRIEND_REQUEST_DENIED: &str = "friend-request-denied";
pub const UNFRIENDED: &str = "unfriended";
pub const FRIEND_CURSOR_POSITION: &str = "friend-cursor-position";
pub const FRIEND_DISCONNECTED: &str = "friend-disconnected";
}
fn on_friend_request_received(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(str) => {
println!("Received friend request: {:?}", str);
get_app_handle()
.emit(WS_EVENT::FRIEND_REQUEST_RECEIVED, str)
.unwrap();
}
_ => error!("Received unexpected payload format for friend request received"),
}
}
fn on_friend_request_accepted(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(str) => {
println!("Received friend request accepted: {:?}", str);
get_app_handle()
.emit(WS_EVENT::FRIEND_REQUEST_ACCEPTED, str)
.unwrap();
}
_ => error!("Received unexpected payload format for friend request accepted"),
}
}
fn on_friend_request_denied(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(str) => {
println!("Received friend request denied: {:?}", str);
get_app_handle()
.emit(WS_EVENT::FRIEND_REQUEST_DENIED, str)
.unwrap();
}
_ => error!("Received unexpected payload format for friend request denied"),
}
}
fn on_unfriended(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(str) => {
println!("Received unfriended: {:?}", str);
get_app_handle().emit(WS_EVENT::UNFRIENDED, str).unwrap();
}
_ => error!("Received unexpected payload format for unfriended"),
}
}
fn on_friend_cursor_position(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(str) => {
get_app_handle()
.emit(WS_EVENT::FRIEND_CURSOR_POSITION, str)
.unwrap();
}
_ => error!("Received unexpected payload format for friend cursor position"),
}
}
fn on_friend_disconnected(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(str) => {
println!("Received friend disconnected: {:?}", str);
get_app_handle()
.emit(WS_EVENT::FRIEND_DISCONNECTED, str)
.unwrap();
}
_ => error!("Received unexpected payload format for friend disconnected"),
}
}
pub async fn report_cursor_data(cursor_position: CursorPosition) {
let client = {
let guard = lock_r!(FDOLL);
guard
.clients
.as_ref()
.expect("Clients are initialized")
.ws_client
.as_ref()
.expect("WebSocket client is initialized")
.clone()
};
match async_runtime::spawn_blocking(move || {
client.emit(
WS_EVENT::CURSOR_REPORT_POSITION,
Payload::Text(vec![json!(cursor_position)]),
)
})
.await
{
Ok(Ok(_)) => (),
Ok(Err(e)) => error!("Failed to emit ping: {}", e),
Err(e) => error!("Failed to execute blocking task: {}", e),
}
}
pub async fn init_ws_client() {
let app_config = {
let guard = lock_r!(FDOLL);
guard.app_config.clone()
};
let ws_client = build_ws_client(&app_config).await;
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.clients.as_mut() {
clients.ws_client = Some(ws_client);
}
info!("WebSocket client initialized after authentication");
}
pub async fn build_ws_client(app_config: &AppConfig) -> rust_socketio::client::Client {
let token = crate::services::auth::get_access_token()
.await
.expect("No access token available for WebSocket connection");
info!("Building WebSocket client with authentication");
let api_base_url = app_config
.api_base_url
.clone()
.expect("Missing API base URL");
let client = async_runtime::spawn_blocking(move || {
ClientBuilder::new(api_base_url)
.namespace("/")
.on(
WS_EVENT::FRIEND_REQUEST_RECEIVED,
on_friend_request_received,
)
.on(
WS_EVENT::FRIEND_REQUEST_ACCEPTED,
on_friend_request_accepted,
)
.on(WS_EVENT::FRIEND_REQUEST_DENIED, on_friend_request_denied)
.on(WS_EVENT::UNFRIENDED, on_unfriended)
.on(WS_EVENT::FRIEND_CURSOR_POSITION, on_friend_cursor_position)
.on(WS_EVENT::FRIEND_DISCONNECTED, on_friend_disconnected)
.auth(json!({ "token": token }))
.connect()
})
.await
.expect("Failed to spawn blocking task");
match client {
Ok(c) => {
info!("WebSocket client connected successfully");
c
}
Err(e) => {
error!("Failed to connect WebSocket: {:?}", e);
panic!(
"TODO: better error handling for WebSocket connection - {}",
e
);
}
}
}