Add API for propagating cursor locations #61

Merged
schmelczer merged 30 commits from asch/show-cursors into main 2025-06-08 20:20:53 +01:00
3 changed files with 52 additions and 24 deletions
Showing only changes of commit 0908a5b527 - Show all commits

Remove cursor for disconnected client

Andras Schmelczer 2025-06-07 12:09:34 +01:00
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C

View file

@ -100,6 +100,14 @@ impl Cursors {
.await; .await;
} }
} }
pub async fn remove_cursors_of_device(&self, vault_id: &str, device_id: &str) {
let mut vault_to_cursors = self.vault_to_cursors.lock().await;
if let Some(cursors) = vault_to_cursors.get_mut(vault_id) {
cursors.retain(|c| c.client_cursors.device_id != device_id);
}
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -12,12 +12,12 @@ use crate::{
server::auth::auth, server::auth::auth,
}; };
pub fn get_handshake( pub fn get_authenticated_handshake(
state: &AppState, state: &AppState,
vault_id: &VaultId, vault_id: &VaultId,
message: Message, message: Option<Message>,
) -> Result<WebSocketHandshake, SyncServerError> { ) -> Result<WebSocketHandshake, SyncServerError> {
if let Message::Text(message) = message { if let Some(Message::Text(message)) = message {
let message: WebSocketClientMessage = serde_json::from_str(&message) let message: WebSocketClientMessage = serde_json::from_str(&message)
.context("Failed to parse message") .context("Failed to parse message")
.map_err(server_error)?; .map_err(server_error)?;

View file

@ -20,10 +20,12 @@ use crate::{
CursorPositionFromServer, WebSocketClientMessage, WebSocketServerMessage, CursorPositionFromServer, WebSocketClientMessage, WebSocketServerMessage,
WebSocketVaultUpdate, WebSocketVaultUpdate,
}, },
utils::{get_handshake, get_unseen_documents, send_update_over_websocket}, utils::{
get_authenticated_handshake, get_unseen_documents, send_update_over_websocket,
},
}, },
}, },
errors::{SyncServerError, client_error, server_error, unauthenticated_error}, errors::{SyncServerError, client_error, server_error},
utils::normalize::normalize, utils::normalize::normalize,
}; };
@ -61,13 +63,15 @@ async fn websocket(
) -> Result<(), SyncServerError> { ) -> Result<(), SyncServerError> {
let (mut sender, mut websocket_receiver) = stream.split(); let (mut sender, mut websocket_receiver) = stream.split();
let handshake = if let Some(Ok(message)) = websocket_receiver.next().await { let handshake = get_authenticated_handshake(
get_handshake(&state, &vault_id, message)? &state,
} else { &vault_id,
return Err(unauthenticated_error(anyhow::anyhow!( websocket_receiver
"Failed to authenticate due to invalid message" .next()
))); .await
}; .transpose()
.unwrap_or_default(),
)?;
let mut broadcast_receiver = state.broadcasts.get_receiver(vault_id.clone()).await; let mut broadcast_receiver = state.broadcasts.get_receiver(vault_id.clone()).await;
@ -103,6 +107,8 @@ async fn websocket(
}); });
let device_id = handshake.device_id.clone(); let device_id = handshake.device_id.clone();
let vault_id_clone = vault_id.clone();
let cursor_manager = state.cursors.clone();
let mut receive_task = tokio::spawn(async move { let mut receive_task = tokio::spawn(async move {
while let Some(Ok(Message::Text(message))) = websocket_receiver.next().await { while let Some(Ok(Message::Text(message))) = websocket_receiver.next().await {
let message: WebSocketClientMessage = serde_json::from_str(&message) let message: WebSocketClientMessage = serde_json::from_str(&message)
@ -116,9 +122,12 @@ async fn websocket(
))); )));
} }
WebSocketClientMessage::CursorPositions(cursors) => { WebSocketClientMessage::CursorPositions(cursors) => {
state cursor_manager
.cursors .update_cursors(
.update_cursors(vault_id.clone(), &device_id, cursors.document_to_cursors) vault_id_clone.clone(),
&device_id,
cursors.document_to_cursors,
)
.await; .await;
} }
} }
@ -132,15 +141,26 @@ async fn websocket(
_ = &mut receive_task => send_task.abort(), _ = &mut receive_task => send_task.abort(),
}; };
send_task let result = {
.await send_task
.context("WebSocket send task failed") .await
.map_err(server_error)??; .context("WebSocket send task failed")
.map_err(server_error)
.and_then(|x| x)?;
receive_task receive_task
.await .await
.context("WebSocket receive task failed") .context("WebSocket receive task failed")
.map_err(server_error)??; .map_err(server_error)
.and_then(|x| x)?;
Ok(()) Ok(())
};
state
.cursors
.remove_cursors_of_device(&vault_id, &handshake.device_id)
.await;
result
} }