Cargo.lock 🔗
@@ -1569,6 +1569,7 @@ dependencies = [
"serde",
"serde_derive",
"settings",
+ "smallvec",
"theme",
"theme_selector",
"time",
Conrad Irwin created
Fix some bugs where ChannelNotes and ChannelChat had old cached channel
instances
Cargo.lock | 1
assets/keymaps/default.json | 198 ++---------
crates/call/src/room.rs | 2
crates/channel/src/channel_buffer.rs | 28 +
crates/channel/src/channel_chat.rs | 27
crates/channel/src/channel_store.rs | 46 ++
crates/channel/src/channel_store/channel_index.rs | 21
crates/collab/src/tests/channel_buffer_tests.rs | 4
crates/collab/src/tests/random_channel_buffer_tests.rs | 13
crates/collab_ui/Cargo.toml | 1
crates/collab_ui/src/channel_view.rs | 46 ++
crates/collab_ui/src/chat_panel.rs | 16
12 files changed, 185 insertions(+), 218 deletions(-)
@@ -1569,6 +1569,7 @@ dependencies = [
"serde",
"serde_derive",
"settings",
+ "smallvec",
"theme",
"theme_selector",
"time",
@@ -370,42 +370,15 @@
{
"context": "Pane",
"bindings": {
- "ctrl-1": [
- "pane::ActivateItem",
- 0
- ],
- "ctrl-2": [
- "pane::ActivateItem",
- 1
- ],
- "ctrl-3": [
- "pane::ActivateItem",
- 2
- ],
- "ctrl-4": [
- "pane::ActivateItem",
- 3
- ],
- "ctrl-5": [
- "pane::ActivateItem",
- 4
- ],
- "ctrl-6": [
- "pane::ActivateItem",
- 5
- ],
- "ctrl-7": [
- "pane::ActivateItem",
- 6
- ],
- "ctrl-8": [
- "pane::ActivateItem",
- 7
- ],
- "ctrl-9": [
- "pane::ActivateItem",
- 8
- ],
+ "ctrl-1": ["pane::ActivateItem", 0],
+ "ctrl-2": ["pane::ActivateItem", 1],
+ "ctrl-3": ["pane::ActivateItem", 2],
+ "ctrl-4": ["pane::ActivateItem", 3],
+ "ctrl-5": ["pane::ActivateItem", 4],
+ "ctrl-6": ["pane::ActivateItem", 5],
+ "ctrl-7": ["pane::ActivateItem", 6],
+ "ctrl-8": ["pane::ActivateItem", 7],
+ "ctrl-9": ["pane::ActivateItem", 8],
"ctrl-0": "pane::ActivateLastItem",
"ctrl--": "pane::GoBack",
"ctrl-_": "pane::GoForward",
@@ -416,42 +389,15 @@
{
"context": "Workspace",
"bindings": {
- "cmd-1": [
- "workspace::ActivatePane",
- 0
- ],
- "cmd-2": [
- "workspace::ActivatePane",
- 1
- ],
- "cmd-3": [
- "workspace::ActivatePane",
- 2
- ],
- "cmd-4": [
- "workspace::ActivatePane",
- 3
- ],
- "cmd-5": [
- "workspace::ActivatePane",
- 4
- ],
- "cmd-6": [
- "workspace::ActivatePane",
- 5
- ],
- "cmd-7": [
- "workspace::ActivatePane",
- 6
- ],
- "cmd-8": [
- "workspace::ActivatePane",
- 7
- ],
- "cmd-9": [
- "workspace::ActivatePane",
- 8
- ],
+ "cmd-1": ["workspace::ActivatePane", 0],
+ "cmd-2": ["workspace::ActivatePane", 1],
+ "cmd-3": ["workspace::ActivatePane", 2],
+ "cmd-4": ["workspace::ActivatePane", 3],
+ "cmd-5": ["workspace::ActivatePane", 4],
+ "cmd-6": ["workspace::ActivatePane", 5],
+ "cmd-7": ["workspace::ActivatePane", 6],
+ "cmd-8": ["workspace::ActivatePane", 7],
+ "cmd-9": ["workspace::ActivatePane", 8],
"cmd-b": "workspace::ToggleLeftDock",
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
@@ -494,38 +440,14 @@
},
{
"bindings": {
- "cmd-k cmd-left": [
- "workspace::ActivatePaneInDirection",
- "Left"
- ],
- "cmd-k cmd-right": [
- "workspace::ActivatePaneInDirection",
- "Right"
- ],
- "cmd-k cmd-up": [
- "workspace::ActivatePaneInDirection",
- "Up"
- ],
- "cmd-k cmd-down": [
- "workspace::ActivatePaneInDirection",
- "Down"
- ],
- "cmd-k shift-left": [
- "workspace::SwapPaneInDirection",
- "Left"
- ],
- "cmd-k shift-right": [
- "workspace::SwapPaneInDirection",
- "Right"
- ],
- "cmd-k shift-up": [
- "workspace::SwapPaneInDirection",
- "Up"
- ],
- "cmd-k shift-down": [
- "workspace::SwapPaneInDirection",
- "Down"
- ]
+ "cmd-k cmd-left": ["workspace::ActivatePaneInDirection", "Left"],
+ "cmd-k cmd-right": ["workspace::ActivatePaneInDirection", "Right"],
+ "cmd-k cmd-up": ["workspace::ActivatePaneInDirection", "Up"],
+ "cmd-k cmd-down": ["workspace::ActivatePaneInDirection", "Down"],
+ "cmd-k shift-left": ["workspace::SwapPaneInDirection", "Left"],
+ "cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
+ "cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
+ "cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"]
}
},
// Bindings from Atom
@@ -627,14 +549,6 @@
"space": "collab_panel::InsertSpace"
}
},
- {
- "context": "(CollabPanel && not_editing) > Editor",
- "bindings": {
- "cmd-c": "collab_panel::StartLinkChannel",
- "cmd-x": "collab_panel::StartMoveChannel",
- "cmd-v": "collab_panel::MoveOrLinkToSelected"
- }
- },
{
"context": "ChannelModal",
"bindings": {
@@ -655,57 +569,21 @@
"cmd-v": "terminal::Paste",
"cmd-k": "terminal::Clear",
// Some nice conveniences
- "cmd-backspace": [
- "terminal::SendText",
- "\u0015"
- ],
- "cmd-right": [
- "terminal::SendText",
- "\u0005"
- ],
- "cmd-left": [
- "terminal::SendText",
- "\u0001"
- ],
+ "cmd-backspace": ["terminal::SendText", "\u0015"],
+ "cmd-right": ["terminal::SendText", "\u0005"],
+ "cmd-left": ["terminal::SendText", "\u0001"],
// Terminal.app compatibility
- "alt-left": [
- "terminal::SendText",
- "\u001bb"
- ],
- "alt-right": [
- "terminal::SendText",
- "\u001bf"
- ],
+ "alt-left": ["terminal::SendText", "\u001bb"],
+ "alt-right": ["terminal::SendText", "\u001bf"],
// There are conflicting bindings for these keys in the global context.
// these bindings override them, remove at your own risk:
- "up": [
- "terminal::SendKeystroke",
- "up"
- ],
- "pageup": [
- "terminal::SendKeystroke",
- "pageup"
- ],
- "down": [
- "terminal::SendKeystroke",
- "down"
- ],
- "pagedown": [
- "terminal::SendKeystroke",
- "pagedown"
- ],
- "escape": [
- "terminal::SendKeystroke",
- "escape"
- ],
- "enter": [
- "terminal::SendKeystroke",
- "enter"
- ],
- "ctrl-c": [
- "terminal::SendKeystroke",
- "ctrl-c"
- ]
+ "up": ["terminal::SendKeystroke", "up"],
+ "pageup": ["terminal::SendKeystroke", "pageup"],
+ "down": ["terminal::SendKeystroke", "down"],
+ "pagedown": ["terminal::SendKeystroke", "pagedown"],
+ "escape": ["terminal::SendKeystroke", "escape"],
+ "enter": ["terminal::SendKeystroke", "enter"],
+ "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
}
}
]
@@ -55,7 +55,7 @@ pub enum Event {
pub struct Room {
id: u64,
- channel_id: Option<u64>,
+ pub channel_id: Option<u64>,
live_kit: Option<LiveKitRoom>,
status: RoomStatus,
shared_projects: HashSet<WeakModelHandle<Project>>,
@@ -1,4 +1,4 @@
-use crate::Channel;
+use crate::{Channel, ChannelId, ChannelStore};
use anyhow::Result;
use client::{Client, Collaborator, UserStore};
use collections::HashMap;
@@ -19,10 +19,11 @@ pub(crate) fn init(client: &Arc<Client>) {
}
pub struct ChannelBuffer {
- pub(crate) channel: Arc<Channel>,
+ pub channel_id: ChannelId,
connected: bool,
collaborators: HashMap<PeerId, Collaborator>,
user_store: ModelHandle<UserStore>,
+ channel_store: ModelHandle<ChannelStore>,
buffer: ModelHandle<language::Buffer>,
buffer_epoch: u64,
client: Arc<Client>,
@@ -34,6 +35,7 @@ pub enum ChannelBufferEvent {
CollaboratorsChanged,
Disconnected,
BufferEdited,
+ ChannelChanged,
}
impl Entity for ChannelBuffer {
@@ -46,7 +48,7 @@ impl Entity for ChannelBuffer {
}
self.client
.send(proto::LeaveChannelBuffer {
- channel_id: self.channel.id,
+ channel_id: self.channel_id,
})
.log_err();
}
@@ -58,6 +60,7 @@ impl ChannelBuffer {
channel: Arc<Channel>,
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
+ channel_store: ModelHandle<ChannelStore>,
mut cx: AsyncAppContext,
) -> Result<ModelHandle<Self>> {
let response = client
@@ -90,9 +93,10 @@ impl ChannelBuffer {
connected: true,
collaborators: Default::default(),
acknowledge_task: None,
- channel,
+ channel_id: channel.id,
subscription: Some(subscription.set_model(&cx.handle(), &mut cx.to_async())),
user_store,
+ channel_store,
};
this.replace_collaborators(response.collaborators, cx);
this
@@ -179,7 +183,7 @@ impl ChannelBuffer {
let operation = language::proto::serialize_operation(operation);
self.client
.send(proto::UpdateChannelBuffer {
- channel_id: self.channel.id,
+ channel_id: self.channel_id,
operations: vec![operation],
})
.log_err();
@@ -223,12 +227,15 @@ impl ChannelBuffer {
&self.collaborators
}
- pub fn channel(&self) -> Arc<Channel> {
- self.channel.clone()
+ pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+ self.channel_store
+ .read(cx)
+ .channel_for_id(self.channel_id)
+ .cloned()
}
pub(crate) fn disconnect(&mut self, cx: &mut ModelContext<Self>) {
- log::info!("channel buffer {} disconnected", self.channel.id);
+ log::info!("channel buffer {} disconnected", self.channel_id);
if self.connected {
self.connected = false;
self.subscription.take();
@@ -237,6 +244,11 @@ impl ChannelBuffer {
}
}
+ pub(crate) fn channel_changed(&mut self, cx: &mut ModelContext<Self>) {
+ cx.emit(ChannelBufferEvent::ChannelChanged);
+ cx.notify()
+ }
+
pub fn is_connected(&self) -> bool {
self.connected
}
@@ -14,7 +14,7 @@ use time::OffsetDateTime;
use util::{post_inc, ResultExt as _, TryFutureExt};
pub struct ChannelChat {
- channel: Arc<Channel>,
+ pub channel_id: ChannelId,
messages: SumTree<ChannelMessage>,
channel_store: ModelHandle<ChannelStore>,
loaded_all_messages: bool,
@@ -74,7 +74,7 @@ impl Entity for ChannelChat {
fn release(&mut self, _: &mut AppContext) {
self.rpc
.send(proto::LeaveChannelChat {
- channel_id: self.channel.id,
+ channel_id: self.channel_id,
})
.log_err();
}
@@ -99,7 +99,7 @@ impl ChannelChat {
Ok(cx.add_model(|cx| {
let mut this = Self {
- channel,
+ channel_id: channel.id,
user_store,
channel_store,
rpc: client,
@@ -116,8 +116,11 @@ impl ChannelChat {
}))
}
- pub fn channel(&self) -> &Arc<Channel> {
- &self.channel
+ pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+ self.channel_store
+ .read(cx)
+ .channel_for_id(self.channel_id)
+ .cloned()
}
pub fn send_message(
@@ -135,7 +138,7 @@ impl ChannelChat {
.current_user()
.ok_or_else(|| anyhow!("current_user is not present"))?;
- let channel_id = self.channel.id;
+ let channel_id = self.channel_id;
let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id));
let nonce = self.rng.gen();
self.insert_messages(
@@ -178,7 +181,7 @@ impl ChannelChat {
pub fn remove_message(&mut self, id: u64, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
let response = self.rpc.request(proto::RemoveChannelMessage {
- channel_id: self.channel.id,
+ channel_id: self.channel_id,
message_id: id,
});
cx.spawn(|this, mut cx| async move {
@@ -195,7 +198,7 @@ impl ChannelChat {
if !self.loaded_all_messages {
let rpc = self.rpc.clone();
let user_store = self.user_store.clone();
- let channel_id = self.channel.id;
+ let channel_id = self.channel_id;
if let Some(before_message_id) =
self.messages.first().and_then(|message| match message.id {
ChannelMessageId::Saved(id) => Some(id),
@@ -236,13 +239,13 @@ impl ChannelChat {
{
self.rpc
.send(proto::AckChannelMessage {
- channel_id: self.channel.id,
+ channel_id: self.channel_id,
message_id: latest_message_id,
})
.ok();
self.last_acknowledged_id = Some(latest_message_id);
self.channel_store.update(cx, |store, cx| {
- store.acknowledge_message_id(self.channel.id, latest_message_id, cx);
+ store.acknowledge_message_id(self.channel_id, latest_message_id, cx);
});
}
}
@@ -251,7 +254,7 @@ impl ChannelChat {
pub fn rejoin(&mut self, cx: &mut ModelContext<Self>) {
let user_store = self.user_store.clone();
let rpc = self.rpc.clone();
- let channel_id = self.channel.id;
+ let channel_id = self.channel_id;
cx.spawn(|this, mut cx| {
async move {
let response = rpc.request(proto::JoinChannelChat { channel_id }).await?;
@@ -348,7 +351,7 @@ impl ChannelChat {
this.update(&mut cx, |this, cx| {
this.insert_messages(SumTree::from_item(message, &()), cx);
cx.emit(ChannelChatEvent::NewMessage {
- channel_id: this.channel.id,
+ channel_id: this.channel_id,
message_id,
})
});
@@ -72,6 +72,10 @@ impl Channel {
slug.trim_matches(|c| c == '-').to_string()
}
+
+ pub fn can_edit_notes(&self) -> bool {
+ self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin
+ }
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
@@ -265,10 +269,11 @@ impl ChannelStore {
) -> Task<Result<ModelHandle<ChannelBuffer>>> {
let client = self.client.clone();
let user_store = self.user_store.clone();
+ let channel_store = cx.handle();
self.open_channel_resource(
channel_id,
|this| &mut this.opened_buffers,
- |channel, cx| ChannelBuffer::new(channel, client, user_store, cx),
+ |channel, cx| ChannelBuffer::new(channel, client, user_store, channel_store, cx),
cx,
)
}
@@ -778,7 +783,7 @@ impl ChannelStore {
let channel_buffer = buffer.read(cx);
let buffer = channel_buffer.buffer().read(cx);
buffer_versions.push(proto::ChannelBufferVersion {
- channel_id: channel_buffer.channel().id,
+ channel_id: channel_buffer.channel_id,
epoch: channel_buffer.epoch(),
version: language::proto::serialize_version(&buffer.version()),
});
@@ -805,13 +810,13 @@ impl ChannelStore {
};
channel_buffer.update(cx, |channel_buffer, cx| {
- let channel_id = channel_buffer.channel().id;
+ let channel_id = channel_buffer.channel_id;
if let Some(remote_buffer) = response
.buffers
.iter_mut()
.find(|buffer| buffer.channel_id == channel_id)
{
- let channel_id = channel_buffer.channel().id;
+ let channel_id = channel_buffer.channel_id;
let remote_version =
language::proto::deserialize_version(&remote_buffer.version);
@@ -934,11 +939,27 @@ impl ChannelStore {
if channels_changed {
if !payload.delete_channels.is_empty() {
- self.channel_index.delete_channels(&payload.delete_channels);
+ let mut channels_to_delete: Vec<u64> = Vec::new();
+ let mut channels_to_rehome: Vec<u64> = Vec::new();
+ for channel_id in payload.delete_channels {
+ if payload
+ .channels
+ .iter()
+ .any(|channel| channel.id == channel_id)
+ {
+ channels_to_rehome.push(channel_id)
+ } else {
+ channels_to_delete.push(channel_id)
+ }
+ }
+
+ self.channel_index.delete_channels(&channels_to_delete);
+ self.channel_index
+ .delete_paths_through_channels(&channels_to_rehome);
self.channel_participants
- .retain(|channel_id, _| !payload.delete_channels.contains(channel_id));
+ .retain(|channel_id, _| !channels_to_delete.contains(channel_id));
- for channel_id in &payload.delete_channels {
+ for channel_id in &channels_to_delete {
let channel_id = *channel_id;
if payload
.channels
@@ -959,7 +980,16 @@ impl ChannelStore {
let mut index = self.channel_index.bulk_insert();
for channel in payload.channels {
- index.insert(channel)
+ let id = channel.id;
+ let channel_changed = index.insert(channel);
+
+ if channel_changed {
+ if let Some(OpenedModelHandle::Open(buffer)) = self.opened_buffers.get(&id) {
+ if let Some(buffer) = buffer.upgrade(cx) {
+ buffer.update(cx, ChannelBuffer::channel_changed);
+ }
+ }
+ }
}
for unseen_buffer_change in payload.unseen_channel_buffer_changes {
@@ -24,12 +24,16 @@ impl ChannelIndex {
/// Delete the given channels from this index.
pub fn delete_channels(&mut self, channels: &[ChannelId]) {
+ dbg!("delete_channels", &channels);
self.channels_by_id
.retain(|channel_id, _| !channels.contains(channel_id));
- self.paths.retain(|path| {
- path.iter()
- .all(|channel_id| self.channels_by_id.contains_key(channel_id))
- });
+ self.delete_paths_through_channels(channels)
+ }
+
+ pub fn delete_paths_through_channels(&mut self, channels: &[ChannelId]) {
+ dbg!("rehome_channels", &channels);
+ self.paths
+ .retain(|path| !path.iter().any(|channel_id| channels.contains(channel_id)));
}
pub fn bulk_insert(&mut self) -> ChannelPathsInsertGuard {
@@ -121,9 +125,15 @@ impl<'a> ChannelPathsInsertGuard<'a> {
insert_new_message(&mut self.channels_by_id, channel_id, message_id)
}
- pub fn insert(&mut self, channel_proto: proto::Channel) {
+ pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
+ let mut ret = false;
if let Some(existing_channel) = self.channels_by_id.get_mut(&channel_proto.id) {
let existing_channel = Arc::make_mut(existing_channel);
+
+ ret = existing_channel.visibility != channel_proto.visibility()
+ || existing_channel.role != channel_proto.role()
+ || existing_channel.name != channel_proto.name;
+
existing_channel.visibility = channel_proto.visibility();
existing_channel.role = channel_proto.role();
existing_channel.name = channel_proto.name;
@@ -141,6 +151,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
);
self.insert_root(channel_proto.id);
}
+ ret
}
pub fn insert_edge(&mut self, channel_id: ChannelId, parent_id: ChannelId) {
@@ -412,7 +412,7 @@ async fn test_channel_buffer_disconnect(
channel_buffer_a.update(cx_a, |buffer, _| {
assert_eq!(
- buffer.channel().as_ref(),
+ buffer.channel(cx).unwrap().as_ref(),
&channel(channel_id, "the-channel", proto::ChannelRole::Admin)
);
assert!(!buffer.is_connected());
@@ -437,7 +437,7 @@ async fn test_channel_buffer_disconnect(
// Channel buffer observed the deletion
channel_buffer_b.update(cx_b, |buffer, _| {
assert_eq!(
- buffer.channel().as_ref(),
+ buffer.channel(cx).unwrap().as_ref(),
&channel(channel_id, "the-channel", proto::ChannelRole::Member)
);
assert!(!buffer.is_connected());
@@ -98,7 +98,8 @@ impl RandomizedTest for RandomChannelBufferTest {
30..=40 => {
if let Some(buffer) = channel_buffers.iter().choose(rng) {
- let channel_name = buffer.read_with(cx, |b, _| b.channel().name.clone());
+ let channel_name =
+ buffer.read_with(cx, |b, _| b.channel(cx).unwrap().name.clone());
break ChannelBufferOperation::LeaveChannelNotes { channel_name };
}
}
@@ -106,7 +107,7 @@ impl RandomizedTest for RandomChannelBufferTest {
_ => {
if let Some(buffer) = channel_buffers.iter().choose(rng) {
break buffer.read_with(cx, |b, _| {
- let channel_name = b.channel().name.clone();
+ let channel_name = b.channel(cx).unwrap().name.clone();
let edits = b
.buffer()
.read_with(cx, |buffer, _| buffer.get_random_edits(rng, 3));
@@ -153,7 +154,7 @@ impl RandomizedTest for RandomChannelBufferTest {
let buffer = cx.update(|cx| {
let mut left_buffer = Err(TestError::Inapplicable);
client.channel_buffers().retain(|buffer| {
- if buffer.read(cx).channel().name == channel_name {
+ if buffer.read(cx).channel(cx).unwrap().name == channel_name {
left_buffer = Ok(buffer.clone());
false
} else {
@@ -179,7 +180,9 @@ impl RandomizedTest for RandomChannelBufferTest {
client
.channel_buffers()
.iter()
- .find(|buffer| buffer.read(cx).channel().name == channel_name)
+ .find(|buffer| {
+ buffer.read(cx).channel(cx).unwrap().name == channel_name
+ })
.cloned()
})
.ok_or_else(|| TestError::Inapplicable)?;
@@ -250,7 +253,7 @@ impl RandomizedTest for RandomChannelBufferTest {
if let Some(channel_buffer) = client
.channel_buffers()
.iter()
- .find(|b| b.read(cx).channel().id == channel_id.to_proto())
+ .find(|b| b.read(cx).channel_id == channel_id.to_proto())
{
let channel_buffer = channel_buffer.read(cx);
@@ -58,6 +58,7 @@ postage.workspace = true
serde.workspace = true
serde_derive.workspace = true
time.workspace = true
+smallvec.workspace = true
[dev-dependencies]
call = { path = "../call", features = ["test-support"] }
@@ -15,13 +15,14 @@ use gpui::{
ViewContext, ViewHandle,
};
use project::Project;
+use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
sync::Arc,
};
use util::ResultExt;
use workspace::{
- item::{FollowableItem, Item, ItemHandle},
+ item::{FollowableItem, Item, ItemEvent, ItemHandle},
register_followable_item,
searchable::SearchableItemHandle,
ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
@@ -140,6 +141,12 @@ impl ChannelView {
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
channel_buffer.clone(),
)));
+ editor.set_read_only(
+ !channel_buffer
+ .read(cx)
+ .channel(cx)
+ .is_some_and(|c| c.can_edit_notes()),
+ );
editor
});
let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
@@ -157,8 +164,8 @@ impl ChannelView {
}
}
- pub fn channel(&self, cx: &AppContext) -> Arc<Channel> {
- self.channel_buffer.read(cx).channel()
+ pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
+ self.channel_buffer.read(cx).channel(cx)
}
fn handle_channel_buffer_event(
@@ -172,6 +179,13 @@ impl ChannelView {
editor.set_read_only(true);
cx.notify();
}),
+ ChannelBufferEvent::ChannelChanged => {
+ self.editor.update(cx, |editor, cx| {
+ editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
+ cx.emit(editor::Event::TitleChanged);
+ cx.notify()
+ });
+ }
ChannelBufferEvent::BufferEdited => {
if cx.is_self_focused() || self.editor.is_focused(cx) {
self.acknowledge_buffer_version(cx);
@@ -179,7 +193,7 @@ impl ChannelView {
self.channel_store.update(cx, |store, cx| {
let channel_buffer = self.channel_buffer.read(cx);
store.notes_changed(
- channel_buffer.channel().id,
+ channel_buffer.channel_id,
channel_buffer.epoch(),
&channel_buffer.buffer().read(cx).version(),
cx,
@@ -187,7 +201,7 @@ impl ChannelView {
});
}
}
- _ => {}
+ ChannelBufferEvent::CollaboratorsChanged => {}
}
}
@@ -195,7 +209,7 @@ impl ChannelView {
self.channel_store.update(cx, |store, cx| {
let channel_buffer = self.channel_buffer.read(cx);
store.acknowledge_notes_version(
- channel_buffer.channel().id,
+ channel_buffer.channel_id,
channel_buffer.epoch(),
&channel_buffer.buffer().read(cx).version(),
cx,
@@ -250,11 +264,17 @@ impl Item for ChannelView {
style: &theme::Tab,
cx: &gpui::AppContext,
) -> AnyElement<V> {
- let channel_name = &self.channel_buffer.read(cx).channel().name;
- let label = if self.channel_buffer.read(cx).is_connected() {
- format!("#{}", channel_name)
+ let label = if let Some(channel) = self.channel(cx) {
+ match (
+ channel.can_edit_notes(),
+ self.channel_buffer.read(cx).is_connected(),
+ ) {
+ (true, true) => format!("#{}", channel.name),
+ (false, true) => format!("#{} (read-only)", channel.name),
+ (_, false) => format!("#{} (disconnected)", channel.name),
+ }
} else {
- format!("#{} (disconnected)", channel_name)
+ format!("channel notes (disconnected)")
};
Label::new(label, style.label.to_owned()).into_any()
}
@@ -298,6 +318,10 @@ impl Item for ChannelView {
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
self.editor.read(cx).pixel_position_of_cursor(cx)
}
+
+ fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
+ editor::Editor::to_item_events(event)
+ }
}
impl FollowableItem for ChannelView {
@@ -313,7 +337,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,
editor: if let Some(proto::view::Variant::Editor(proto)) =
self.editor.read(cx).to_state_proto(cx)
{
@@ -267,11 +267,15 @@ impl ChatPanel {
fn set_active_chat(&mut self, chat: ModelHandle<ChannelChat>, cx: &mut ViewContext<Self>) {
if self.active_chat.as_ref().map(|e| &e.0) != Some(&chat) {
- let id = chat.read(cx).channel().id;
+ let id = chat.read(cx).channel_id;
{
let chat = chat.read(cx);
self.message_list.reset(chat.message_count());
- let placeholder = format!("Message #{}", chat.channel().name);
+ let placeholder = if let Some(channel) = chat.channel(cx) {
+ format!("Message #{}", channel.name)
+ } else {
+ "Message Channel".to_string()
+ };
self.input_editor.update(cx, move |editor, cx| {
editor.set_placeholder_text(placeholder, cx);
});
@@ -360,7 +364,7 @@ impl ChatPanel {
let is_admin = self
.channel_store
.read(cx)
- .is_channel_admin(active_chat.channel().id);
+ .is_channel_admin(active_chat.channel_id);
let last_message = active_chat.message(ix.saturating_sub(1));
let this_message = active_chat.message(ix);
let is_continuation = last_message.id != this_message.id
@@ -645,7 +649,7 @@ impl ChatPanel {
cx: &mut ViewContext<ChatPanel>,
) -> Task<Result<()>> {
if let Some((chat, _)) = &self.active_chat {
- if chat.read(cx).channel().id == selected_channel_id {
+ if chat.read(cx).channel_id == selected_channel_id {
return Task::ready(Ok(()));
}
}
@@ -664,7 +668,7 @@ impl ChatPanel {
fn open_notes(&mut self, _: &OpenChannelNotes, cx: &mut ViewContext<Self>) {
if let Some((chat, _)) = &self.active_chat {
- let channel_id = chat.read(cx).channel().id;
+ let channel_id = chat.read(cx).channel_id;
if let Some(workspace) = self.workspace.upgrade(cx) {
ChannelView::open(channel_id, workspace, cx).detach();
}
@@ -673,7 +677,7 @@ impl ChatPanel {
fn join_call(&mut self, _: &JoinCall, cx: &mut ViewContext<Self>) {
if let Some((chat, _)) = &self.active_chat {
- let channel_id = chat.read(cx).channel().id;
+ let channel_id = chat.read(cx).channel_id;
ActiveCall::global(cx)
.update(cx, |call, cx| call.join_channel(channel_id, cx))
.detach_and_log_err(cx);