stop forwarding data to remote when no friend is online
This commit is contained in:
@@ -173,7 +173,9 @@ async fn connect(
|
|||||||
changed = cursor_data.changed() => {
|
changed = cursor_data.changed() => {
|
||||||
changed.map_err(|_| "cursor sender closed")?;
|
changed.map_err(|_| "cursor sender closed")?;
|
||||||
let payload = { cursor_data.borrow_and_update().clone() };
|
let payload = { cursor_data.borrow_and_update().clone() };
|
||||||
if let Some(payload) = payload {
|
if let Some(payload) = payload
|
||||||
|
&& friend_presence.has_online_friends(&remote.id)?
|
||||||
|
{
|
||||||
send(&mut writer, &ClientMessage::Signed {
|
send(&mut writer, &ClientMessage::Signed {
|
||||||
signature: keypair.sign(&message_bytes(&payload)),
|
signature: keypair.sign(&message_bytes(&payload)),
|
||||||
payload,
|
payload,
|
||||||
@@ -183,7 +185,9 @@ async fn connect(
|
|||||||
changed = foreground_app_data.changed() => {
|
changed = foreground_app_data.changed() => {
|
||||||
changed.map_err(|_| "foreground-app sender closed")?;
|
changed.map_err(|_| "foreground-app sender closed")?;
|
||||||
let payload = { foreground_app_data.borrow_and_update().clone() };
|
let payload = { foreground_app_data.borrow_and_update().clone() };
|
||||||
if let Some(payload) = payload {
|
if let Some(payload) = payload
|
||||||
|
&& friend_presence.has_online_friends(&remote.id)?
|
||||||
|
{
|
||||||
send(&mut writer, &ClientMessage::Signed {
|
send(&mut writer, &ClientMessage::Signed {
|
||||||
signature: keypair.sign(&message_bytes(&payload)),
|
signature: keypair.sign(&message_bytes(&payload)),
|
||||||
payload,
|
payload,
|
||||||
|
|||||||
@@ -95,6 +95,14 @@ impl Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_live_data(&self, data: LiveData) {
|
pub fn send_live_data(&self, data: LiveData) {
|
||||||
|
let active_remotes = match self.friend_presence.active_remotes() {
|
||||||
|
Ok(remotes) if remotes.is_empty() => return,
|
||||||
|
Ok(remotes) => remotes,
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("failed to read friend presence for live data: {error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let sequence = match data.kind() {
|
let sequence = match data.kind() {
|
||||||
LiveDataKind::Cursor => self.next_cursor_sequence.fetch_add(1, Ordering::Relaxed),
|
LiveDataKind::Cursor => self.next_cursor_sequence.fetch_add(1, Ordering::Relaxed),
|
||||||
LiveDataKind::ForegroundApp => self
|
LiveDataKind::ForegroundApp => self
|
||||||
@@ -114,7 +122,10 @@ impl Network {
|
|||||||
eprintln!("failed to lock remote connections for live data");
|
eprintln!("failed to lock remote connections for live data");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
for connection in connections.values() {
|
for (remote_id, connection) in connections.iter() {
|
||||||
|
if !active_remotes.contains(remote_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match envelope.kind() {
|
match envelope.kind() {
|
||||||
LiveDataKind::Cursor => {
|
LiveDataKind::Cursor => {
|
||||||
connection.cursor_sender.send_replace(Some(payload.clone()));
|
connection.cursor_sender.send_replace(Some(payload.clone()));
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ pub(super) struct Change {
|
|||||||
pub(super) struct FriendPresence(Mutex<HashMap<String, HashSet<String>>>);
|
pub(super) struct FriendPresence(Mutex<HashMap<String, HashSet<String>>>);
|
||||||
|
|
||||||
impl FriendPresence {
|
impl FriendPresence {
|
||||||
|
pub(super) fn active_remotes(&self) -> Result<HashSet<String>, String> {
|
||||||
|
let by_remote = self.0.lock().map_err(|error| error.to_string())?;
|
||||||
|
Ok(by_remote.keys().cloned().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn has_online_friends(&self, remote_id: &str) -> Result<bool, String> {
|
||||||
|
let by_remote = self.0.lock().map_err(|error| error.to_string())?;
|
||||||
|
Ok(by_remote.contains_key(remote_id))
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn remotes_for(&self, friend_id: &str) -> Result<HashSet<String>, String> {
|
pub(super) fn remotes_for(&self, friend_id: &str) -> Result<HashSet<String>, String> {
|
||||||
let by_remote = self.0.lock().map_err(|error| error.to_string())?;
|
let by_remote = self.0.lock().map_err(|error| error.to_string())?;
|
||||||
Ok(by_remote
|
Ok(by_remote
|
||||||
@@ -128,6 +138,67 @@ fn diff(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::{Change, FriendPresence};
|
use super::{Change, FriendPresence};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn live_data_routes_follow_each_remotes_online_friends() {
|
||||||
|
let presence = FriendPresence::default();
|
||||||
|
assert!(presence.active_remotes().unwrap().is_empty());
|
||||||
|
assert!(!presence.has_online_friends("remote-a").unwrap());
|
||||||
|
|
||||||
|
presence
|
||||||
|
.replace("remote-a", vec!["friend-a".into(), "friend-b".into()])
|
||||||
|
.unwrap();
|
||||||
|
presence.replace("remote-b", Vec::new()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
presence.active_remotes().unwrap(),
|
||||||
|
["remote-a".to_owned()].into()
|
||||||
|
);
|
||||||
|
assert!(!presence.has_online_friends("remote-b").unwrap());
|
||||||
|
|
||||||
|
presence
|
||||||
|
.update("remote-a", "friend-a".into(), false)
|
||||||
|
.unwrap();
|
||||||
|
assert!(presence.has_online_friends("remote-a").unwrap());
|
||||||
|
presence
|
||||||
|
.update("remote-a", "friend-b".into(), false)
|
||||||
|
.unwrap();
|
||||||
|
assert!(!presence.has_online_friends("remote-a").unwrap());
|
||||||
|
assert!(presence.active_remotes().unwrap().is_empty());
|
||||||
|
|
||||||
|
let change = presence
|
||||||
|
.update("remote-b", "friend-a".into(), true)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
change.route_added,
|
||||||
|
"resuming must trigger current-state publication"
|
||||||
|
);
|
||||||
|
assert!(presence.has_online_friends("remote-b").unwrap());
|
||||||
|
assert!(!presence.has_online_friends("remote-a").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disconnect_snapshot_and_friend_removal_disable_live_data_routes() {
|
||||||
|
let presence = FriendPresence::default();
|
||||||
|
for remote in ["disconnected", "empty-snapshot", "removed-friend"] {
|
||||||
|
presence.update(remote, "friend".into(), true).unwrap();
|
||||||
|
}
|
||||||
|
presence.remove("disconnected").unwrap();
|
||||||
|
presence.replace("empty-snapshot", Vec::new()).unwrap();
|
||||||
|
assert!(!presence.has_online_friends("disconnected").unwrap());
|
||||||
|
assert!(!presence.has_online_friends("empty-snapshot").unwrap());
|
||||||
|
assert!(presence.has_online_friends("removed-friend").unwrap());
|
||||||
|
presence.retain(&[]).unwrap();
|
||||||
|
assert!(presence.active_remotes().unwrap().is_empty());
|
||||||
|
assert!(!presence.has_online_friends("removed-friend").unwrap());
|
||||||
|
|
||||||
|
let change = presence
|
||||||
|
.replace("disconnected", vec!["friend".into()])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert!(change.route_added);
|
||||||
|
assert!(presence.has_online_friends("disconnected").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn friend_stays_online_while_any_remote_reports_presence() {
|
fn friend_stays_online_while_any_remote_reports_presence() {
|
||||||
let presence = FriendPresence::default();
|
let presence = FriendPresence::default();
|
||||||
|
|||||||
Reference in New Issue
Block a user