hosted projects (#8627)

- **Allow joining a hosted project**

You can't yet do anything in a hosted project, but you can join it and
look how empty it is.

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2024-03-04 19:17:40 -07:00 committed by GitHub
parent 4167c66b86
commit 27c5343707
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 519 additions and 232 deletions

View file

@ -4,8 +4,9 @@ use crate::{
auth::{self, Impersonator},
db::{
self, BufferId, ChannelId, ChannelRole, ChannelsForUser, CreatedChannelMessage, Database,
InviteMemberResult, MembershipUpdated, MessageId, NotificationId, ProjectId,
RemoveChannelMemberResult, RespondToChannelInvite, RoomId, ServerId, User, UserId,
HostedProjectId, InviteMemberResult, MembershipUpdated, MessageId, NotificationId, Project,
ProjectId, RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, ServerId,
User, UserId,
},
executor::Executor,
AppState, Error, Result,
@ -197,6 +198,7 @@ impl Server {
.add_request_handler(share_project)
.add_message_handler(unshare_project)
.add_request_handler(join_project)
.add_request_handler(join_hosted_project)
.add_message_handler(leave_project)
.add_request_handler(update_project)
.add_request_handler(update_worktree)
@ -1584,22 +1586,46 @@ async fn join_project(
session: Session,
) -> Result<()> {
let project_id = ProjectId::from_proto(request.project_id);
let guest_user_id = session.user_id;
tracing::info!(%project_id, "join project");
let (project, replica_id) = &mut *session
.db()
.await
.join_project(project_id, session.connection_id)
.join_project_in_room(project_id, session.connection_id)
.await?;
join_project_internal(response, session, project, replica_id)
}
trait JoinProjectInternalResponse {
fn send(self, result: proto::JoinProjectResponse) -> Result<()>;
}
impl JoinProjectInternalResponse for Response<proto::JoinProject> {
fn send(self, result: proto::JoinProjectResponse) -> Result<()> {
Response::<proto::JoinProject>::send(self, result)
}
}
impl JoinProjectInternalResponse for Response<proto::JoinHostedProject> {
fn send(self, result: proto::JoinProjectResponse) -> Result<()> {
Response::<proto::JoinHostedProject>::send(self, result)
}
}
fn join_project_internal(
response: impl JoinProjectInternalResponse,
session: Session,
project: &mut Project,
replica_id: &ReplicaId,
) -> Result<()> {
let collaborators = project
.collaborators
.iter()
.filter(|collaborator| collaborator.connection_id != session.connection_id)
.map(|collaborator| collaborator.to_proto())
.collect::<Vec<_>>();
let project_id = project.id;
let guest_user_id = session.user_id;
let worktrees = project
.worktrees
@ -1631,10 +1657,12 @@ async fn join_project(
// First, we send the metadata associated with each worktree.
response.send(proto::JoinProjectResponse {
project_id: project.id.0 as u64,
worktrees: worktrees.clone(),
replica_id: replica_id.0 as u32,
collaborators: collaborators.clone(),
language_servers: project.language_servers.clone(),
role: project.role.into(), // todo
})?;
for (worktree_id, worktree) in mem::take(&mut project.worktrees) {
@ -1707,15 +1735,17 @@ async fn join_project(
async fn leave_project(request: proto::LeaveProject, session: Session) -> Result<()> {
let sender_id = session.connection_id;
let project_id = ProjectId::from_proto(request.project_id);
let db = session.db().await;
if db.is_hosted_project(project_id).await? {
let project = db.leave_hosted_project(project_id, sender_id).await?;
project_left(&project, &session);
return Ok(());
}
let (room, project) = &*session
.db()
.await
.leave_project(project_id, sender_id)
.await?;
let (room, project) = &*db.leave_project(project_id, sender_id).await?;
tracing::info!(
%project_id,
host_user_id = %project.host_user_id,
host_user_id = ?project.host_user_id,
host_connection_id = ?project.host_connection_id,
"leave project"
);
@ -1726,6 +1756,24 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result
Ok(())
}
async fn join_hosted_project(
request: proto::JoinHostedProject,
response: Response<proto::JoinHostedProject>,
session: Session,
) -> Result<()> {
let (mut project, replica_id) = session
.db()
.await
.join_hosted_project(
HostedProjectId(request.id as i32),
session.user_id,
session.connection_id,
)
.await?;
join_project_internal(response, session, &mut project, &replica_id)
}
/// Updates other participants with changes to the project
async fn update_project(
request: proto::UpdateProject,
@ -3624,7 +3672,7 @@ async fn leave_channel_buffers_for_session(session: &Session) -> Result<()> {
fn project_left(project: &db::LeftProject, session: &Session) {
for connection_id in &project.connection_ids {
if project.host_user_id == session.user_id {
if project.host_user_id == Some(session.user_id) {
session
.peer
.send(