Start work on displaying channels and invites in collab panel
This commit is contained in:
parent
003a711dea
commit
7954b02819
8 changed files with 412 additions and 70 deletions
|
@ -6,18 +6,19 @@ use rpc::{proto, TypedEnvelope};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct ChannelStore {
|
pub struct ChannelStore {
|
||||||
channels: Vec<Channel>,
|
channels: Vec<Arc<Channel>>,
|
||||||
channel_invitations: Vec<Channel>,
|
channel_invitations: Vec<Arc<Channel>>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
_rpc_subscription: Subscription,
|
_rpc_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub parent_id: Option<u64>,
|
pub parent_id: Option<u64>,
|
||||||
|
pub depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for ChannelStore {
|
impl Entity for ChannelStore {
|
||||||
|
@ -41,11 +42,11 @@ impl ChannelStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channels(&self) -> &[Channel] {
|
pub fn channels(&self) -> &[Arc<Channel>] {
|
||||||
&self.channels
|
&self.channels
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_invitations(&self) -> &[Channel] {
|
pub fn channel_invitations(&self) -> &[Arc<Channel>] {
|
||||||
&self.channel_invitations
|
&self.channel_invitations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +98,10 @@ impl ChannelStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_channel_invite_pending(&self, channel: &Arc<Channel>) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_member(
|
pub fn remove_member(
|
||||||
&self,
|
&self,
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
|
@ -124,66 +129,74 @@ impl ChannelStore {
|
||||||
_: Arc<Client>,
|
_: Arc<Client>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let payload = message.payload;
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.channels
|
this.update_channels(message.payload, cx);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_channels(
|
||||||
|
&mut self,
|
||||||
|
payload: proto::UpdateChannels,
|
||||||
|
cx: &mut ModelContext<ChannelStore>,
|
||||||
|
) {
|
||||||
|
self.channels
|
||||||
.retain(|channel| !payload.remove_channels.contains(&channel.id));
|
.retain(|channel| !payload.remove_channels.contains(&channel.id));
|
||||||
this.channel_invitations
|
self.channel_invitations
|
||||||
.retain(|channel| !payload.remove_channel_invitations.contains(&channel.id));
|
.retain(|channel| !payload.remove_channel_invitations.contains(&channel.id));
|
||||||
|
|
||||||
for channel in payload.channel_invitations {
|
for channel in payload.channel_invitations {
|
||||||
if let Some(existing_channel) = this
|
if let Some(existing_channel) = self
|
||||||
.channel_invitations
|
.channel_invitations
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|c| c.id == channel.id)
|
.find(|c| c.id == channel.id)
|
||||||
{
|
{
|
||||||
existing_channel.name = channel.name;
|
Arc::make_mut(existing_channel).name = channel.name;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.channel_invitations.insert(
|
self.channel_invitations.insert(
|
||||||
0,
|
0,
|
||||||
Channel {
|
Arc::new(Channel {
|
||||||
id: channel.id,
|
id: channel.id,
|
||||||
name: channel.name,
|
name: channel.name,
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
},
|
depth: 0,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for channel in payload.channels {
|
for channel in payload.channels {
|
||||||
if let Some(existing_channel) =
|
if let Some(existing_channel) = self.channels.iter_mut().find(|c| c.id == channel.id) {
|
||||||
this.channels.iter_mut().find(|c| c.id == channel.id)
|
Arc::make_mut(existing_channel).name = channel.name;
|
||||||
{
|
|
||||||
existing_channel.name = channel.name;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent_id) = channel.parent_id {
|
if let Some(parent_id) = channel.parent_id {
|
||||||
if let Some(ix) = this.channels.iter().position(|c| c.id == parent_id) {
|
if let Some(ix) = self.channels.iter().position(|c| c.id == parent_id) {
|
||||||
this.channels.insert(
|
let depth = self.channels[ix].depth + 1;
|
||||||
|
self.channels.insert(
|
||||||
ix + 1,
|
ix + 1,
|
||||||
Channel {
|
Arc::new(Channel {
|
||||||
id: channel.id,
|
id: channel.id,
|
||||||
name: channel.name,
|
name: channel.name,
|
||||||
parent_id: Some(parent_id),
|
parent_id: Some(parent_id),
|
||||||
},
|
depth,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.channels.insert(
|
self.channels.insert(
|
||||||
0,
|
0,
|
||||||
Channel {
|
Arc::new(Channel {
|
||||||
id: channel.id,
|
id: channel.id,
|
||||||
name: channel.name,
|
name: channel.name,
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
},
|
depth: 0,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
95
crates/client/src/channel_store_tests.rs
Normal file
95
crates/client/src/channel_store_tests.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use util::http::FakeHttpClient;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_update_channels(cx: &mut AppContext) {
|
||||||
|
let http = FakeHttpClient::with_404_response();
|
||||||
|
let client = Client::new(http.clone(), cx);
|
||||||
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||||
|
|
||||||
|
let channel_store = cx.add_model(|cx| ChannelStore::new(client, user_store, cx));
|
||||||
|
|
||||||
|
update_channels(
|
||||||
|
&channel_store,
|
||||||
|
proto::UpdateChannels {
|
||||||
|
channels: vec![
|
||||||
|
proto::Channel {
|
||||||
|
id: 1,
|
||||||
|
name: "b".to_string(),
|
||||||
|
parent_id: None,
|
||||||
|
},
|
||||||
|
proto::Channel {
|
||||||
|
id: 2,
|
||||||
|
name: "a".to_string(),
|
||||||
|
parent_id: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
assert_channels(
|
||||||
|
&channel_store,
|
||||||
|
&[
|
||||||
|
//
|
||||||
|
(0, "a"),
|
||||||
|
(0, "b"),
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
update_channels(
|
||||||
|
&channel_store,
|
||||||
|
proto::UpdateChannels {
|
||||||
|
channels: vec![
|
||||||
|
proto::Channel {
|
||||||
|
id: 3,
|
||||||
|
name: "x".to_string(),
|
||||||
|
parent_id: Some(1),
|
||||||
|
},
|
||||||
|
proto::Channel {
|
||||||
|
id: 4,
|
||||||
|
name: "y".to_string(),
|
||||||
|
parent_id: Some(2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
assert_channels(
|
||||||
|
&channel_store,
|
||||||
|
&[
|
||||||
|
//
|
||||||
|
(0, "a"),
|
||||||
|
(1, "y"),
|
||||||
|
(0, "b"),
|
||||||
|
(1, "x"),
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_channels(
|
||||||
|
channel_store: &ModelHandle<ChannelStore>,
|
||||||
|
message: proto::UpdateChannels,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) {
|
||||||
|
channel_store.update(cx, |store, cx| store.update_channels(message, cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_channels(
|
||||||
|
channel_store: &ModelHandle<ChannelStore>,
|
||||||
|
expected_channels: &[(usize, &str)],
|
||||||
|
cx: &AppContext,
|
||||||
|
) {
|
||||||
|
channel_store.read_with(cx, |store, _| {
|
||||||
|
let actual = store
|
||||||
|
.channels()
|
||||||
|
.iter()
|
||||||
|
.map(|c| (c.depth, c.name.as_str()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(actual, expected_channels);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod channel_store_tests;
|
||||||
|
|
||||||
pub mod channel_store;
|
pub mod channel_store;
|
||||||
pub mod telemetry;
|
pub mod telemetry;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
|
@ -193,6 +193,7 @@ impl TestServer {
|
||||||
let app_state = Arc::new(workspace::AppState {
|
let app_state = Arc::new(workspace::AppState {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
user_store: user_store.clone(),
|
user_store: user_store.clone(),
|
||||||
|
channel_store: channel_store.clone(),
|
||||||
languages: Arc::new(LanguageRegistry::test()),
|
languages: Arc::new(LanguageRegistry::test()),
|
||||||
fs: fs.clone(),
|
fs: fs.clone(),
|
||||||
build_window_options: |_, _, _| Default::default(),
|
build_window_options: |_, _, _| Default::default(),
|
||||||
|
|
|
@ -29,11 +29,12 @@ async fn test_basic_channels(
|
||||||
client_a.channel_store.read_with(cx_a, |channels, _| {
|
client_a.channel_store.read_with(cx_a, |channels, _| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
channels.channels(),
|
channels.channels(),
|
||||||
&[Channel {
|
&[Arc::new(Channel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".to_string(),
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
}]
|
depth: 0,
|
||||||
|
})]
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -56,11 +57,12 @@ async fn test_basic_channels(
|
||||||
client_b.channel_store.read_with(cx_b, |channels, _| {
|
client_b.channel_store.read_with(cx_b, |channels, _| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
channels.channel_invitations(),
|
channels.channel_invitations(),
|
||||||
&[Channel {
|
&[Arc::new(Channel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".to_string(),
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
}]
|
depth: 0,
|
||||||
|
})]
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,11 +78,12 @@ async fn test_basic_channels(
|
||||||
assert_eq!(channels.channel_invitations(), &[]);
|
assert_eq!(channels.channel_invitations(), &[]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
channels.channels(),
|
channels.channels(),
|
||||||
&[Channel {
|
&[Arc::new(Channel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".to_string(),
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
}]
|
depth: 0,
|
||||||
|
})]
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ mod panel_settings;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{proto::PeerId, Client, Contact, User, UserStore};
|
use client::{proto::PeerId, Channel, ChannelStore, Client, Contact, User, UserStore};
|
||||||
use contact_finder::build_contact_finder;
|
use contact_finder::build_contact_finder;
|
||||||
use context_menu::ContextMenu;
|
use context_menu::ContextMenu;
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
|
@ -62,6 +62,7 @@ pub struct CollabPanel {
|
||||||
entries: Vec<ContactEntry>,
|
entries: Vec<ContactEntry>,
|
||||||
selection: Option<usize>,
|
selection: Option<usize>,
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
|
channel_store: ModelHandle<ChannelStore>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
match_candidates: Vec<StringMatchCandidate>,
|
match_candidates: Vec<StringMatchCandidate>,
|
||||||
list_state: ListState<Self>,
|
list_state: ListState<Self>,
|
||||||
|
@ -109,8 +110,10 @@ enum ContactEntry {
|
||||||
peer_id: PeerId,
|
peer_id: PeerId,
|
||||||
is_last: bool,
|
is_last: bool,
|
||||||
},
|
},
|
||||||
|
ChannelInvite(Arc<Channel>),
|
||||||
IncomingRequest(Arc<User>),
|
IncomingRequest(Arc<User>),
|
||||||
OutgoingRequest(Arc<User>),
|
OutgoingRequest(Arc<User>),
|
||||||
|
Channel(Arc<Channel>),
|
||||||
Contact {
|
Contact {
|
||||||
contact: Arc<Contact>,
|
contact: Arc<Contact>,
|
||||||
calling: bool,
|
calling: bool,
|
||||||
|
@ -204,6 +207,16 @@ impl CollabPanel {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ContactEntry::Channel(channel) => {
|
||||||
|
Self::render_channel(&*channel, &theme.collab_panel, is_selected, cx)
|
||||||
|
}
|
||||||
|
ContactEntry::ChannelInvite(channel) => Self::render_channel_invite(
|
||||||
|
channel.clone(),
|
||||||
|
this.channel_store.clone(),
|
||||||
|
&theme.collab_panel,
|
||||||
|
is_selected,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
||||||
user.clone(),
|
user.clone(),
|
||||||
this.user_store.clone(),
|
this.user_store.clone(),
|
||||||
|
@ -241,6 +254,7 @@ impl CollabPanel {
|
||||||
entries: Vec::default(),
|
entries: Vec::default(),
|
||||||
selection: None,
|
selection: None,
|
||||||
user_store: workspace.user_store().clone(),
|
user_store: workspace.user_store().clone(),
|
||||||
|
channel_store: workspace.app_state().channel_store.clone(),
|
||||||
project: workspace.project().clone(),
|
project: workspace.project().clone(),
|
||||||
subscriptions: Vec::default(),
|
subscriptions: Vec::default(),
|
||||||
match_candidates: Vec::default(),
|
match_candidates: Vec::default(),
|
||||||
|
@ -320,6 +334,7 @@ impl CollabPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_entries(&mut self, cx: &mut ViewContext<Self>) {
|
fn update_entries(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
let channel_store = self.channel_store.read(cx);
|
||||||
let user_store = self.user_store.read(cx);
|
let user_store = self.user_store.read(cx);
|
||||||
let query = self.filter_editor.read(cx).text(cx);
|
let query = self.filter_editor.read(cx).text(cx);
|
||||||
let executor = cx.background().clone();
|
let executor = cx.background().clone();
|
||||||
|
@ -445,10 +460,65 @@ impl CollabPanel {
|
||||||
self.entries
|
self.entries
|
||||||
.push(ContactEntry::Header(Section::Channels, 0));
|
.push(ContactEntry::Header(Section::Channels, 0));
|
||||||
|
|
||||||
|
let channels = channel_store.channels();
|
||||||
|
if !channels.is_empty() {
|
||||||
|
self.match_candidates.clear();
|
||||||
|
self.match_candidates
|
||||||
|
.extend(
|
||||||
|
channels
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(ix, channel)| StringMatchCandidate {
|
||||||
|
id: ix,
|
||||||
|
string: channel.name.clone(),
|
||||||
|
char_bag: channel.name.chars().collect(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let matches = executor.block(match_strings(
|
||||||
|
&self.match_candidates,
|
||||||
|
&query,
|
||||||
|
true,
|
||||||
|
usize::MAX,
|
||||||
|
&Default::default(),
|
||||||
|
executor.clone(),
|
||||||
|
));
|
||||||
|
self.entries.extend(
|
||||||
|
matches
|
||||||
|
.iter()
|
||||||
|
.map(|mat| ContactEntry::Channel(channels[mat.candidate_id].clone())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.entries
|
self.entries
|
||||||
.push(ContactEntry::Header(Section::Contacts, 0));
|
.push(ContactEntry::Header(Section::Contacts, 0));
|
||||||
|
|
||||||
let mut request_entries = Vec::new();
|
let mut request_entries = Vec::new();
|
||||||
|
let channel_invites = channel_store.channel_invitations();
|
||||||
|
if !channel_invites.is_empty() {
|
||||||
|
self.match_candidates.clear();
|
||||||
|
self.match_candidates
|
||||||
|
.extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
|
||||||
|
StringMatchCandidate {
|
||||||
|
id: ix,
|
||||||
|
string: channel.name.clone(),
|
||||||
|
char_bag: channel.name.chars().collect(),
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
let matches = executor.block(match_strings(
|
||||||
|
&self.match_candidates,
|
||||||
|
&query,
|
||||||
|
true,
|
||||||
|
usize::MAX,
|
||||||
|
&Default::default(),
|
||||||
|
executor.clone(),
|
||||||
|
));
|
||||||
|
request_entries.extend(
|
||||||
|
matches.iter().map(|mat| {
|
||||||
|
ContactEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let incoming = user_store.incoming_contact_requests();
|
let incoming = user_store.incoming_contact_requests();
|
||||||
if !incoming.is_empty() {
|
if !incoming.is_empty() {
|
||||||
self.match_candidates.clear();
|
self.match_candidates.clear();
|
||||||
|
@ -1112,6 +1182,121 @@ impl CollabPanel {
|
||||||
event_handler.into_any()
|
event_handler.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_channel(
|
||||||
|
channel: &Channel,
|
||||||
|
theme: &theme::CollabPanel,
|
||||||
|
is_selected: bool,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> AnyElement<Self> {
|
||||||
|
let channel_id = channel.id;
|
||||||
|
MouseEventHandler::<Channel, Self>::new(channel.id as usize, cx, |state, cx| {
|
||||||
|
Flex::row()
|
||||||
|
.with_child({
|
||||||
|
Svg::new("icons/hash")
|
||||||
|
// .with_style(theme.contact_avatar)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
})
|
||||||
|
.with_child(
|
||||||
|
Label::new(channel.name.clone(), theme.contact_username.text.clone())
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.contact_username.container)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.flex(1., true),
|
||||||
|
)
|
||||||
|
.constrained()
|
||||||
|
.with_height(theme.row_height)
|
||||||
|
.contained()
|
||||||
|
.with_style(*theme.contact_row.in_state(is_selected).style_for(state))
|
||||||
|
})
|
||||||
|
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||||
|
this.join_channel(channel_id, cx);
|
||||||
|
})
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_channel_invite(
|
||||||
|
channel: Arc<Channel>,
|
||||||
|
user_store: ModelHandle<ChannelStore>,
|
||||||
|
theme: &theme::CollabPanel,
|
||||||
|
is_selected: bool,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> AnyElement<Self> {
|
||||||
|
enum Decline {}
|
||||||
|
enum Accept {}
|
||||||
|
|
||||||
|
let channel_id = channel.id;
|
||||||
|
let is_invite_pending = user_store.read(cx).is_channel_invite_pending(&channel);
|
||||||
|
let button_spacing = theme.contact_button_spacing;
|
||||||
|
|
||||||
|
Flex::row()
|
||||||
|
.with_child({
|
||||||
|
Svg::new("icons/hash")
|
||||||
|
// .with_style(theme.contact_avatar)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
})
|
||||||
|
.with_child(
|
||||||
|
Label::new(channel.name.clone(), theme.contact_username.text.clone())
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.contact_username.container)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.flex(1., true),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
MouseEventHandler::<Decline, Self>::new(
|
||||||
|
channel.id as usize,
|
||||||
|
cx,
|
||||||
|
|mouse_state, _| {
|
||||||
|
let button_style = if is_invite_pending {
|
||||||
|
&theme.disabled_button
|
||||||
|
} else {
|
||||||
|
theme.contact_button.style_for(mouse_state)
|
||||||
|
};
|
||||||
|
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||||
|
this.respond_to_channel_invite(channel_id, false, cx);
|
||||||
|
})
|
||||||
|
.contained()
|
||||||
|
.with_margin_right(button_spacing),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
MouseEventHandler::<Accept, Self>::new(
|
||||||
|
channel.id as usize,
|
||||||
|
cx,
|
||||||
|
|mouse_state, _| {
|
||||||
|
let button_style = if is_invite_pending {
|
||||||
|
&theme.disabled_button
|
||||||
|
} else {
|
||||||
|
theme.contact_button.style_for(mouse_state)
|
||||||
|
};
|
||||||
|
render_icon_button(button_style, "icons/check_8.svg")
|
||||||
|
.aligned()
|
||||||
|
.flex_float()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||||
|
this.respond_to_channel_invite(channel_id, true, cx);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.constrained()
|
||||||
|
.with_height(theme.row_height)
|
||||||
|
.contained()
|
||||||
|
.with_style(
|
||||||
|
*theme
|
||||||
|
.contact_row
|
||||||
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
|
)
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
|
||||||
fn render_contact_request(
|
fn render_contact_request(
|
||||||
user: Arc<User>,
|
user: Arc<User>,
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
|
@ -1384,6 +1569,18 @@ impl CollabPanel {
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn respond_to_channel_invite(
|
||||||
|
&mut self,
|
||||||
|
channel_id: u64,
|
||||||
|
accept: bool,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
let respond = self.channel_store.update(cx, |store, _| {
|
||||||
|
store.respond_to_channel_invite(channel_id, accept)
|
||||||
|
});
|
||||||
|
cx.foreground().spawn(respond).detach();
|
||||||
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&mut self,
|
&mut self,
|
||||||
recipient_user_id: u64,
|
recipient_user_id: u64,
|
||||||
|
@ -1396,6 +1593,12 @@ impl CollabPanel {
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn join_channel(&self, channel: u64, cx: &mut ViewContext<Self>) {
|
||||||
|
ActiveCall::global(cx)
|
||||||
|
.update(cx, |call, cx| call.join_channel(channel, cx))
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for CollabPanel {
|
impl View for CollabPanel {
|
||||||
|
@ -1557,6 +1760,16 @@ impl PartialEq for ContactEntry {
|
||||||
return peer_id_1 == peer_id_2;
|
return peer_id_1 == peer_id_2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ContactEntry::Channel(channel_1) => {
|
||||||
|
if let ContactEntry::Channel(channel_2) = other {
|
||||||
|
return channel_1.id == channel_2.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactEntry::ChannelInvite(channel_1) => {
|
||||||
|
if let ContactEntry::ChannelInvite(channel_2) = other {
|
||||||
|
return channel_1.id == channel_2.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
ContactEntry::IncomingRequest(user_1) => {
|
ContactEntry::IncomingRequest(user_1) => {
|
||||||
if let ContactEntry::IncomingRequest(user_2) = other {
|
if let ContactEntry::IncomingRequest(user_2) = other {
|
||||||
return user_1.id == user_2.id;
|
return user_1.id == user_2.id;
|
||||||
|
|
|
@ -14,7 +14,7 @@ use anyhow::{anyhow, Context, Result};
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{
|
use client::{
|
||||||
proto::{self, PeerId},
|
proto::{self, PeerId},
|
||||||
Client, TypedEnvelope, UserStore,
|
ChannelStore, Client, TypedEnvelope, UserStore,
|
||||||
};
|
};
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use drag_and_drop::DragAndDrop;
|
use drag_and_drop::DragAndDrop;
|
||||||
|
@ -400,8 +400,9 @@ pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) {
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub languages: Arc<LanguageRegistry>,
|
pub languages: Arc<LanguageRegistry>,
|
||||||
pub client: Arc<client::Client>,
|
pub client: Arc<Client>,
|
||||||
pub user_store: ModelHandle<client::UserStore>,
|
pub user_store: ModelHandle<UserStore>,
|
||||||
|
pub channel_store: ModelHandle<ChannelStore>,
|
||||||
pub fs: Arc<dyn fs::Fs>,
|
pub fs: Arc<dyn fs::Fs>,
|
||||||
pub build_window_options:
|
pub build_window_options:
|
||||||
fn(Option<WindowBounds>, Option<uuid::Uuid>, &dyn Platform) -> WindowOptions<'static>,
|
fn(Option<WindowBounds>, Option<uuid::Uuid>, &dyn Platform) -> WindowOptions<'static>,
|
||||||
|
@ -424,6 +425,8 @@ impl AppState {
|
||||||
let http_client = util::http::FakeHttpClient::with_404_response();
|
let http_client = util::http::FakeHttpClient::with_404_response();
|
||||||
let client = Client::new(http_client.clone(), cx);
|
let client = Client::new(http_client.clone(), cx);
|
||||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||||
|
let channel_store =
|
||||||
|
cx.add_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
|
||||||
|
|
||||||
theme::init((), cx);
|
theme::init((), cx);
|
||||||
client::init(&client, cx);
|
client::init(&client, cx);
|
||||||
|
@ -434,6 +437,7 @@ impl AppState {
|
||||||
fs,
|
fs,
|
||||||
languages,
|
languages,
|
||||||
user_store,
|
user_store,
|
||||||
|
channel_store,
|
||||||
initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
|
initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
|
||||||
build_window_options: |_, _, _| Default::default(),
|
build_window_options: |_, _, _| Default::default(),
|
||||||
background_actions: || &[],
|
background_actions: || &[],
|
||||||
|
@ -3406,10 +3410,15 @@ impl Workspace {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let client = project.read(cx).client();
|
||||||
|
let user_store = project.read(cx).user_store();
|
||||||
|
let channel_store =
|
||||||
|
cx.add_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
|
||||||
let app_state = Arc::new(AppState {
|
let app_state = Arc::new(AppState {
|
||||||
languages: project.read(cx).languages().clone(),
|
languages: project.read(cx).languages().clone(),
|
||||||
client: project.read(cx).client(),
|
client,
|
||||||
user_store: project.read(cx).user_store(),
|
user_store,
|
||||||
|
channel_store,
|
||||||
fs: project.read(cx).fs().clone(),
|
fs: project.read(cx).fs().clone(),
|
||||||
build_window_options: |_, _, _| Default::default(),
|
build_window_options: |_, _, _| Default::default(),
|
||||||
initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
|
initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
|
||||||
|
|
|
@ -7,7 +7,9 @@ use cli::{
|
||||||
ipc::{self, IpcSender},
|
ipc::{self, IpcSender},
|
||||||
CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
|
CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
|
||||||
};
|
};
|
||||||
use client::{self, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
|
use client::{
|
||||||
|
self, ChannelStore, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
|
||||||
|
};
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
||||||
use futures::{
|
use futures::{
|
||||||
|
@ -140,6 +142,8 @@ fn main() {
|
||||||
|
|
||||||
languages::init(languages.clone(), node_runtime.clone());
|
languages::init(languages.clone(), node_runtime.clone());
|
||||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
||||||
|
let channel_store =
|
||||||
|
cx.add_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));
|
||||||
|
|
||||||
cx.set_global(client.clone());
|
cx.set_global(client.clone());
|
||||||
|
|
||||||
|
@ -181,6 +185,7 @@ fn main() {
|
||||||
languages,
|
languages,
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
user_store,
|
user_store,
|
||||||
|
channel_store,
|
||||||
fs,
|
fs,
|
||||||
build_window_options,
|
build_window_options,
|
||||||
initialize_workspace,
|
initialize_workspace,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue