channel projects (#8456)

Add plumbing for hosted projects. This will currently show them if they
exist
but provides no UX to create/rename/delete them.

Also changed the `ChannelId` type to not auto-cast to u64; this avoids
type
confusion if you have multiple id types.


Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2024-02-26 22:15:11 -07:00 committed by GitHub
parent 8cf36ae603
commit c31626717f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 446 additions and 144 deletions

View file

@ -1,9 +1,9 @@
use anyhow::Result;
use call::report_call_event_for_channel;
use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore};
use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelStore};
use client::{
proto::{self, PeerId},
Collaborator, ParticipantIndex,
ChannelId, Collaborator, ParticipantIndex,
};
use collections::HashMap;
use editor::{
@ -454,7 +454,7 @@ impl FollowableItem for ChannelView {
Some(proto::view::Variant::ChannelView(
proto::view::ChannelView {
channel_id: channel_buffer.channel_id,
channel_id: channel_buffer.channel_id.0,
editor: if let Some(proto::view::Variant::Editor(proto)) =
self.editor.read(cx).to_state_proto(cx)
{
@ -480,7 +480,8 @@ impl FollowableItem for ChannelView {
unreachable!()
};
let open = ChannelView::open_in_pane(state.channel_id, None, pane, workspace, cx);
let open =
ChannelView::open_in_pane(ChannelId(state.channel_id), None, pane, workspace, cx);
Some(cx.spawn(|mut cx| async move {
let this = open.await?;

View file

@ -2,7 +2,7 @@ use crate::{collab_panel, ChatPanelSettings};
use anyhow::Result;
use call::{room, ActiveCall};
use channel::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId, ChannelStore};
use client::Client;
use client::{ChannelId, Client};
use collections::HashMap;
use db::kvp::KEY_VALUE_STORE;
use editor::Editor;
@ -169,7 +169,7 @@ impl ChatPanel {
})
}
pub fn channel_id(&self, cx: &AppContext) -> Option<u64> {
pub fn channel_id(&self, cx: &AppContext) -> Option<ChannelId> {
self.active_chat
.as_ref()
.map(|(chat, _)| chat.read(cx).channel_id)
@ -710,7 +710,7 @@ impl ChatPanel {
pub fn select_channel(
&mut self,
selected_channel_id: u64,
selected_channel_id: ChannelId,
scroll_to_message_id: Option<u64>,
cx: &mut ViewContext<ChatPanel>,
) -> Task<Result<()>> {

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
use client::UserId;
use channel::{ChannelMembership, ChannelStore, MessageParams};
use client::{ChannelId, UserId};
use collections::{HashMap, HashSet};
use editor::{AnchorRangeExt, CompletionProvider, Editor, EditorElement, EditorStyle};
use fuzzy::StringMatchCandidate;
@ -131,7 +131,7 @@ impl MessageEditor {
pub fn set_channel(
&mut self,
channel_id: u64,
channel_id: ChannelId,
channel_name: Option<SharedString>,
cx: &mut ViewContext<Self>,
) {

View file

@ -7,8 +7,8 @@ use crate::{
CollaborationPanelSettings,
};
use call::ActiveCall;
use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
use client::{Client, Contact, User, UserStore};
use channel::{Channel, ChannelEvent, ChannelStore, HostedProjectId};
use client::{ChannelId, Client, Contact, User, UserStore};
use contact_finder::ContactFinder;
use db::kvp::KEY_VALUE_STORE;
use editor::{Editor, EditorElement, EditorStyle};
@ -184,6 +184,10 @@ enum ListEntry {
ChannelEditor {
depth: usize,
},
HostedProject {
id: HostedProjectId,
name: SharedString,
},
Contact {
contact: Arc<Contact>,
calling: bool,
@ -326,7 +330,10 @@ impl CollabPanel {
panel.width = serialized_panel.width;
panel.collapsed_channels = serialized_panel
.collapsed_channels
.unwrap_or_else(|| Vec::new());
.unwrap_or_else(|| Vec::new())
.iter()
.map(|cid| ChannelId(*cid))
.collect();
cx.notify();
});
}
@ -344,7 +351,9 @@ impl CollabPanel {
COLLABORATION_PANEL_KEY.into(),
serde_json::to_string(&SerializedCollabPanel {
width,
collapsed_channels: Some(collapsed_channels),
collapsed_channels: Some(
collapsed_channels.iter().map(|cid| cid.0).collect(),
),
})?,
)
.await?;
@ -563,6 +572,7 @@ impl CollabPanel {
}
}
let hosted_projects = channel_store.projects_for_id(channel.id);
let has_children = channel_store
.channel_at_index(mat.candidate_id + 1)
.map_or(false, |next_channel| {
@ -596,6 +606,10 @@ impl CollabPanel {
});
}
}
for (name, id) in hosted_projects {
self.entries.push(ListEntry::HostedProject { id, name })
}
}
}
@ -1023,6 +1037,33 @@ impl CollabPanel {
.tooltip(move |cx| Tooltip::text("Open Chat", cx))
}
fn render_channel_project(
&self,
id: HostedProjectId,
name: &SharedString,
is_selected: bool,
cx: &mut ViewContext<Self>,
) -> impl IntoElement {
ListItem::new(ElementId::NamedInteger(
"channel-project".into(),
id.0 as usize,
))
.indent_level(2)
.indent_step_size(px(20.))
.selected(is_selected)
.on_click(cx.listener(move |_this, _, _cx| {
// todo!()
}))
.start_slot(
h_flex()
.relative()
.gap_1()
.child(IconButton::new(0, IconName::FileTree)),
)
.child(Label::new(name.clone()))
.tooltip(move |cx| Tooltip::text("Open Project", cx))
}
fn has_subchannels(&self, ix: usize) -> bool {
self.entries.get(ix).map_or(false, |entry| {
if let ListEntry::Channel { has_children, .. } = entry {
@ -1486,6 +1527,12 @@ impl CollabPanel {
ListEntry::ChannelChat { channel_id } => {
self.join_channel_chat(*channel_id, cx)
}
ListEntry::HostedProject {
id: _id,
name: _name,
} => {
// todo!()
}
ListEntry::OutgoingRequest(_) => {}
ListEntry::ChannelEditor { .. } => {}
@ -1923,7 +1970,7 @@ impl CollabPanel {
fn respond_to_channel_invite(
&mut self,
channel_id: u64,
channel_id: ChannelId,
accept: bool,
cx: &mut ViewContext<Self>,
) {
@ -1942,7 +1989,7 @@ impl CollabPanel {
.detach_and_prompt_err("Call failed", cx, |_, _| None);
}
fn join_channel(&self, channel_id: u64, cx: &mut ViewContext<Self>) {
fn join_channel(&self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
let Some(workspace) = self.workspace.upgrade() else {
return;
};
@ -2089,6 +2136,10 @@ impl CollabPanel {
ListEntry::ChannelChat { channel_id } => self
.render_channel_chat(*channel_id, is_selected, cx)
.into_any_element(),
ListEntry::HostedProject { id, name } => self
.render_channel_project(*id, name, is_selected, cx)
.into_any_element(),
}
}
@ -2405,7 +2456,7 @@ impl CollabPanel {
.tooltip(|cx| Tooltip::text("Accept invite", cx)),
];
ListItem::new(("channel-invite", channel.id as usize))
ListItem::new(("channel-invite", channel.id.0 as usize))
.selected(is_selected)
.child(
h_flex()
@ -2497,7 +2548,7 @@ impl CollabPanel {
div()
.h_6()
.id(channel_id as usize)
.id(channel_id.0 as usize)
.group("")
.flex()
.w_full()
@ -2525,7 +2576,7 @@ impl CollabPanel {
this.move_channel(dragged_channel.id, channel_id, cx);
}))
.child(
ListItem::new(channel_id as usize)
ListItem::new(channel_id.0 as usize)
// Add one level of depth for the disclosure arrow.
.indent_level(depth + 1)
.indent_step_size(px(20.))
@ -2572,7 +2623,7 @@ impl CollabPanel {
)
.child(
h_flex()
.id(channel_id as usize)
.id(channel_id.0 as usize)
.child(Label::new(channel.name.clone()))
.children(face_pile.map(|face_pile| face_pile.p_1())),
),
@ -2826,6 +2877,11 @@ impl PartialEq for ListEntry {
return channel_1.id == channel_2.id;
}
}
ListEntry::HostedProject { id, .. } => {
if let ListEntry::HostedProject { id: other_id, .. } = other {
return id == other_id;
}
}
ListEntry::ChannelNotes { channel_id } => {
if let ListEntry::ChannelNotes {
channel_id: other_id,

View file

@ -1,7 +1,7 @@
use channel::{ChannelId, ChannelMembership, ChannelStore};
use channel::{ChannelMembership, ChannelStore};
use client::{
proto::{self, ChannelRole, ChannelVisibility},
User, UserId, UserStore,
ChannelId, User, UserId, UserStore,
};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{

View file

@ -1,7 +1,7 @@
use crate::{chat_panel::ChatPanel, NotificationPanelSettings};
use anyhow::Result;
use channel::ChannelStore;
use client::{Client, Notification, User, UserStore};
use client::{ChannelId, Client, Notification, User, UserStore};
use collections::HashMap;
use db::kvp::KEY_VALUE_STORE;
use futures::StreamExt;
@ -357,7 +357,7 @@ impl NotificationPanel {
"{} invited you to join the #{channel_name} channel",
inviter.github_login
),
needs_response: channel_store.has_channel_invitation(channel_id),
needs_response: channel_store.has_channel_invitation(ChannelId(channel_id)),
actor: Some(inviter),
can_navigate: false,
})
@ -368,7 +368,7 @@ impl NotificationPanel {
message_id,
} => {
let sender = user_store.get_cached_user(sender_id)?;
let channel = channel_store.channel_for_id(channel_id)?;
let channel = channel_store.channel_for_id(ChannelId(channel_id))?;
let message = self
.notification_store
.read(cx)
@ -432,7 +432,7 @@ impl NotificationPanel {
if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
panel.update(cx, |panel, cx| {
panel
.select_channel(channel_id, Some(message_id), cx)
.select_channel(ChannelId(channel_id), Some(message_id), cx)
.detach_and_log_err(cx);
});
}
@ -454,7 +454,7 @@ impl NotificationPanel {
panel.is_scrolled_to_bottom()
&& panel
.active_chat()
.map_or(false, |chat| chat.read(cx).channel_id == *channel_id)
.map_or(false, |chat| chat.read(cx).channel_id.0 == *channel_id)
} else {
false
};