feat(w7): add-friend-from-room — friend a co-participant with one click (P5)
Each participant card gets a star affordance: a clickable outline star (☆) that adds that peer to your friends list, or a non-interactive gold filled star (★) once they're already a friend (hidden while the friends store is read-only). The add pulls the peer's live presence name + address from the room roster and passes addr: Some(..) to CoreCommand::AddFriend, so the new friend is reachable immediately — no waiting for a future call to seed last_addr the way a bare add-by-id does. Name sanitized, short-id fallback; idempotent in core; no-op if already a friend. New AppMessage::AddFriendFromRoom(EndpointId). clippy --all-targets clean, 257 lib tests green. UI wiring screenshot-pending: the star + click need a live 2-machine call (a peer in the room) to verify visually. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -145,6 +145,9 @@ pub enum AppMessage {
|
|||||||
FriendAddIdChanged(String),
|
FriendAddIdChanged(String),
|
||||||
FriendAddNameChanged(String),
|
FriendAddNameChanged(String),
|
||||||
AddFriend,
|
AddFriend,
|
||||||
|
/// Add a peer you're in a call with to your friends list (W7), using their
|
||||||
|
/// live presence name + address so they're reachable immediately.
|
||||||
|
AddFriendFromRoom(EndpointId),
|
||||||
RemoveFriend(EndpointId),
|
RemoveFriend(EndpointId),
|
||||||
RenameFriend(EndpointId, String),
|
RenameFriend(EndpointId, String),
|
||||||
/// Join the gathering a friend is in (W7 B2), via the member ticket their
|
/// Join the gathering a friend is in (W7 B2), via the member ticket their
|
||||||
@@ -837,6 +840,26 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AppMessage::AddFriendFromRoom(id) => {
|
||||||
|
// Add a co-participant: pull their name + live address from the room
|
||||||
|
// roster so the friend is reachable right away (no wait for a future
|
||||||
|
// call to seed last_addr, unlike a bare add-by-id). No-op if read-only
|
||||||
|
// or already a friend; idempotent in core regardless.
|
||||||
|
if !state.friends_read_only
|
||||||
|
&& !state.friends.contains(&id)
|
||||||
|
&& let Some(peer) = state.peers.get(&id)
|
||||||
|
{
|
||||||
|
let name = {
|
||||||
|
let n = crate::sanitize::sanitize_name(&peer.name);
|
||||||
|
if n.is_empty() { short_id(&id.to_string()) } else { n }
|
||||||
|
};
|
||||||
|
let _ = state.controller.send(CoreCommand::AddFriend {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
addr: Some(peer.addr.clone()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
AppMessage::RemoveFriend(id) => {
|
AppMessage::RemoveFriend(id) => {
|
||||||
if !state.friends_read_only {
|
if !state.friends_read_only {
|
||||||
let _ = state.controller.send(CoreCommand::RemoveFriend(id));
|
let _ = state.controller.send(CoreCommand::RemoveFriend(id));
|
||||||
@@ -2405,6 +2428,36 @@ fn view(state: &AppState) -> Element<'_, AppMessage> {
|
|||||||
color_surface
|
color_surface
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add-friend affordance (W7): a star you can click to friend this
|
||||||
|
// co-participant; a filled gold star (non-interactive) once they're a
|
||||||
|
// friend. Hidden while their friend state is read-only-degraded.
|
||||||
|
let add_friend_el: Element<'_, AppMessage> = if state.friends_read_only {
|
||||||
|
iced::widget::Space::new().width(0.0).height(0.0).into()
|
||||||
|
} else if state.friends.contains(peer_id) {
|
||||||
|
tooltip(
|
||||||
|
text("★").size(16).color(color_yellow),
|
||||||
|
container(text("In your friends list").size(11).color(color_text))
|
||||||
|
.padding(8)
|
||||||
|
.style(c_style(color_crust, color_surface, 6.0)),
|
||||||
|
iced::widget::tooltip::Position::Bottom,
|
||||||
|
)
|
||||||
|
.gap(6)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
tooltip(
|
||||||
|
button(text("☆").size(16).color(color_subtext))
|
||||||
|
.on_press(AppMessage::AddFriendFromRoom(peer_id_clone))
|
||||||
|
.style(b_style(color_surface, color_blue, color_text, 6.0))
|
||||||
|
.padding([2, 6]),
|
||||||
|
container(text("Add to your friends list").size(11).color(color_text))
|
||||||
|
.padding(8)
|
||||||
|
.style(c_style(color_crust, color_surface, 6.0)),
|
||||||
|
iced::widget::tooltip::Position::Bottom,
|
||||||
|
)
|
||||||
|
.gap(6)
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
|
||||||
let mut card_content = column![
|
let mut card_content = column![
|
||||||
row![
|
row![
|
||||||
avatar_view(&peer.avatar, &peer.name, &peer_id.to_string(), 38.0),
|
avatar_view(&peer.avatar, &peer.name, &peer_id.to_string(), 38.0),
|
||||||
@@ -2412,6 +2465,7 @@ fn view(state: &AppState) -> Element<'_, AppMessage> {
|
|||||||
text(&peer.name).size(16).color(color_text),
|
text(&peer.name).size(16).color(color_text),
|
||||||
text(format!("ID: {}", short_id(&peer_id.to_string()))).size(11).color(color_subtext)
|
text(format!("ID: {}", short_id(&peer_id.to_string()))).size(11).color(color_subtext)
|
||||||
],
|
],
|
||||||
|
add_friend_el,
|
||||||
horizontal_space(),
|
horizontal_space(),
|
||||||
share_el,
|
share_el,
|
||||||
mute_btn,
|
mute_btn,
|
||||||
|
|||||||
Reference in New Issue
Block a user