@@ -55,7 +55,7 @@ pub struct Room {
leave_when_empty: bool,
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
- follows_by_leader_id: HashMap<PeerId, HashSet<PeerId>>,
+ follows_by_leader_id: HashMap<PeerId, Vec<PeerId>>,
subscriptions: Vec<client::Subscription>,
pending_room_update: Option<Task<()>>,
maintain_connection: Option<Task<Option<()>>>,
@@ -459,6 +459,12 @@ impl Room {
self.participant_user_ids.contains(&user_id)
}
+ pub fn follows(&self, leader_id: PeerId) -> &[PeerId] {
+ self.follows_by_leader_id
+ .get(&leader_id)
+ .map_or(&[], |v| v.as_slice())
+ }
+
async fn handle_room_updated(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::RoomUpdated>,
@@ -636,10 +642,13 @@ impl Room {
}
};
- this.follows_by_leader_id
+ let list = this
+ .follows_by_leader_id
.entry(leader)
- .or_insert(Default::default())
- .insert(follower);
+ .or_insert(Vec::new());
+ if !list.contains(&follower) {
+ list.push(follower);
+ }
}
this.pending_room_update.take();
@@ -12,11 +12,11 @@ use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f, PathBuilder},
json::{self, ToJson},
- Border, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext,
- Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
+ Border, CursorStyle, Entity, ImageData, ModelHandle, MouseButton, MutableAppContext,
+ RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
};
use settings::Settings;
-use std::ops::Range;
+use std::{ops::Range, sync::Arc};
use theme::Theme;
use util::ResultExt;
use workspace::{FollowNextCollaborator, JoinProject, ToggleFollow, Workspace};
@@ -510,11 +510,7 @@ impl CollabTitlebarItem {
Some(self.render_face_pile(
&user,
replica_id,
- Some((
- participant.peer_id,
- &user.github_login,
- participant.location,
- )),
+ Some((participant.peer_id, participant.location)),
workspace,
theme,
cx,
@@ -564,18 +560,23 @@ impl CollabTitlebarItem {
&self,
user: &User,
replica_id: Option<ReplicaId>,
- peer: Option<(PeerId, &str, ParticipantLocation)>,
+ peer_id_and_location: Option<(PeerId, ParticipantLocation)>,
workspace: &ViewHandle<Workspace>,
theme: &Theme,
cx: &mut RenderContext<Self>,
) -> ElementBox {
- let is_followed = peer.map_or(false, |(peer_id, _, _)| {
+ let is_followed = peer_id_and_location.map_or(false, |(peer_id, _)| {
workspace.read(cx).is_following(peer_id)
});
+ let room = ActiveCall::global(cx).read(cx).room();
+ let get_followers = |leader_id: PeerId| -> &[PeerId] {
+ room.map_or(&[], |room| room.read(cx).follows(leader_id))
+ };
+
let mut avatar_style;
- if let Some((_, _, location)) = peer.as_ref() {
- if let ParticipantLocation::SharedProject { project_id } = *location {
+ if let Some((_, location)) = peer_id_and_location {
+ if let ParticipantLocation::SharedProject { project_id } = location {
if Some(project_id) == workspace.read(cx).project().read(cx).remote_id() {
avatar_style = theme.workspace.titlebar.avatar;
} else {
@@ -599,11 +600,36 @@ impl CollabTitlebarItem {
let content = Stack::new()
.with_children(user.avatar.as_ref().map(|avatar| {
- Image::new(avatar.clone())
- .with_style(avatar_style)
- .constrained()
- .with_width(theme.workspace.titlebar.avatar_width)
- .aligned()
+ Flex::row()
+ .with_child(Self::render_face(avatar.clone(), avatar_style, theme))
+ .with_children(
+ peer_id_and_location
+ .map(|(peer_id, _)| {
+ get_followers(peer_id)
+ .into_iter()
+ .map(|&follower| {
+ room.map(|room| {
+ room.read(cx)
+ .remote_participant_for_peer_id(follower)
+ .map(|participant| {
+ participant.user.avatar.as_ref().map(|avatar| {
+ Self::render_face(
+ avatar.clone(),
+ avatar_style,
+ theme,
+ )
+ })
+ })
+ .flatten()
+ })
+ .flatten()
+ })
+ .flatten()
+ })
+ .into_iter()
+ .flatten(),
+ )
+ .with_reversed_paint_order()
.boxed()
}))
.with_children(replica_color.map(|replica_color| {
@@ -621,7 +647,7 @@ impl CollabTitlebarItem {
.with_margin_left(theme.workspace.titlebar.avatar_margin)
.boxed();
- if let Some((peer_id, peer_github_login, location)) = peer {
+ if let Some((peer_id, location)) = peer_id_and_location {
if let Some(replica_id) = replica_id {
MouseEventHandler::<ToggleFollow>::new(replica_id.into(), cx, move |_, _| content)
.with_cursor_style(CursorStyle::PointingHand)
@@ -631,9 +657,9 @@ impl CollabTitlebarItem {
.with_tooltip::<ToggleFollow, _>(
peer_id.as_u64() as usize,
if is_followed {
- format!("Unfollow {}", peer_github_login)
+ format!("Unfollow {}", user.github_login)
} else {
- format!("Follow {}", peer_github_login)
+ format!("Follow {}", user.github_login)
},
Some(Box::new(FollowNextCollaborator)),
theme.tooltip.clone(),
@@ -654,7 +680,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::<JoinProject, _>(
peer_id.as_u64() as usize,
- format!("Follow {} into external project", peer_github_login),
+ format!("Follow {} into external project", user.github_login),
Some(Box::new(FollowNextCollaborator)),
theme.tooltip.clone(),
cx,
@@ -668,6 +694,15 @@ impl CollabTitlebarItem {
}
}
+ fn render_face(avatar: Arc<ImageData>, avatar_style: ImageStyle, theme: &Theme) -> ElementBox {
+ Image::new(avatar)
+ .with_style(avatar_style)
+ .constrained()
+ .with_width(theme.workspace.titlebar.avatar_width)
+ .aligned()
+ .boxed()
+ }
+
fn render_connection_status(
&self,
workspace: &ViewHandle<Workspace>,