data between friends (with some crazy fancy UI)
This commit is contained in:
@@ -30,6 +30,13 @@ pub struct CursorPositionChanged {
|
||||
pub positions: CursorPositions,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FriendCursorPositionChanged {
|
||||
pub friend_id: String,
|
||||
pub positions: CursorPositions,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CursorState(RwLock<CursorPositions>);
|
||||
|
||||
@@ -91,9 +98,32 @@ fn update_cursor_position(handle: &AppHandle, positions: CursorPositions) {
|
||||
*guard = positions.clone();
|
||||
drop(guard);
|
||||
|
||||
if let Err(error) = (CursorPositionChanged { positions }).emit(handle) {
|
||||
if let Err(error) = (CursorPositionChanged {
|
||||
positions: positions.clone(),
|
||||
})
|
||||
.emit(handle)
|
||||
{
|
||||
eprintln!("Failed to emit cursor position change: {error}");
|
||||
}
|
||||
|
||||
handle
|
||||
.state::<crate::network::Network>()
|
||||
.send_live_data(crate::live_data::LiveData::Cursor { positions });
|
||||
}
|
||||
|
||||
pub(crate) fn emit_friend_position(
|
||||
handle: &AppHandle,
|
||||
friend_id: String,
|
||||
positions: CursorPositions,
|
||||
) {
|
||||
if let Err(error) = (FriendCursorPositionChanged {
|
||||
friend_id,
|
||||
positions,
|
||||
})
|
||||
.emit(handle)
|
||||
{
|
||||
eprintln!("Failed to emit friend cursor position change: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert absolute to normalized coordinates (0.12, 0.78), or normalized to absolute (1234, 567)
|
||||
|
||||
@@ -2,6 +2,7 @@ mod cursor;
|
||||
mod db;
|
||||
mod friends;
|
||||
mod keypair;
|
||||
mod live_data;
|
||||
mod network;
|
||||
mod profile;
|
||||
mod remotes;
|
||||
@@ -73,7 +74,9 @@ fn specta_builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
profile::ProfileChanged,
|
||||
network::NetworkStatusChanged,
|
||||
cursor::CursorPositionChanged,
|
||||
cursor::FriendCursorPositionChanged,
|
||||
ufa::ForegroundAppChanged,
|
||||
ufa::FriendForegroundAppChanged,
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::cursor::CursorPositions;
|
||||
use crate::ufa::AppMeta;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum LiveData {
|
||||
Cursor { positions: CursorPositions },
|
||||
ForegroundApp { meta: AppMeta },
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cursor::{CursorPosition, CursorPositions};
|
||||
|
||||
#[test]
|
||||
fn live_data_is_internally_tagged_for_client_side_dispatch() {
|
||||
let payload = serde_json::to_string(&LiveData::Cursor {
|
||||
positions: CursorPositions {
|
||||
raw: CursorPosition { x: 120.0, y: 80.0 },
|
||||
mapped: CursorPosition { x: 0.25, y: 0.5 },
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
r#"{"type":"cursor","positions":{"raw":{"x":120.0,"y":80.0},"mapped":{"x":0.25,"y":0.5}}}"#
|
||||
);
|
||||
assert!(matches!(
|
||||
serde_json::from_str(&payload).unwrap(),
|
||||
LiveData::Cursor { .. }
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ use wyd_common::{
|
||||
use crate::db::AppDatabase;
|
||||
use crate::friends::{self, FriendsChanged};
|
||||
use crate::keypair::AppKeypair;
|
||||
use crate::live_data::LiveData;
|
||||
use crate::remotes::{self, Remote, RemotesChanged};
|
||||
|
||||
type Statuses = Arc<Mutex<HashMap<String, (u64, ConnectionStatus)>>>;
|
||||
@@ -62,16 +63,23 @@ pub struct Network {
|
||||
}
|
||||
|
||||
impl Network {
|
||||
#[allow(dead_code)] // Ready for the first domain message sender.
|
||||
pub fn send(&self, remote_id: &str, payload: String) -> Result<(), String> {
|
||||
let connections = self.connections.lock().map_err(|error| error.to_string())?;
|
||||
let sender = connections
|
||||
.get(remote_id)
|
||||
.map(|connection| &connection.sender)
|
||||
.ok_or_else(|| "remote is not configured".to_string())?;
|
||||
sender
|
||||
.try_send(payload)
|
||||
.map_err(|error| format!("remote is not ready: {error}"))
|
||||
pub fn send_live_data(&self, data: LiveData) {
|
||||
let Ok(payload) = serde_json::to_string(&data) else {
|
||||
eprintln!("failed to serialize live data");
|
||||
return;
|
||||
};
|
||||
let Ok(connections) = self.connections.lock() else {
|
||||
eprintln!("failed to lock remote connections for live data");
|
||||
return;
|
||||
};
|
||||
for connection in connections.values() {
|
||||
match connection.sender.try_send(payload.clone()) {
|
||||
Ok(()) | Err(mpsc::error::TrySendError::Full(_)) => {}
|
||||
Err(mpsc::error::TrySendError::Closed(_)) => {
|
||||
eprintln!("remote {} cannot accept live data", connection.remote.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_profile(&self, profile: crate::user::User) {
|
||||
@@ -263,6 +271,7 @@ async fn connect(
|
||||
if !matches!(recv(&mut reader).await?, ServerMessage::Registered) {
|
||||
return Err("server rejected registration".into());
|
||||
}
|
||||
while outgoing.try_recv().is_ok() {}
|
||||
send(&mut writer, &ClientMessage::SyncFriendProfiles).await?;
|
||||
changed(
|
||||
handle,
|
||||
@@ -315,6 +324,19 @@ async fn connect(
|
||||
eprintln!("failed to synchronize friend profiles: {error}");
|
||||
}
|
||||
}
|
||||
ServerMessage::FriendLiveData { friend_id, payload } => {
|
||||
match serde_json::from_str(&payload) {
|
||||
Ok(LiveData::Cursor { positions }) => {
|
||||
crate::cursor::emit_friend_position(handle, friend_id, positions);
|
||||
}
|
||||
Ok(LiveData::ForegroundApp { meta }) => {
|
||||
crate::ufa::emit_friend_app(handle, friend_id, meta);
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("failed to decode friend live data: {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Message::Ping(data) => writer.send(Message::Pong(data)).await?,
|
||||
|
||||
@@ -19,6 +19,13 @@ pub struct ForegroundAppChanged {
|
||||
pub meta: AppMeta,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type, Event)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FriendForegroundAppChanged {
|
||||
pub friend_id: String,
|
||||
pub meta: AppMeta,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ForegroundAppState(RwLock<AppMeta>);
|
||||
|
||||
@@ -67,8 +74,18 @@ pub fn init(handle: &AppHandle) {
|
||||
*current = meta.clone();
|
||||
drop(current);
|
||||
|
||||
if let Err(error) = (ForegroundAppChanged { meta }).emit(&handle) {
|
||||
if let Err(error) = (ForegroundAppChanged { meta: meta.clone() }).emit(&handle) {
|
||||
eprintln!("Failed to emit foreground app change: {error}");
|
||||
}
|
||||
|
||||
handle
|
||||
.state::<crate::network::Network>()
|
||||
.send_live_data(crate::live_data::LiveData::ForegroundApp { meta });
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn emit_friend_app(handle: &AppHandle, friend_id: String, meta: AppMeta) {
|
||||
if let Err(error) = (FriendForegroundAppChanged { friend_id, meta }).emit(handle) {
|
||||
eprintln!("Failed to emit friend foreground app change: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user