Detailed changes
@@ -3,9 +3,7 @@ mod channel_index;
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
use anyhow::{anyhow, Result};
use channel_index::ChannelIndex;
-use client::{
- ChannelId, Client, ClientSettings, HostedProjectId, Subscription, User, UserId, UserStore,
-};
+use client::{ChannelId, Client, ClientSettings, ProjectId, Subscription, User, UserId, UserStore};
use collections::{hash_map, HashMap, HashSet};
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{
@@ -37,7 +35,7 @@ struct NotesVersion {
#[derive(Debug, Clone)]
pub struct HostedProject {
- id: HostedProjectId,
+ project_id: ProjectId,
channel_id: ChannelId,
name: SharedString,
_visibility: proto::ChannelVisibility,
@@ -46,7 +44,7 @@ pub struct HostedProject {
impl From<proto::HostedProject> for HostedProject {
fn from(project: proto::HostedProject) -> Self {
Self {
- id: HostedProjectId(project.id),
+ project_id: ProjectId(project.project_id),
channel_id: ChannelId(project.channel_id),
_visibility: project.visibility(),
name: project.name.into(),
@@ -59,7 +57,7 @@ pub struct ChannelStore {
channel_invitations: Vec<Arc<Channel>>,
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
channel_states: HashMap<ChannelId, ChannelState>,
- hosted_projects: HashMap<HostedProjectId, HostedProject>,
+ hosted_projects: HashMap<ProjectId, HostedProject>,
outgoing_invites: HashSet<(ChannelId, UserId)>,
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
@@ -88,7 +86,7 @@ pub struct ChannelState {
observed_notes_version: NotesVersion,
observed_chat_message: Option<u64>,
role: Option<ChannelRole>,
- projects: HashSet<HostedProjectId>,
+ projects: HashSet<ProjectId>,
}
impl Channel {
@@ -305,8 +303,8 @@ impl ChannelStore {
self.channel_index.by_id().get(&channel_id)
}
- pub fn projects_for_id(&self, channel_id: ChannelId) -> Vec<(SharedString, HostedProjectId)> {
- let mut projects: Vec<(SharedString, HostedProjectId)> = self
+ pub fn projects_for_id(&self, channel_id: ChannelId) -> Vec<(SharedString, ProjectId)> {
+ let mut projects: Vec<(SharedString, ProjectId)> = self
.channel_states
.get(&channel_id)
.map(|state| state.projects.clone())
@@ -1159,27 +1157,27 @@ impl ChannelStore {
let hosted_project: HostedProject = hosted_project.into();
if let Some(old_project) = self
.hosted_projects
- .insert(hosted_project.id, hosted_project.clone())
+ .insert(hosted_project.project_id, hosted_project.clone())
{
self.channel_states
.entry(old_project.channel_id)
.or_default()
- .remove_hosted_project(old_project.id);
+ .remove_hosted_project(old_project.project_id);
}
self.channel_states
.entry(hosted_project.channel_id)
.or_default()
- .add_hosted_project(hosted_project.id);
+ .add_hosted_project(hosted_project.project_id);
}
for hosted_project_id in payload.deleted_hosted_projects {
- let hosted_project_id = HostedProjectId(hosted_project_id);
+ let hosted_project_id = ProjectId(hosted_project_id);
if let Some(old_project) = self.hosted_projects.remove(&hosted_project_id) {
self.channel_states
.entry(old_project.channel_id)
.or_default()
- .remove_hosted_project(old_project.id);
+ .remove_hosted_project(old_project.project_id);
}
}
}
@@ -1289,11 +1287,11 @@ impl ChannelState {
}
}
- fn add_hosted_project(&mut self, project_id: HostedProjectId) {
+ fn add_hosted_project(&mut self, project_id: ProjectId) {
self.projects.insert(project_id);
}
- fn remove_hosted_project(&mut self, project_id: HostedProjectId) {
+ fn remove_hosted_project(&mut self, project_id: ProjectId) {
self.projects.remove(&project_id);
}
}
@@ -25,7 +25,7 @@ impl std::fmt::Display for ChannelId {
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
-pub struct HostedProjectId(pub u64);
+pub struct ProjectId(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantIndex(pub u32);
@@ -9,20 +9,21 @@ impl Database {
roles: &HashMap<ChannelId, ChannelRole>,
tx: &DatabaseTransaction,
) -> Result<Vec<proto::HostedProject>> {
- Ok(hosted_project::Entity::find()
+ let projects = hosted_project::Entity::find()
+ .find_also_related(project::Entity)
.filter(hosted_project::Column::ChannelId.is_in(channel_ids.iter().map(|id| id.0)))
.all(tx)
.await?
.into_iter()
- .flat_map(|project| {
- if project.deleted_at.is_some() {
+ .flat_map(|(hosted_project, project)| {
+ if hosted_project.deleted_at.is_some() {
return None;
}
- match project.visibility {
+ match hosted_project.visibility {
ChannelVisibility::Public => {}
ChannelVisibility::Members => {
let is_visible = roles
- .get(&project.channel_id)
+ .get(&hosted_project.channel_id)
.map(|role| role.can_see_all_descendants())
.unwrap_or(false);
if !is_visible {
@@ -31,13 +32,15 @@ impl Database {
}
};
Some(proto::HostedProject {
- id: project.id.to_proto(),
- channel_id: project.channel_id.to_proto(),
- name: project.name.clone(),
- visibility: project.visibility.into(),
+ project_id: project?.id.to_proto(),
+ channel_id: hosted_project.channel_id.to_proto(),
+ name: hosted_project.name.clone(),
+ visibility: hosted_project.visibility.into(),
})
})
- .collect())
+ .collect();
+
+ Ok(projects)
}
pub async fn get_hosted_project(
@@ -512,18 +512,30 @@ impl Database {
/// Adds the given connection to the specified hosted project
pub async fn join_hosted_project(
&self,
- id: HostedProjectId,
+ id: ProjectId,
user_id: UserId,
connection: ConnectionId,
) -> Result<(Project, ReplicaId)> {
self.transaction(|tx| async move {
- let (hosted_project, role) = self.get_hosted_project(id, user_id, &tx).await?;
- let project = project::Entity::find()
- .filter(project::Column::HostedProjectId.eq(hosted_project.id))
+ let (project, hosted_project) = project::Entity::find_by_id(id)
+ .find_also_related(hosted_project::Entity)
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("hosted project is no longer shared"))?;
+ let Some(hosted_project) = hosted_project else {
+ return Err(anyhow!("project is not hosted"))?;
+ };
+
+ let channel = channel::Entity::find_by_id(hosted_project.channel_id)
+ .one(&*tx)
+ .await?
+ .ok_or_else(|| anyhow!("no such channel"))?;
+
+ let role = self
+ .check_user_is_channel_participant(&channel, user_id, &tx)
+ .await?;
+
self.join_project_internal(project, user_id, connection, role, &tx)
.await
})
@@ -15,4 +15,13 @@ pub struct Model {
impl ActiveModelBehavior for ActiveModel {}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {}
+pub enum Relation {
+ #[sea_orm(has_one = "super::project::Entity")]
+ Project,
+}
+
+impl Related<super::project::Entity> for Entity {
+ fn to() -> RelationDef {
+ Relation::Project.def()
+ }
+}
@@ -50,6 +50,12 @@ pub enum Relation {
Collaborators,
#[sea_orm(has_many = "super::language_server::Entity")]
LanguageServers,
+ #[sea_orm(
+ belongs_to = "super::hosted_project::Entity",
+ from = "Column::HostedProjectId",
+ to = "super::hosted_project::Column::Id"
+ )]
+ HostedProject,
}
impl Related<super::user::Entity> for Entity {
@@ -82,4 +88,10 @@ impl Related<super::language_server::Entity> for Entity {
}
}
+impl Related<super::hosted_project::Entity> for Entity {
+ fn to() -> RelationDef {
+ Relation::HostedProject.def()
+ }
+}
+
impl ActiveModelBehavior for ActiveModel {}
@@ -4,9 +4,9 @@ use crate::{
auth::{self, Impersonator},
db::{
self, BufferId, ChannelId, ChannelRole, ChannelsForUser, CreatedChannelMessage, Database,
- HostedProjectId, InviteMemberResult, MembershipUpdated, MessageId, NotificationId, Project,
- ProjectId, RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, ServerId,
- User, UserId,
+ InviteMemberResult, MembershipUpdated, MessageId, NotificationId, Project, ProjectId,
+ RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, ServerId, User,
+ UserId,
},
executor::Executor,
AppState, Error, Result,
@@ -1770,7 +1770,7 @@ async fn join_hosted_project(
.db()
.await
.join_hosted_project(
- HostedProjectId(request.id as i32),
+ ProjectId(request.project_id as i32),
session.user_id,
session.connection_id,
)
@@ -8,7 +8,7 @@ use crate::{
};
use call::ActiveCall;
use channel::{Channel, ChannelEvent, ChannelStore};
-use client::{ChannelId, Client, Contact, HostedProjectId, User, UserStore};
+use client::{ChannelId, Client, Contact, ProjectId, User, UserStore};
use contact_finder::ContactFinder;
use db::kvp::KEY_VALUE_STORE;
use editor::{Editor, EditorElement, EditorStyle};
@@ -185,7 +185,7 @@ enum ListEntry {
depth: usize,
},
HostedProject {
- id: HostedProjectId,
+ id: ProjectId,
name: SharedString,
},
Contact {
@@ -1035,7 +1035,7 @@ impl CollabPanel {
fn render_channel_project(
&self,
- id: HostedProjectId,
+ id: ProjectId,
name: &SharedString,
is_selected: bool,
cx: &mut ViewContext<Self>,
@@ -12,8 +12,7 @@ mod project_tests;
use anyhow::{anyhow, bail, Context as _, Result};
use async_trait::async_trait;
use client::{
- proto, Client, Collaborator, HostedProjectId, PendingEntitySubscription, TypedEnvelope,
- UserStore,
+ proto, Client, Collaborator, PendingEntitySubscription, ProjectId, TypedEnvelope, UserStore,
};
use clock::ReplicaId;
use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque};
@@ -175,7 +174,7 @@ pub struct Project {
prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
prettier_instances: HashMap<PathBuf, PrettierInstance>,
tasks: Model<Inventory>,
- hosted_project_id: Option<HostedProjectId>,
+ hosted_project_id: Option<ProjectId>,
}
pub enum LanguageServerToQuery {
@@ -780,29 +779,31 @@ impl Project {
}
pub async fn hosted(
- _hosted_project_id: HostedProjectId,
- _user_store: Model<UserStore>,
- _client: Arc<Client>,
- _languages: Arc<LanguageRegistry>,
- _fs: Arc<dyn Fs>,
- _cx: AsyncAppContext,
+ remote_id: ProjectId,
+ user_store: Model<UserStore>,
+ client: Arc<Client>,
+ languages: Arc<LanguageRegistry>,
+ fs: Arc<dyn Fs>,
+ cx: AsyncAppContext,
) -> Result<Model<Self>> {
- // let response = client
- // .request_envelope(proto::JoinHostedProject {
- // id: hosted_project_id.0,
- // })
- // .await?;
- // Self::from_join_project_response(
- // response,
- // Some(hosted_project_id),
- // client,
- // user_store,
- // languages,
- // fs,
- // cx,
- // )
- // .await
- Err(anyhow!("disabled"))
+ client.authenticate_and_connect(true, &cx).await?;
+
+ let subscription = client.subscribe_to_entity(remote_id.0)?;
+ let response = client
+ .request_envelope(proto::JoinHostedProject {
+ project_id: remote_id.0,
+ })
+ .await?;
+ Self::from_join_project_response(
+ response,
+ subscription,
+ client,
+ user_store,
+ languages,
+ fs,
+ cx,
+ )
+ .await
}
fn release(&mut self, cx: &mut AppContext) {
@@ -1050,7 +1051,7 @@ impl Project {
}
}
- pub fn hosted_project_id(&self) -> Option<HostedProjectId> {
+ pub fn hosted_project_id(&self) -> Option<ProjectId> {
self.hosted_project_id
}
@@ -408,7 +408,7 @@ message JoinProject {
}
message JoinHostedProject {
- uint64 id = 1;
+ uint64 project_id = 1;
}
message JoinProjectResponse {
@@ -1067,7 +1067,7 @@ message ChannelParticipants {
}
message HostedProject {
- uint64 id = 1;
+ uint64 project_id = 1;
uint64 channel_id = 2;
string name = 3;
ChannelVisibility visibility = 4;
@@ -15,7 +15,7 @@ use anyhow::{anyhow, Context as _, Result};
use call::{call_settings::CallSettings, ActiveCall};
use client::{
proto::{self, ErrorCode, PeerId},
- ChannelId, Client, ErrorExt, HostedProjectId, Status, TypedEnvelope, UserStore,
+ ChannelId, Client, ErrorExt, ProjectId, Status, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use derive_more::{Deref, DerefMut};
@@ -4462,7 +4462,7 @@ pub fn create_and_open_local_file(
}
pub fn join_hosted_project(
- hosted_project_id: HostedProjectId,
+ hosted_project_id: ProjectId,
app_state: Arc<AppState>,
cx: &mut AppContext,
) -> Task<Result<()>> {