The repo never enforced rustfmt, so formatting had drifted broadly. This is a
single mechanical `cargo fmt` pass over the whole crate (no behavioral change;
lib suite green, 493 passed). Going forward fmt should be enforced (planned CI
fmt --check step). Part of the 0.6.1 hygiene pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The friends list only ever updated a friend's status on a *successful*
presence probe, so it could ratchet a status up (offline -> online -> in a
room) but never down. A friend who dropped, left a room, or went invisible
kept showing a stale "online"/"in a room" status until PeerSpeak was
relaunched (which cleared the in-memory presence map back to offline).
The 60s auto-refresh scheduler already existed; the bug was that
`probe_friends_once` emitted nothing on a failed probe. Now every pass
reports a *definitive* status for every friend: a failed probe (or a
friend with no known address) is mapped to a new `FriendPresence::Offline`
via the pure, tested `presence::presence_from_probe`, so the list
self-heals each cycle.
Also adds a manual "⟳ Rescan" button to the Friends panel (new
`CoreCommand::RefreshFriends` -> immediate probe pass) for instant
feedback instead of waiting up to 60s.
469 lib tests pass, clippy clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rooms can now be named. A "Room name (optional)" field on the home Create card
mints a ticket carrying the label; PeerSpeakTicket gains a #[serde(default)]
`name` field (backward/forward compatible — serde ignores unknown fields and
defaults missing ones, so old/new builds still interoperate, just without
labels). restamp preserves the label so member-issued doors keep it; new
label_of helper reads it. Every member (creator or joiner) sets current_room.name
from the ticket, so presence reports a consistent "in <name>" to friends, and the
room-screen header shows the label under the wordmark. Labels are sanitized via
sanitize_name on both mint and display (untrusted peer-supplied ticket).
CoreCommand::Join gains room_name (used only when creating). +2 ticket tests
(label round-trip through restamp/label_of, pre-label backward-compat). clippy
--all-targets clean, 257 lib tests green.
Pure seam unit-tested + home field screenshot-verified; the in-room header label
and friend-side "in HangOut" presence display need a live/2-machine confirm.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the three tooltip'd presence radios in the home Friends card with a
pick_list dropdown + a one-line explainer for the current choice, mirroring the
Settings NetworkMode picker. Add PresenceMode::ALL + a Display impl (descriptive
labels) to back the picker. Tightens the Friends card vertically. clippy
--all-targets clean, 256 lib tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The logic half of the friends-only idle listener, built as pure unit-tested
seams so the security-critical decisions are provable without live networking.
src/presence.rs:
- ControlMsg { Ping, Pong { room: Option<RoomPresence> } } — self-describing
tagged JSON; unknown tags rejected (forward-compat).
- should_answer(from, friends, mode): the authorization gate — answer pings
from FRIENDS ONLY and never while invisible. This whitelist is what keeps
the always-on-while-open endpoint from being a stranger-facing spam/DoS
surface; must be the authenticated remote_id, never payload data.
- PresenceMode { Invisible, Normal(default), Discoverable } + helpers; persisted
in AppConfig (backward-compat default = Normal = friends-only, no beacon).
- interpret_pong: defensive reply handling — sanitizes the peer-supplied room
name and only surfaces a joinable room if its ticket actually parses, else
downgrades to plain Online (no dead/hostile Join button). Never auto-joins.
Deferred to a 2-machine session (the I/O edges): binding the live control
endpoint, its accept loop, and the ping scheduler. +8 presence tests, 256 lib
tests green, clippy clean, release builds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>