mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-27 04:44:30 +00:00
Show worktree root names when sharing additional projects on a call
Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
This commit is contained in:
parent
94c68d246e
commit
b8c2acf0f2
9 changed files with 153 additions and 35 deletions
|
@ -23,6 +23,6 @@ impl ParticipantLocation {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteParticipant {
|
||||
pub user: Arc<User>,
|
||||
pub project_ids: Vec<u64>,
|
||||
pub projects: Vec<proto::ParticipantProject>,
|
||||
pub location: ParticipantLocation,
|
||||
}
|
||||
|
|
|
@ -13,7 +13,11 @@ use util::ResultExt;
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Event {
|
||||
RemoteProjectShared { owner: Arc<User>, project_id: u64 },
|
||||
RemoteProjectShared {
|
||||
owner: Arc<User>,
|
||||
project_id: u64,
|
||||
worktree_root_names: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Room {
|
||||
|
@ -219,16 +223,19 @@ impl Room {
|
|||
let peer_id = PeerId(participant.peer_id);
|
||||
this.participant_user_ids.insert(participant.user_id);
|
||||
|
||||
let existing_project_ids = this
|
||||
let existing_projects = this
|
||||
.remote_participants
|
||||
.get(&peer_id)
|
||||
.map(|existing| existing.project_ids.clone())
|
||||
.unwrap_or_default();
|
||||
for project_id in &participant.project_ids {
|
||||
if !existing_project_ids.contains(project_id) {
|
||||
.into_iter()
|
||||
.flat_map(|existing| &existing.projects)
|
||||
.map(|project| project.id)
|
||||
.collect::<HashSet<_>>();
|
||||
for project in &participant.projects {
|
||||
if !existing_projects.contains(&project.id) {
|
||||
cx.emit(Event::RemoteProjectShared {
|
||||
owner: user.clone(),
|
||||
project_id: *project_id,
|
||||
project_id: project.id,
|
||||
worktree_root_names: project.worktree_root_names.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -237,7 +244,7 @@ impl Room {
|
|||
peer_id,
|
||||
RemoteParticipant {
|
||||
user: user.clone(),
|
||||
project_ids: participant.project_ids,
|
||||
projects: participant.projects,
|
||||
location: ParticipantLocation::from_proto(participant.location)
|
||||
.unwrap_or(ParticipantLocation::External),
|
||||
},
|
||||
|
@ -334,9 +341,21 @@ impl Room {
|
|||
return Task::ready(Ok(project_id));
|
||||
}
|
||||
|
||||
let request = self
|
||||
.client
|
||||
.request(proto::ShareProject { room_id: self.id() });
|
||||
let request = self.client.request(proto::ShareProject {
|
||||
room_id: self.id(),
|
||||
worktrees: project
|
||||
.read(cx)
|
||||
.worktrees(cx)
|
||||
.map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
proto::WorktreeMetadata {
|
||||
id: worktree.id().to_proto(),
|
||||
root_name: worktree.root_name().into(),
|
||||
visible: worktree.is_visible(),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
cx.spawn_weak(|_, mut cx| async move {
|
||||
let response = request.await?;
|
||||
project
|
||||
|
|
|
@ -819,6 +819,7 @@ async fn test_active_call_events(
|
|||
avatar: None,
|
||||
}),
|
||||
project_id: project_a_id,
|
||||
worktree_root_names: vec!["a".to_string()],
|
||||
}]
|
||||
);
|
||||
|
||||
|
@ -836,6 +837,7 @@ async fn test_active_call_events(
|
|||
avatar: None,
|
||||
}),
|
||||
project_id: project_b_id,
|
||||
worktree_root_names: vec!["b".to_string()]
|
||||
}]
|
||||
);
|
||||
assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]);
|
||||
|
|
|
@ -827,7 +827,12 @@ impl Server {
|
|||
.user_id_for_connection(request.sender_id)?;
|
||||
let project_id = self.app_state.db.register_project(user_id).await?;
|
||||
let mut store = self.store().await;
|
||||
let room = store.share_project(request.payload.room_id, project_id, request.sender_id)?;
|
||||
let room = store.share_project(
|
||||
request.payload.room_id,
|
||||
project_id,
|
||||
request.payload.worktrees,
|
||||
request.sender_id,
|
||||
)?;
|
||||
response.send(proto::ShareProjectResponse {
|
||||
project_id: project_id.to_proto(),
|
||||
})?;
|
||||
|
@ -1036,11 +1041,13 @@ impl Server {
|
|||
let guest_connection_ids = state
|
||||
.read_project(project_id, request.sender_id)?
|
||||
.guest_connection_ids();
|
||||
state.update_project(project_id, &request.payload.worktrees, request.sender_id)?;
|
||||
let room =
|
||||
state.update_project(project_id, &request.payload.worktrees, request.sender_id)?;
|
||||
broadcast(request.sender_id, guest_connection_ids, |connection_id| {
|
||||
self.peer
|
||||
.forward_send(request.sender_id, connection_id, request.payload.clone())
|
||||
});
|
||||
self.room_updated(room);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -383,7 +383,7 @@ impl Store {
|
|||
room.participants.push(proto::Participant {
|
||||
user_id: connection.user_id.to_proto(),
|
||||
peer_id: creator_connection_id.0,
|
||||
project_ids: Default::default(),
|
||||
projects: Default::default(),
|
||||
location: Some(proto::ParticipantLocation {
|
||||
variant: Some(proto::participant_location::Variant::External(
|
||||
proto::participant_location::External {},
|
||||
|
@ -441,7 +441,7 @@ impl Store {
|
|||
room.participants.push(proto::Participant {
|
||||
user_id: user_id.to_proto(),
|
||||
peer_id: connection_id.0,
|
||||
project_ids: Default::default(),
|
||||
projects: Default::default(),
|
||||
location: Some(proto::ParticipantLocation {
|
||||
variant: Some(proto::participant_location::Variant::External(
|
||||
proto::participant_location::External {},
|
||||
|
@ -689,7 +689,8 @@ impl Store {
|
|||
anyhow::ensure!(
|
||||
room.participants
|
||||
.iter()
|
||||
.any(|participant| participant.project_ids.contains(&project.id)),
|
||||
.flat_map(|participant| &participant.projects)
|
||||
.any(|participant_project| participant_project.id == project.id),
|
||||
"no such project"
|
||||
);
|
||||
}
|
||||
|
@ -708,6 +709,7 @@ impl Store {
|
|||
&mut self,
|
||||
room_id: RoomId,
|
||||
project_id: ProjectId,
|
||||
worktrees: Vec<proto::WorktreeMetadata>,
|
||||
host_connection_id: ConnectionId,
|
||||
) -> Result<&proto::Room> {
|
||||
let connection = self
|
||||
|
@ -724,7 +726,14 @@ impl Store {
|
|||
.iter_mut()
|
||||
.find(|participant| participant.peer_id == host_connection_id.0)
|
||||
.ok_or_else(|| anyhow!("no such room"))?;
|
||||
participant.project_ids.push(project_id.to_proto());
|
||||
participant.projects.push(proto::ParticipantProject {
|
||||
id: project_id.to_proto(),
|
||||
worktree_root_names: worktrees
|
||||
.iter()
|
||||
.filter(|worktree| worktree.visible)
|
||||
.map(|worktree| worktree.root_name.clone())
|
||||
.collect(),
|
||||
});
|
||||
|
||||
connection.projects.insert(project_id);
|
||||
self.projects.insert(
|
||||
|
@ -741,7 +750,19 @@ impl Store {
|
|||
},
|
||||
guests: Default::default(),
|
||||
active_replica_ids: Default::default(),
|
||||
worktrees: Default::default(),
|
||||
worktrees: worktrees
|
||||
.into_iter()
|
||||
.map(|worktree| {
|
||||
(
|
||||
worktree.id,
|
||||
Worktree {
|
||||
root_name: worktree.root_name,
|
||||
visible: worktree.visible,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
language_servers: Default::default(),
|
||||
},
|
||||
);
|
||||
|
@ -779,8 +800,8 @@ impl Store {
|
|||
.find(|participant| participant.peer_id == connection_id.0)
|
||||
.ok_or_else(|| anyhow!("no such room"))?;
|
||||
participant
|
||||
.project_ids
|
||||
.retain(|id| *id != project_id.to_proto());
|
||||
.projects
|
||||
.retain(|project| project.id != project_id.to_proto());
|
||||
|
||||
Ok((room, project))
|
||||
} else {
|
||||
|
@ -796,7 +817,7 @@ impl Store {
|
|||
project_id: ProjectId,
|
||||
worktrees: &[proto::WorktreeMetadata],
|
||||
connection_id: ConnectionId,
|
||||
) -> Result<()> {
|
||||
) -> Result<&proto::Room> {
|
||||
let project = self
|
||||
.projects
|
||||
.get_mut(&project_id)
|
||||
|
@ -818,7 +839,23 @@ impl Store {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let room = self
|
||||
.rooms
|
||||
.get_mut(&project.room_id)
|
||||
.ok_or_else(|| anyhow!("no such room"))?;
|
||||
let participant_project = room
|
||||
.participants
|
||||
.iter_mut()
|
||||
.flat_map(|participant| &mut participant.projects)
|
||||
.find(|project| project.id == project_id.to_proto())
|
||||
.ok_or_else(|| anyhow!("no such project"))?;
|
||||
participant_project.worktree_root_names = worktrees
|
||||
.iter()
|
||||
.filter(|worktree| worktree.visible)
|
||||
.map(|worktree| worktree.root_name.clone())
|
||||
.collect();
|
||||
|
||||
Ok(room)
|
||||
} else {
|
||||
Err(anyhow!("no such project"))?
|
||||
}
|
||||
|
@ -1132,8 +1169,8 @@ impl Store {
|
|||
"room contains participant that has disconnected"
|
||||
);
|
||||
|
||||
for project_id in &participant.project_ids {
|
||||
let project = &self.projects[&ProjectId::from_proto(*project_id)];
|
||||
for participant_project in &participant.projects {
|
||||
let project = &self.projects[&ProjectId::from_proto(participant_project.id)];
|
||||
assert_eq!(
|
||||
project.room_id, *room_id,
|
||||
"project was shared on a different room"
|
||||
|
@ -1173,8 +1210,9 @@ impl Store {
|
|||
.unwrap();
|
||||
assert!(
|
||||
room_participant
|
||||
.project_ids
|
||||
.contains(&project_id.to_proto()),
|
||||
.projects
|
||||
.iter()
|
||||
.any(|project| project.id == project_id.to_proto()),
|
||||
"project was not shared in room"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,10 +19,16 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
|
||||
let active_call = ActiveCall::global(cx);
|
||||
cx.subscribe(&active_call, move |_, event, cx| match event {
|
||||
room::Event::RemoteProjectShared { owner, project_id } => {
|
||||
room::Event::RemoteProjectShared {
|
||||
owner,
|
||||
project_id,
|
||||
worktree_root_names,
|
||||
} => {
|
||||
const PADDING: f32 = 16.;
|
||||
let screen_size = cx.platform().screen_size();
|
||||
let window_size = vec2f(366., 64.);
|
||||
|
||||
let theme = &cx.global::<Settings>().theme.project_shared_notification;
|
||||
let window_size = vec2f(theme.window_width, theme.window_height);
|
||||
cx.add_window(
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(
|
||||
|
@ -34,7 +40,13 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
},
|
||||
|_| ProjectSharedNotification::new(*project_id, owner.clone()),
|
||||
|_| {
|
||||
ProjectSharedNotification::new(
|
||||
owner.clone(),
|
||||
*project_id,
|
||||
worktree_root_names.clone(),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
|
@ -43,12 +55,17 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
|
||||
pub struct ProjectSharedNotification {
|
||||
project_id: u64,
|
||||
worktree_root_names: Vec<String>,
|
||||
owner: Arc<User>,
|
||||
}
|
||||
|
||||
impl ProjectSharedNotification {
|
||||
fn new(project_id: u64, owner: Arc<User>) -> Self {
|
||||
Self { project_id, owner }
|
||||
fn new(owner: Arc<User>, project_id: u64, worktree_root_names: Vec<String>) -> Self {
|
||||
Self {
|
||||
project_id,
|
||||
worktree_root_names,
|
||||
owner,
|
||||
}
|
||||
}
|
||||
|
||||
fn join(&mut self, _: &JoinProject, cx: &mut ViewContext<Self>) {
|
||||
|
@ -84,13 +101,33 @@ impl ProjectSharedNotification {
|
|||
)
|
||||
.with_child(
|
||||
Label::new(
|
||||
"has shared a project with you".into(),
|
||||
format!(
|
||||
"shared a project in Zed{}",
|
||||
if self.worktree_root_names.is_empty() {
|
||||
""
|
||||
} else {
|
||||
":"
|
||||
}
|
||||
),
|
||||
theme.message.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(theme.message.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(if self.worktree_root_names.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
Label::new(
|
||||
self.worktree_root_names.join(", "),
|
||||
theme.worktree_roots.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(theme.worktree_roots.container)
|
||||
.boxed(),
|
||||
)
|
||||
})
|
||||
.contained()
|
||||
.with_style(theme.owner_metadata)
|
||||
.aligned()
|
||||
|
|
|
@ -163,10 +163,15 @@ message Room {
|
|||
message Participant {
|
||||
uint64 user_id = 1;
|
||||
uint32 peer_id = 2;
|
||||
repeated uint64 project_ids = 3;
|
||||
repeated ParticipantProject projects = 3;
|
||||
ParticipantLocation location = 4;
|
||||
}
|
||||
|
||||
message ParticipantProject {
|
||||
uint64 id = 1;
|
||||
repeated string worktree_root_names = 2;
|
||||
}
|
||||
|
||||
message ParticipantLocation {
|
||||
oneof variant {
|
||||
Project project = 1;
|
||||
|
@ -215,6 +220,7 @@ message RoomUpdated {
|
|||
|
||||
message ShareProject {
|
||||
uint64 room_id = 1;
|
||||
repeated WorktreeMetadata worktrees = 2;
|
||||
}
|
||||
|
||||
message ShareProjectResponse {
|
||||
|
|
|
@ -471,6 +471,8 @@ pub struct UpdateNotification {
|
|||
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct ProjectSharedNotification {
|
||||
pub window_height: f32,
|
||||
pub window_width: f32,
|
||||
#[serde(default)]
|
||||
pub background: Color,
|
||||
pub owner_container: ContainerStyle,
|
||||
|
@ -478,6 +480,7 @@ pub struct ProjectSharedNotification {
|
|||
pub owner_metadata: ContainerStyle,
|
||||
pub owner_username: ContainedText,
|
||||
pub message: ContainedText,
|
||||
pub worktree_roots: ContainedText,
|
||||
pub button_width: f32,
|
||||
pub join_button: ContainedText,
|
||||
pub dismiss_button: ContainedText,
|
||||
|
|
|
@ -2,8 +2,10 @@ import Theme from "../themes/common/theme";
|
|||
import { backgroundColor, borderColor, text } from "./components";
|
||||
|
||||
export default function projectSharedNotification(theme: Theme): Object {
|
||||
const avatarSize = 32;
|
||||
const avatarSize = 48;
|
||||
return {
|
||||
windowHeight: 72,
|
||||
windowWidth: 360,
|
||||
background: backgroundColor(theme, 300),
|
||||
ownerContainer: {
|
||||
padding: 12,
|
||||
|
@ -24,6 +26,10 @@ export default function projectSharedNotification(theme: Theme): Object {
|
|||
...text(theme, "sans", "secondary", { size: "xs" }),
|
||||
margin: { top: -3 },
|
||||
},
|
||||
worktreeRoots: {
|
||||
...text(theme, "sans", "secondary", { size: "xs", weight: "bold" }),
|
||||
margin: { top: -3 },
|
||||
},
|
||||
buttonWidth: 96,
|
||||
joinButton: {
|
||||
background: backgroundColor(theme, "info", "active"),
|
||||
|
|
Loading…
Reference in a new issue