mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Channel notifications from the server works
This commit is contained in:
parent
1469c02998
commit
9ba975d6ad
@ -43,6 +43,7 @@ pub type ChannelData = (Channel, ChannelPath);
|
||||
pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub has_changed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
|
||||
@ -207,6 +208,13 @@ impl ChannelStore {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_channel_buffer_changed(&self, channel_id: ChannelId) -> Option<bool> {
|
||||
self.channel_index
|
||||
.by_id()
|
||||
.get(&channel_id)
|
||||
.map(|channel| channel.has_changed)
|
||||
}
|
||||
|
||||
pub fn open_channel_chat(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
@ -779,6 +787,7 @@ impl ChannelStore {
|
||||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
has_changed: false,
|
||||
}),
|
||||
),
|
||||
}
|
||||
@ -787,7 +796,8 @@ impl ChannelStore {
|
||||
let channels_changed = !payload.channels.is_empty()
|
||||
|| !payload.delete_channels.is_empty()
|
||||
|| !payload.insert_edge.is_empty()
|
||||
|| !payload.delete_edge.is_empty();
|
||||
|| !payload.delete_edge.is_empty()
|
||||
|| !payload.notes_changed.is_empty();
|
||||
|
||||
if channels_changed {
|
||||
if !payload.delete_channels.is_empty() {
|
||||
@ -814,6 +824,10 @@ impl ChannelStore {
|
||||
index.insert(channel)
|
||||
}
|
||||
|
||||
for id_changed in payload.notes_changed {
|
||||
index.has_changed(id_changed);
|
||||
}
|
||||
|
||||
for edge in payload.insert_edge {
|
||||
index.insert_edge(edge.channel_id, edge.parent_id);
|
||||
}
|
||||
|
@ -76,6 +76,12 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_changed(&mut self, channel_id: ChannelId) {
|
||||
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
|
||||
Arc::make_mut(channel).has_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, channel_proto: proto::Channel) {
|
||||
if let Some(existing_channel) = self.channels_by_id.get_mut(&channel_proto.id) {
|
||||
Arc::make_mut(existing_channel).name = channel_proto.name;
|
||||
@ -85,6 +91,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
||||
Arc::new(Channel {
|
||||
id: channel_proto.id,
|
||||
name: channel_proto.name,
|
||||
has_changed: false,
|
||||
}),
|
||||
);
|
||||
self.insert_root(channel_proto.id);
|
||||
|
@ -291,12 +291,12 @@ CREATE INDEX "index_user_features_on_user_id" ON "user_features" ("user_id");
|
||||
CREATE INDEX "index_user_features_on_feature_id" ON "user_features" ("feature_id");
|
||||
|
||||
|
||||
CREATE TABLE "observed_channel_note_edits" (
|
||||
CREATE TABLE "observed_buffer_edits" (
|
||||
"user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE,
|
||||
"buffer_id" INTEGER NOT NULL REFERENCES buffers (id) ON DELETE CASCADE,
|
||||
"epoch" INTEGER NOT NULL,
|
||||
"lamport_timestamp" INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, channel_id)
|
||||
PRIMARY KEY (user_id, buffer_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "index_observed_notes_user_and_channel_id" ON "observed_channel_note_edits" ("user_id", "channel_id");
|
||||
CREATE UNIQUE INDEX "index_observed_buffers_user_and_buffer_id" ON "observed_buffer_edits" ("user_id", "buffer_id");
|
||||
|
@ -1,9 +1,9 @@
|
||||
CREATE TABLE "observed_channel_note_edits" (
|
||||
CREATE TABLE "observed_buffer_edits" (
|
||||
"user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE,
|
||||
"buffer_id" INTEGER NOT NULL REFERENCES buffers (id) ON DELETE CASCADE,
|
||||
"epoch" INTEGER NOT NULL,
|
||||
"lamport_timestamp" INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, channel_id)
|
||||
PRIMARY KEY (user_id, buffer_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "index_observed_notes_user_and_channel_id" ON "observed_channel_note_edits" ("user_id", "channel_id");
|
||||
CREATE UNIQUE INDEX "index_observed_buffer_user_and_buffer_id" ON "observed_buffer_edits" ("user_id", "buffer_id");
|
||||
|
@ -436,6 +436,7 @@ pub struct Channel {
|
||||
pub struct ChannelsForUser {
|
||||
pub channels: ChannelGraph,
|
||||
pub channel_participants: HashMap<ChannelId, Vec<UserId>>,
|
||||
pub channels_with_changed_notes: HashSet<ChannelId>,
|
||||
pub channels_with_admin_privileges: HashSet<ChannelId>,
|
||||
}
|
||||
|
||||
|
@ -80,20 +80,20 @@ impl Database {
|
||||
|
||||
// Save the last observed operation
|
||||
if let Some(max_operation) = max_operation {
|
||||
observed_note_edits::Entity::insert(observed_note_edits::ActiveModel {
|
||||
observed_buffer_edits::Entity::insert(observed_buffer_edits::ActiveModel {
|
||||
user_id: ActiveValue::Set(user_id),
|
||||
channel_id: ActiveValue::Set(channel_id),
|
||||
buffer_id: ActiveValue::Set(buffer.id),
|
||||
epoch: ActiveValue::Set(max_operation.0),
|
||||
lamport_timestamp: ActiveValue::Set(max_operation.1),
|
||||
})
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
observed_note_edits::Column::UserId,
|
||||
observed_note_edits::Column::ChannelId,
|
||||
observed_buffer_edits::Column::UserId,
|
||||
observed_buffer_edits::Column::BufferId,
|
||||
])
|
||||
.update_columns([
|
||||
observed_note_edits::Column::Epoch,
|
||||
observed_note_edits::Column::LamportTimestamp,
|
||||
observed_buffer_edits::Column::Epoch,
|
||||
observed_buffer_edits::Column::LamportTimestamp,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
@ -110,20 +110,20 @@ impl Database {
|
||||
.map(|model| (model.epoch, model.lamport_timestamp));
|
||||
|
||||
if let Some(buffer_max) = buffer_max {
|
||||
observed_note_edits::Entity::insert(observed_note_edits::ActiveModel {
|
||||
observed_buffer_edits::Entity::insert(observed_buffer_edits::ActiveModel {
|
||||
user_id: ActiveValue::Set(user_id),
|
||||
channel_id: ActiveValue::Set(channel_id),
|
||||
buffer_id: ActiveValue::Set(buffer.id),
|
||||
epoch: ActiveValue::Set(buffer_max.0),
|
||||
lamport_timestamp: ActiveValue::Set(buffer_max.1),
|
||||
})
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
observed_note_edits::Column::UserId,
|
||||
observed_note_edits::Column::ChannelId,
|
||||
observed_buffer_edits::Column::UserId,
|
||||
observed_buffer_edits::Column::BufferId,
|
||||
])
|
||||
.update_columns([
|
||||
observed_note_edits::Column::Epoch,
|
||||
observed_note_edits::Column::LamportTimestamp,
|
||||
observed_buffer_edits::Column::Epoch,
|
||||
observed_buffer_edits::Column::LamportTimestamp,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
@ -463,7 +463,7 @@ impl Database {
|
||||
channel_id: ChannelId,
|
||||
user: UserId,
|
||||
operations: &[proto::Operation],
|
||||
) -> Result<Vec<ConnectionId>> {
|
||||
) -> Result<(Vec<ConnectionId>, Vec<UserId>)> {
|
||||
self.transaction(move |tx| async move {
|
||||
self.check_user_is_channel_member(channel_id, user, &*tx)
|
||||
.await?;
|
||||
@ -483,11 +483,24 @@ impl Database {
|
||||
.filter_map(|op| operation_to_storage(op, &buffer, serialization_version))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut channel_members;
|
||||
|
||||
if !operations.is_empty() {
|
||||
// get current channel participants and save the max operation above
|
||||
self.save_max_operation_for_collaborators(operations.as_slice(), channel_id, &*tx)
|
||||
self.save_max_operation_for_collaborators(
|
||||
operations.as_slice(),
|
||||
channel_id,
|
||||
buffer.id,
|
||||
&*tx,
|
||||
)
|
||||
.await?;
|
||||
|
||||
channel_members = self.get_channel_members_internal(channel_id, &*tx).await?;
|
||||
let collaborators = self
|
||||
.get_channel_buffer_collaborators_internal(channel_id, &*tx)
|
||||
.await?;
|
||||
channel_members.retain(|member| !collaborators.contains(member));
|
||||
|
||||
buffer_operation::Entity::insert_many(operations)
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
@ -501,6 +514,8 @@ impl Database {
|
||||
)
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
} else {
|
||||
channel_members = Vec::new();
|
||||
}
|
||||
|
||||
let mut connections = Vec::new();
|
||||
@ -519,7 +534,7 @@ impl Database {
|
||||
});
|
||||
}
|
||||
|
||||
Ok(connections)
|
||||
Ok((connections, channel_members))
|
||||
})
|
||||
.await
|
||||
}
|
||||
@ -528,6 +543,7 @@ impl Database {
|
||||
&self,
|
||||
operations: &[buffer_operation::ActiveModel],
|
||||
channel_id: ChannelId,
|
||||
buffer_id: BufferId,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<()> {
|
||||
let max_operation = operations
|
||||
@ -553,22 +569,22 @@ impl Database {
|
||||
.get_channel_buffer_collaborators_internal(channel_id, tx)
|
||||
.await?;
|
||||
|
||||
observed_note_edits::Entity::insert_many(users.iter().map(|id| {
|
||||
observed_note_edits::ActiveModel {
|
||||
observed_buffer_edits::Entity::insert_many(users.iter().map(|id| {
|
||||
observed_buffer_edits::ActiveModel {
|
||||
user_id: ActiveValue::Set(*id),
|
||||
channel_id: ActiveValue::Set(channel_id),
|
||||
buffer_id: ActiveValue::Set(buffer_id),
|
||||
epoch: max_operation.0.clone(),
|
||||
lamport_timestamp: ActiveValue::Set(*max_operation.1.as_ref()),
|
||||
}
|
||||
}))
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
observed_note_edits::Column::UserId,
|
||||
observed_note_edits::Column::ChannelId,
|
||||
observed_buffer_edits::Column::UserId,
|
||||
observed_buffer_edits::Column::BufferId,
|
||||
])
|
||||
.update_columns([
|
||||
observed_note_edits::Column::Epoch,
|
||||
observed_note_edits::Column::LamportTimestamp,
|
||||
observed_buffer_edits::Column::Epoch,
|
||||
observed_buffer_edits::Column::LamportTimestamp,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
@ -699,11 +715,36 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn has_buffer_changed(&self, user_id: UserId, channel_id: ChannelId) -> Result<bool> {
|
||||
self.transaction(|tx| async move {
|
||||
let user_max = observed_note_edits::Entity::find()
|
||||
.filter(observed_note_edits::Column::UserId.eq(user_id))
|
||||
.filter(observed_note_edits::Column::ChannelId.eq(channel_id))
|
||||
#[cfg(test)]
|
||||
pub async fn test_has_note_changed(
|
||||
&self,
|
||||
user_id: UserId,
|
||||
channel_id: ChannelId,
|
||||
) -> Result<bool> {
|
||||
self.transaction(|tx| async move { self.has_note_changed(user_id, channel_id, &*tx).await })
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn has_note_changed(
|
||||
&self,
|
||||
user_id: UserId,
|
||||
channel_id: ChannelId,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<bool> {
|
||||
let Some(buffer_id) = channel::Model {
|
||||
id: channel_id,
|
||||
..Default::default()
|
||||
}
|
||||
.find_related(buffer::Entity)
|
||||
.one(&*tx)
|
||||
.await?
|
||||
.map(|buffer| buffer.id) else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let user_max = observed_buffer_edits::Entity::find()
|
||||
.filter(observed_buffer_edits::Column::UserId.eq(user_id))
|
||||
.filter(observed_buffer_edits::Column::BufferId.eq(buffer_id))
|
||||
.one(&*tx)
|
||||
.await?
|
||||
.map(|model| (model.epoch, model.lamport_timestamp));
|
||||
@ -734,9 +775,7 @@ impl Database {
|
||||
// check if this user observed the last edit of the previous epoch
|
||||
channel_max = buffer_operation::Entity::find()
|
||||
.filter(buffer_operation::Column::BufferId.eq(channel_buffer.id))
|
||||
.filter(
|
||||
buffer_operation::Column::Epoch.eq(channel_buffer.epoch.saturating_sub(1)),
|
||||
)
|
||||
.filter(buffer_operation::Column::Epoch.eq(channel_buffer.epoch.saturating_sub(1)))
|
||||
.order_by(buffer_operation::Column::Epoch, Desc)
|
||||
.order_by(buffer_operation::Column::LamportTimestamp, Desc)
|
||||
.one(&*tx)
|
||||
@ -745,8 +784,6 @@ impl Database {
|
||||
}
|
||||
|
||||
Ok(user_max != channel_max)
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,7 +391,8 @@ impl Database {
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
self.get_user_channels(channel_memberships, &tx).await
|
||||
self.get_user_channels(user_id, channel_memberships, &tx)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
@ -414,13 +415,15 @@ impl Database {
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
self.get_user_channels(channel_membership, &tx).await
|
||||
self.get_user_channels(user_id, channel_membership, &tx)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_user_channels(
|
||||
&self,
|
||||
user_id: UserId,
|
||||
channel_memberships: Vec<channel_member::Model>,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<ChannelsForUser> {
|
||||
@ -460,10 +463,18 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
let mut channels_with_changed_notes = HashSet::default();
|
||||
for channel in graph.channels.iter() {
|
||||
if self.has_note_changed(user_id, channel.id, tx).await? {
|
||||
channels_with_changed_notes.insert(channel.id);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ChannelsForUser {
|
||||
channels: graph,
|
||||
channel_participants,
|
||||
channels_with_admin_privileges,
|
||||
channels_with_changed_notes,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ pub mod contact;
|
||||
pub mod feature_flag;
|
||||
pub mod follower;
|
||||
pub mod language_server;
|
||||
pub mod observed_note_edits;
|
||||
pub mod observed_buffer_edits;
|
||||
pub mod project;
|
||||
pub mod project_collaborator;
|
||||
pub mod room;
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::db::{ChannelId, UserId};
|
||||
use crate::db::{BufferId, UserId};
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "observed_channel_note_edits")]
|
||||
#[sea_orm(table_name = "observed_buffer_edits")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub user_id: UserId,
|
||||
pub channel_id: ChannelId,
|
||||
pub buffer_id: BufferId,
|
||||
pub epoch: i32,
|
||||
pub lamport_timestamp: i32,
|
||||
}
|
||||
@ -14,11 +14,11 @@ pub struct Model {
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::channel::Entity",
|
||||
from = "Column::ChannelId",
|
||||
to = "super::channel::Column::Id"
|
||||
belongs_to = "super::buffer::Entity",
|
||||
from = "Column::BufferId",
|
||||
to = "super::buffer::Column::Id"
|
||||
)]
|
||||
Channel,
|
||||
Buffer,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
@ -27,9 +27,9 @@ pub enum Relation {
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::channel::Entity> for Entity {
|
||||
impl Related<super::buffer::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Channel.def()
|
||||
Relation::Buffer.def()
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ async fn test_channel_buffers_diffs(db: &Database) {
|
||||
};
|
||||
|
||||
// Zero test: A should not register as changed on an unitialized channel buffer
|
||||
assert!(!db.has_buffer_changed(a_id, zed_id).await.unwrap());
|
||||
assert!(!db.test_has_note_changed(a_id, zed_id).await.unwrap());
|
||||
|
||||
let _ = db
|
||||
.join_channel_buffer(zed_id, a_id, connection_id_a)
|
||||
@ -228,7 +228,7 @@ async fn test_channel_buffers_diffs(db: &Database) {
|
||||
.unwrap();
|
||||
|
||||
// Zero test: A should register as changed on an empty channel buffer
|
||||
assert!(!db.has_buffer_changed(a_id, zed_id).await.unwrap());
|
||||
assert!(!db.test_has_note_changed(a_id, zed_id).await.unwrap());
|
||||
|
||||
let mut buffer_a = Buffer::new(0, 0, "".to_string());
|
||||
let mut operations = Vec::new();
|
||||
@ -245,15 +245,16 @@ async fn test_channel_buffers_diffs(db: &Database) {
|
||||
.unwrap();
|
||||
|
||||
// Smoke test: Does B register as changed, A as unchanged?
|
||||
assert!(db.has_buffer_changed(b_id, zed_id).await.unwrap());
|
||||
assert!(!db.has_buffer_changed(a_id, zed_id).await.unwrap());
|
||||
assert!(db.test_has_note_changed(b_id, zed_id).await.unwrap());
|
||||
|
||||
assert!(!db.test_has_note_changed(a_id, zed_id).await.unwrap());
|
||||
|
||||
db.leave_channel_buffer(zed_id, connection_id_a)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Snapshotting from leaving the channel buffer should not have a diff
|
||||
assert!(!db.has_buffer_changed(a_id, zed_id).await.unwrap());
|
||||
assert!(!db.test_has_note_changed(a_id, zed_id).await.unwrap());
|
||||
|
||||
let _ = db
|
||||
.join_channel_buffer(zed_id, b_id, connection_id_b)
|
||||
@ -261,13 +262,13 @@ async fn test_channel_buffers_diffs(db: &Database) {
|
||||
.unwrap();
|
||||
|
||||
// B has opened the channel buffer, so we shouldn't have any diff
|
||||
assert!(!db.has_buffer_changed(b_id, zed_id).await.unwrap());
|
||||
assert!(!db.test_has_note_changed(b_id, zed_id).await.unwrap());
|
||||
|
||||
db.leave_channel_buffer(zed_id, connection_id_b)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Since B just opened and closed the buffer without editing, neither should have a diff
|
||||
assert!(!db.has_buffer_changed(a_id, zed_id).await.unwrap());
|
||||
assert!(!db.has_buffer_changed(b_id, zed_id).await.unwrap());
|
||||
assert!(!db.test_has_note_changed(a_id, zed_id).await.unwrap());
|
||||
assert!(!db.test_has_note_changed(b_id, zed_id).await.unwrap());
|
||||
}
|
||||
|
@ -2691,7 +2691,7 @@ async fn update_channel_buffer(
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
|
||||
let collaborators = db
|
||||
let (collaborators, non_collaborators) = db
|
||||
.update_channel_buffer(channel_id, session.user_id, &request.operations)
|
||||
.await?;
|
||||
|
||||
@ -2704,6 +2704,25 @@ async fn update_channel_buffer(
|
||||
},
|
||||
&session.peer,
|
||||
);
|
||||
|
||||
let pool = &*session.connection_pool().await;
|
||||
|
||||
broadcast(
|
||||
None,
|
||||
non_collaborators
|
||||
.iter()
|
||||
.flat_map(|user_id| pool.user_connection_ids(*user_id)),
|
||||
|peer_id| {
|
||||
session.peer.send(
|
||||
peer_id.into(),
|
||||
proto::UpdateChannels {
|
||||
notes_changed: vec![channel_id.to_proto()],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -2986,6 +3005,12 @@ fn build_initial_channels_update(
|
||||
});
|
||||
}
|
||||
|
||||
update.notes_changed = channels
|
||||
.channels_with_changed_notes
|
||||
.iter()
|
||||
.map(|channel_id| channel_id.to_proto())
|
||||
.collect();
|
||||
|
||||
update.insert_edge = channels.channels.edges;
|
||||
|
||||
for (channel_id, participants) in channels.channel_participants {
|
||||
|
@ -410,10 +410,7 @@ async fn test_channel_buffer_disconnect(
|
||||
channel_buffer_a.update(cx_a, |buffer, _| {
|
||||
assert_eq!(
|
||||
buffer.channel().as_ref(),
|
||||
&Channel {
|
||||
id: channel_id,
|
||||
name: "the-channel".to_string()
|
||||
}
|
||||
&channel(channel_id, "the-channel")
|
||||
);
|
||||
assert!(!buffer.is_connected());
|
||||
});
|
||||
@ -438,15 +435,20 @@ async fn test_channel_buffer_disconnect(
|
||||
channel_buffer_b.update(cx_b, |buffer, _| {
|
||||
assert_eq!(
|
||||
buffer.channel().as_ref(),
|
||||
&Channel {
|
||||
id: channel_id,
|
||||
name: "the-channel".to_string()
|
||||
}
|
||||
&channel(channel_id, "the-channel")
|
||||
);
|
||||
assert!(!buffer.is_connected());
|
||||
});
|
||||
}
|
||||
|
||||
fn channel(id: u64, name: &'static str) -> Channel {
|
||||
Channel {
|
||||
id,
|
||||
name: name.to_string(),
|
||||
has_changed: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rejoin_channel_buffer(
|
||||
deterministic: Arc<Deterministic>,
|
||||
@ -627,6 +629,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
||||
let mut server = TestServer::start(&deterministic).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
|
||||
let client_c = server.create_client(cx_c, "user_c").await;
|
||||
|
||||
cx_a.update(editor::init);
|
||||
@ -757,6 +760,50 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_channel_buffer_changes(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
deterministic.forbid_parking();
|
||||
let mut server = TestServer::start(&deterministic).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
|
||||
let channel_id = server
|
||||
.make_channel(
|
||||
"the-channel",
|
||||
None,
|
||||
(&client_a, cx_a),
|
||||
&mut [(&client_b, cx_b)],
|
||||
)
|
||||
.await;
|
||||
|
||||
let channel_buffer_a = client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |store, cx| store.open_channel_buffer(channel_id, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
channel_buffer_a.update(cx_a, |buffer, cx| {
|
||||
buffer.buffer().update(cx, |buffer, cx| {
|
||||
buffer.edit([(0..0, "1")], None, cx);
|
||||
})
|
||||
});
|
||||
deterministic.run_until_parked();
|
||||
|
||||
let has_buffer_changed = cx_b.read(|cx| {
|
||||
client_b
|
||||
.channel_store()
|
||||
.read(cx)
|
||||
.has_channel_buffer_changed(channel_id)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
assert!(has_buffer_changed);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_collaborators(collaborators: &HashMap<PeerId, Collaborator>, ids: &[Option<UserId>]) {
|
||||
let mut user_ids = collaborators
|
||||
|
@ -1816,7 +1816,14 @@ impl CollabPanel {
|
||||
.left(),
|
||||
)
|
||||
.with_child(
|
||||
Label::new(channel.name.clone(), theme.channel_name.text.clone())
|
||||
Label::new(
|
||||
channel.name.clone(),
|
||||
theme
|
||||
.channel_name
|
||||
.in_state(channel.has_changed)
|
||||
.text
|
||||
.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(theme.channel_name.container)
|
||||
.aligned()
|
||||
|
@ -955,6 +955,7 @@ message UpdateChannels {
|
||||
repeated uint64 remove_channel_invitations = 6;
|
||||
repeated ChannelParticipants channel_participants = 7;
|
||||
repeated ChannelPermission channel_permissions = 8;
|
||||
repeated uint64 notes_changed = 9;
|
||||
}
|
||||
|
||||
message ChannelEdge {
|
||||
|
@ -251,7 +251,7 @@ pub struct CollabPanel {
|
||||
pub leave_call: Interactive<ContainedText>,
|
||||
pub contact_row: Toggleable<Interactive<ContainerStyle>>,
|
||||
pub channel_row: Toggleable<Interactive<ContainerStyle>>,
|
||||
pub channel_name: ContainedText,
|
||||
pub channel_name: Toggleable<ContainedText>,
|
||||
pub row_height: f32,
|
||||
pub project_row: Toggleable<Interactive<ProjectRow>>,
|
||||
pub tree_branch: Toggleable<Interactive<TreeBranch>>,
|
||||
|
@ -267,10 +267,18 @@ export default function contacts_panel(): any {
|
||||
}),
|
||||
channel_row: item_row,
|
||||
channel_name: {
|
||||
active: {
|
||||
...text(layer, "sans", { size: "sm", weight: "bold" }),
|
||||
margin: {
|
||||
left: CHANNEL_SPACING,
|
||||
},
|
||||
},
|
||||
inactive: {
|
||||
...text(layer, "sans", { size: "sm" }),
|
||||
margin: {
|
||||
left: CHANNEL_SPACING,
|
||||
},
|
||||
}
|
||||
},
|
||||
list_empty_label_container: {
|
||||
margin: {
|
||||
|
Loading…
Reference in New Issue
Block a user