feat(friends): persist per-friend share preference across launches
The host's "auto-share my code with" picker was session-scoped — an in-memory exclusion set that reset to share-with-all on every launch. Move the preference onto the friend itself: a `share: bool` on `Friend` in friends.toml, `#[serde(default = true)]` so new friends are included and an older file without the field loads as share-with-all. The picker now toggles the stored flag and persists immediately (like the other settings), and `selected_share_targets` filters on it. This drops the parallel `share_excluded` state and is self-cleaning: removing a friend takes their preference with them, no stale ids linger. `upsert`'s update path leaves `share` untouched, so a name/presence refresh can't reset the user's choice. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+41
-1
@@ -35,6 +35,16 @@ pub struct Friend {
|
||||
/// Display name — seeded from the name the peer reported, locally editable.
|
||||
pub name: String,
|
||||
pub state: FriendState,
|
||||
/// Whether the host auto-shares its session code with this friend. Toggled
|
||||
/// on the host's share picker; persisted here so the choice survives a
|
||||
/// restart. Defaults to `true` so a newly added friend is included (and an
|
||||
/// older `friends.toml` without the field loads as share-with-all).
|
||||
#[serde(default = "default_share")]
|
||||
pub share: bool,
|
||||
}
|
||||
|
||||
fn default_share() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// The persisted friends list. Serialises as a TOML array of tables
|
||||
@@ -120,7 +130,12 @@ impl FriendStore {
|
||||
f.state = state;
|
||||
f
|
||||
} else {
|
||||
self.friends.push(Friend { id, name, state });
|
||||
self.friends.push(Friend {
|
||||
id,
|
||||
name,
|
||||
state,
|
||||
share: true,
|
||||
});
|
||||
self.friends.last_mut().expect("just pushed")
|
||||
}
|
||||
}
|
||||
@@ -186,6 +201,31 @@ mod tests {
|
||||
assert_eq!(back.friends, store.friends);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_friends_default_to_shared_and_survive_round_trip() {
|
||||
let mut store = FriendStore::default();
|
||||
let id = sample_id();
|
||||
store.upsert(id, "Alice".into(), FriendState::Accepted);
|
||||
assert!(store.find(&id).unwrap().share, "new friends start shared");
|
||||
|
||||
// An older friends.toml predating the field loads as share-with-all.
|
||||
let toml = format!("[[friends]]\nid = \"{id}\"\nname = \"Legacy\"\nstate = \"accepted\"\n");
|
||||
let back: FriendStore = toml::from_str(&toml).unwrap();
|
||||
assert!(back.friends[0].share);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upsert_preserves_share_across_refresh() {
|
||||
let mut store = FriendStore::default();
|
||||
let id = sample_id();
|
||||
store.upsert(id, "Alice".into(), FriendState::Accepted);
|
||||
store.find_mut(&id).unwrap().share = false;
|
||||
// A later name/presence refresh re-upserts the same peer; the share
|
||||
// choice must not be reset by it.
|
||||
store.upsert(id, "Alice (new name)".into(), FriendState::Accepted);
|
||||
assert!(!store.find(&id).unwrap().share);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upsert_updates_in_place() {
|
||||
let mut store = FriendStore::default();
|
||||
|
||||
Reference in New Issue
Block a user