Add read-only channel notes support

Fix some bugs where ChannelNotes and ChannelChat had old cached channel
instances
This commit is contained in:
Conrad Irwin 2023-10-19 13:03:44 -06:00
parent 3853009d92
commit 0eff7c6ca9
12 changed files with 186 additions and 219 deletions

1
Cargo.lock generated
View file

@ -1569,6 +1569,7 @@ dependencies = [
"serde",
"serde_derive",
"settings",
"smallvec",
"theme",
"theme_selector",
"time",

View file

@ -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"]
}
}
]

View file

@ -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>>,

View file

@ -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
}

View file

@ -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,
})
});

View file

@ -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);
self.channel_participants
.retain(|channel_id, _| !payload.delete_channels.contains(channel_id));
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)
}
}
for channel_id in &payload.delete_channels {
self.channel_index.delete_channels(&channels_to_delete);
self.channel_index
.delete_paths_through_channels(&channels_to_rehome);
self.channel_participants
.retain(|channel_id, _| !channels_to_delete.contains(channel_id));
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 {

View file

@ -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) {

View file

@ -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());

View file

@ -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);

View file

@ -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"] }

View file

@ -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)
{

View file

@ -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);