diff --git a/crates/client/src/channel_store.rs b/crates/client/src/channel_store.rs index b9aa4268cd..6325bc1a30 100644 --- a/crates/client/src/channel_store.rs +++ b/crates/client/src/channel_store.rs @@ -1,3 +1,4 @@ +use crate::Status; use crate::{Client, Subscription, User, UserStore}; use anyhow::anyhow; use anyhow::Result; @@ -21,7 +22,7 @@ pub struct ChannelStore { client: Arc, user_store: ModelHandle, _rpc_subscription: Subscription, - _maintain_user: Task<()>, + _watch_connection_status: Task<()>, } #[derive(Clone, Debug, PartialEq)] @@ -57,15 +58,16 @@ impl ChannelStore { let rpc_subscription = client.add_message_handler(cx.handle(), Self::handle_update_channels); - let mut current_user = user_store.read(cx).watch_current_user(); - let maintain_user = cx.spawn_weak(|this, mut cx| async move { - while let Some(current_user) = current_user.next().await { - if current_user.is_none() { + let mut connection_status = client.status(); + let watch_connection_status = cx.spawn_weak(|this, mut cx| async move { + while let Some(status) = connection_status.next().await { + if matches!(status, Status::ConnectionLost | Status::SignedOut) { if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { this.channels.clear(); this.channel_invitations.clear(); this.channel_participants.clear(); + this.channels_with_admin_privileges.clear(); this.outgoing_invites.clear(); cx.notify(); }); @@ -84,7 +86,7 @@ impl ChannelStore { client, user_store, _rpc_subscription: rpc_subscription, - _maintain_user: maintain_user, + _watch_connection_status: watch_connection_status, } } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 07d343959f..c2f0d31f90 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -2,7 +2,7 @@ mod connection_pool; use crate::{ auth, - db::{self, ChannelId, Database, ProjectId, RoomId, ServerId, User, UserId}, + db::{self, ChannelId, ChannelsForUser, Database, ProjectId, RoomId, ServerId, User, UserId}, executor::Executor, AppState, Result, }; @@ -541,8 +541,7 @@ impl Server { pool.add_connection(connection_id, user_id, user.admin); this.peer.send(connection_id, build_initial_contacts_update(contacts, &pool))?; this.peer.send(connection_id, build_initial_channels_update( - channels_for_user.channels, - channels_for_user.channel_participants, + channels_for_user, channel_invites ))?; @@ -2537,13 +2536,12 @@ fn to_tungstenite_message(message: AxumMessage) -> TungsteniteMessage { } fn build_initial_channels_update( - channels: Vec, - channel_participants: HashMap>, + channels: ChannelsForUser, channel_invites: Vec, ) -> proto::UpdateChannels { let mut update = proto::UpdateChannels::default(); - for channel in channels { + for channel in channels.channels { update.channels.push(proto::Channel { id: channel.id.to_proto(), name: channel.name, @@ -2551,7 +2549,7 @@ fn build_initial_channels_update( }); } - for (channel_id, participants) in channel_participants { + for (channel_id, participants) in channels.channel_participants { update .channel_participants .push(proto::ChannelParticipants { @@ -2560,6 +2558,18 @@ fn build_initial_channels_update( }); } + update + .channel_permissions + .extend( + channels + .channels_with_admin_privileges + .into_iter() + .map(|id| proto::ChannelPermission { + channel_id: id.to_proto(), + is_admin: true, + }), + ); + for channel in channel_invites { update.channel_invitations.push(proto::Channel { id: channel.id.to_proto(), diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index b2e9cae08a..63fab0d5f8 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -1,8 +1,11 @@ -use crate::tests::{room_participants, RoomParticipants, TestServer}; +use crate::{ + rpc::RECONNECT_TIMEOUT, + tests::{room_participants, RoomParticipants, TestServer}, +}; use call::ActiveCall; use client::{Channel, ChannelMembership, User}; use gpui::{executor::Deterministic, TestAppContext}; -use rpc::proto; +use rpc::{proto, RECEIVE_TIMEOUT}; use std::sync::Arc; #[gpui::test] @@ -49,7 +52,9 @@ async fn test_core_channels( depth: 1, }) ] - ) + ); + assert!(channels.is_user_admin(channel_a_id)); + assert!(channels.is_user_admin(channel_b_id)); }); client_b @@ -84,6 +89,7 @@ async fn test_core_channels( })] ) }); + let members = client_a .channel_store() .update(cx_a, |store, cx| { @@ -128,7 +134,6 @@ async fn test_core_channels( id: channel_a_id, name: "channel-a".to_string(), parent_id: None, - depth: 0, }), Arc::new(Channel { @@ -138,7 +143,9 @@ async fn test_core_channels( depth: 1, }) ] - ) + ); + assert!(!channels.is_user_admin(channel_a_id)); + assert!(!channels.is_user_admin(channel_b_id)); }); let channel_c_id = client_a @@ -280,6 +287,30 @@ async fn test_core_channels( client_b .channel_store() .read_with(cx_b, |channels, _| assert_eq!(channels.channels(), &[])); + + // When disconnected, client A sees no channels. + server.forbid_connections(); + server.disconnect_client(client_a.peer_id().unwrap()); + deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT); + client_a.channel_store().read_with(cx_a, |channels, _| { + assert_eq!(channels.channels(), &[]); + assert!(!channels.is_user_admin(channel_a_id)); + }); + + server.allow_connections(); + deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT); + client_a.channel_store().read_with(cx_a, |channels, _| { + assert_eq!( + channels.channels(), + &[Arc::new(Channel { + id: channel_a_id, + name: "channel-a".to_string(), + parent_id: None, + depth: 0, + })] + ); + assert!(channels.is_user_admin(channel_a_id)); + }); } fn assert_participants_eq(participants: &[Arc], expected_partitipants: &[u64]) {