diff --git a/src/app/mod.rs b/src/app/mod.rs index 7d231bd..3283367 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -145,6 +145,9 @@ pub enum AppMessage { FriendAddIdChanged(String), FriendAddNameChanged(String), 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), RenameFriend(EndpointId, String), /// 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::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) => { if !state.friends_read_only { let _ = state.controller.send(CoreCommand::RemoveFriend(id)); @@ -2405,6 +2428,36 @@ fn view(state: &AppState) -> Element<'_, AppMessage> { 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![ row![ 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(format!("ID: {}", short_id(&peer_id.to_string()))).size(11).color(color_subtext) ], + add_friend_el, horizontal_space(), share_el, mute_btn,