Acknowledge channel notes and chat changes when views are active
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
af09861f5c
commit
61e0289014
19 changed files with 478 additions and 209 deletions
|
@ -2,14 +2,17 @@ use crate::Channel;
|
|||
use anyhow::Result;
|
||||
use client::{Client, Collaborator, UserStore};
|
||||
use collections::HashMap;
|
||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle};
|
||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
||||
use language::proto::serialize_version;
|
||||
use rpc::{
|
||||
proto::{self, PeerId},
|
||||
TypedEnvelope,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use util::ResultExt;
|
||||
|
||||
const ACKNOWLEDGE_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(250);
|
||||
|
||||
pub(crate) fn init(client: &Arc<Client>) {
|
||||
client.add_model_message_handler(ChannelBuffer::handle_update_channel_buffer);
|
||||
client.add_model_message_handler(ChannelBuffer::handle_update_channel_buffer_collaborators);
|
||||
|
@ -24,11 +27,13 @@ pub struct ChannelBuffer {
|
|||
buffer_epoch: u64,
|
||||
client: Arc<Client>,
|
||||
subscription: Option<client::Subscription>,
|
||||
acknowledge_task: Option<Task<Result<()>>>,
|
||||
}
|
||||
|
||||
pub enum ChannelBufferEvent {
|
||||
CollaboratorsChanged,
|
||||
Disconnected,
|
||||
BufferEdited,
|
||||
}
|
||||
|
||||
impl Entity for ChannelBuffer {
|
||||
|
@ -36,6 +41,9 @@ impl Entity for ChannelBuffer {
|
|||
|
||||
fn release(&mut self, _: &mut AppContext) {
|
||||
if self.connected {
|
||||
if let Some(task) = self.acknowledge_task.take() {
|
||||
task.detach();
|
||||
}
|
||||
self.client
|
||||
.send(proto::LeaveChannelBuffer {
|
||||
channel_id: self.channel.id,
|
||||
|
@ -81,6 +89,7 @@ impl ChannelBuffer {
|
|||
client,
|
||||
connected: true,
|
||||
collaborators: Default::default(),
|
||||
acknowledge_task: None,
|
||||
channel,
|
||||
subscription: Some(subscription.set_model(&cx.handle(), &mut cx.to_async())),
|
||||
user_store,
|
||||
|
@ -159,19 +168,45 @@ impl ChannelBuffer {
|
|||
&mut self,
|
||||
_: ModelHandle<language::Buffer>,
|
||||
event: &language::Event,
|
||||
_: &mut ModelContext<Self>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
if let language::Event::Operation(operation) = event {
|
||||
let operation = language::proto::serialize_operation(operation);
|
||||
self.client
|
||||
.send(proto::UpdateChannelBuffer {
|
||||
channel_id: self.channel.id,
|
||||
operations: vec![operation],
|
||||
})
|
||||
.log_err();
|
||||
match event {
|
||||
language::Event::Operation(operation) => {
|
||||
let operation = language::proto::serialize_operation(operation);
|
||||
self.client
|
||||
.send(proto::UpdateChannelBuffer {
|
||||
channel_id: self.channel.id,
|
||||
operations: vec![operation],
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
language::Event::Edited => {
|
||||
cx.emit(ChannelBufferEvent::BufferEdited);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acknowledge_buffer_version(&mut self, cx: &mut ModelContext<'_, ChannelBuffer>) {
|
||||
let buffer = self.buffer.read(cx);
|
||||
let version = buffer.version();
|
||||
let buffer_id = buffer.remote_id();
|
||||
let client = self.client.clone();
|
||||
let epoch = self.epoch();
|
||||
|
||||
self.acknowledge_task = Some(cx.spawn_weak(|_, cx| async move {
|
||||
cx.background().timer(ACKNOWLEDGE_DEBOUNCE_INTERVAL).await;
|
||||
client
|
||||
.send(proto::AckBufferOperation {
|
||||
buffer_id,
|
||||
epoch,
|
||||
version: serialize_version(&version),
|
||||
})
|
||||
.ok();
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn epoch(&self) -> u64 {
|
||||
self.buffer_epoch
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Channel;
|
||||
use crate::{Channel, ChannelStore};
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{
|
||||
proto,
|
||||
|
@ -16,7 +16,9 @@ use util::{post_inc, ResultExt as _, TryFutureExt};
|
|||
pub struct ChannelChat {
|
||||
channel: Arc<Channel>,
|
||||
messages: SumTree<ChannelMessage>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
loaded_all_messages: bool,
|
||||
last_acknowledged_id: Option<u64>,
|
||||
next_pending_message_id: usize,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
rpc: Arc<Client>,
|
||||
|
@ -77,6 +79,7 @@ impl Entity for ChannelChat {
|
|||
impl ChannelChat {
|
||||
pub async fn new(
|
||||
channel: Arc<Channel>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
client: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
|
@ -94,11 +97,13 @@ impl ChannelChat {
|
|||
let mut this = Self {
|
||||
channel,
|
||||
user_store,
|
||||
channel_store,
|
||||
rpc: client,
|
||||
outgoing_messages_lock: Default::default(),
|
||||
messages: Default::default(),
|
||||
loaded_all_messages,
|
||||
next_pending_message_id: 0,
|
||||
last_acknowledged_id: None,
|
||||
rng: StdRng::from_entropy(),
|
||||
_subscription: subscription.set_model(&cx.handle(), &mut cx.to_async()),
|
||||
};
|
||||
|
@ -219,6 +224,26 @@ impl ChannelChat {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn acknowledge_last_message(&mut self, cx: &mut ModelContext<Self>) {
|
||||
if let ChannelMessageId::Saved(latest_message_id) = self.messages.summary().max_id {
|
||||
if self
|
||||
.last_acknowledged_id
|
||||
.map_or(true, |acknowledged_id| acknowledged_id < latest_message_id)
|
||||
{
|
||||
self.rpc
|
||||
.send(proto::AckChannelMessage {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rejoin(&mut self, cx: &mut ModelContext<Self>) {
|
||||
let user_store = self.user_store.clone();
|
||||
let rpc = self.rpc.clone();
|
||||
|
|
|
@ -43,8 +43,8 @@ pub type ChannelData = (Channel, ChannelPath);
|
|||
pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub has_note_changed: bool,
|
||||
pub has_new_messages: bool,
|
||||
pub unseen_note_version: Option<(u64, clock::Global)>,
|
||||
pub unseen_message_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
|
||||
|
@ -201,34 +201,60 @@ impl ChannelStore {
|
|||
) -> Task<Result<ModelHandle<ChannelBuffer>>> {
|
||||
let client = self.client.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let open_channel_buffer = self.open_channel_resource(
|
||||
self.open_channel_resource(
|
||||
channel_id,
|
||||
|this| &mut this.opened_buffers,
|
||||
|channel, cx| ChannelBuffer::new(channel, client, user_store, cx),
|
||||
cx,
|
||||
);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let buffer = open_channel_buffer.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.channel_index.clear_note_changed(channel_id);
|
||||
cx.notify();
|
||||
});
|
||||
Ok(buffer)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_channel_buffer_changed(&self, channel_id: ChannelId) -> Option<bool> {
|
||||
self.channel_index
|
||||
.by_id()
|
||||
.get(&channel_id)
|
||||
.map(|channel| channel.has_note_changed)
|
||||
.map(|channel| channel.unseen_note_version.is_some())
|
||||
}
|
||||
|
||||
pub fn has_new_messages(&self, channel_id: ChannelId) -> Option<bool> {
|
||||
self.channel_index
|
||||
.by_id()
|
||||
.get(&channel_id)
|
||||
.map(|channel| channel.has_new_messages)
|
||||
.map(|channel| channel.unseen_message_id.is_some())
|
||||
}
|
||||
|
||||
pub fn notes_changed(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
epoch: u64,
|
||||
version: &clock::Global,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.channel_index.note_changed(channel_id, epoch, version);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn acknowledge_message_id(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
message_id: u64,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.channel_index
|
||||
.acknowledge_message_id(channel_id, message_id);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn acknowledge_notes_version(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
epoch: u64,
|
||||
version: &clock::Global,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.channel_index
|
||||
.acknowledge_note_version(channel_id, epoch, version);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn open_channel_chat(
|
||||
|
@ -238,20 +264,13 @@ impl ChannelStore {
|
|||
) -> Task<Result<ModelHandle<ChannelChat>>> {
|
||||
let client = self.client.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let open_channel_chat = self.open_channel_resource(
|
||||
let this = cx.handle();
|
||||
self.open_channel_resource(
|
||||
channel_id,
|
||||
|this| &mut this.opened_chats,
|
||||
|channel, cx| ChannelChat::new(channel, user_store, client, cx),
|
||||
|channel, cx| ChannelChat::new(channel, this, user_store, client, cx),
|
||||
cx,
|
||||
);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let chat = open_channel_chat.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.channel_index.clear_message_changed(channel_id);
|
||||
cx.notify();
|
||||
});
|
||||
Ok(chat)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/// Asynchronously open a given resource associated with a channel.
|
||||
|
@ -811,8 +830,8 @@ impl ChannelStore {
|
|||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
has_note_changed: false,
|
||||
has_new_messages: false,
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
@ -822,8 +841,8 @@ impl ChannelStore {
|
|||
|| !payload.delete_channels.is_empty()
|
||||
|| !payload.insert_edge.is_empty()
|
||||
|| !payload.delete_edge.is_empty()
|
||||
|| !payload.notes_changed.is_empty()
|
||||
|| !payload.new_messages.is_empty();
|
||||
|| !payload.unseen_channel_messages.is_empty()
|
||||
|| !payload.unseen_channel_buffer_changes.is_empty();
|
||||
|
||||
if channels_changed {
|
||||
if !payload.delete_channels.is_empty() {
|
||||
|
@ -850,12 +869,20 @@ impl ChannelStore {
|
|||
index.insert(channel)
|
||||
}
|
||||
|
||||
for id_changed in payload.notes_changed {
|
||||
index.note_changed(id_changed);
|
||||
for unseen_buffer_change in payload.unseen_channel_buffer_changes {
|
||||
let version = language::proto::deserialize_version(&unseen_buffer_change.version);
|
||||
index.note_changed(
|
||||
unseen_buffer_change.channel_id,
|
||||
unseen_buffer_change.epoch,
|
||||
&version,
|
||||
);
|
||||
}
|
||||
|
||||
for id_changed in payload.new_messages {
|
||||
index.new_messages(id_changed);
|
||||
for unseen_channel_message in payload.unseen_channel_messages {
|
||||
index.new_messages(
|
||||
unseen_channel_message.channel_id,
|
||||
unseen_channel_message.message_id,
|
||||
);
|
||||
}
|
||||
|
||||
for edge in payload.insert_edge {
|
||||
|
|
|
@ -39,17 +39,38 @@ impl ChannelIndex {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clear_note_changed(&mut self, channel_id: ChannelId) {
|
||||
pub fn acknowledge_note_version(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
epoch: u64,
|
||||
version: &clock::Global,
|
||||
) {
|
||||
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
|
||||
Arc::make_mut(channel).has_note_changed = false;
|
||||
let channel = Arc::make_mut(channel);
|
||||
if let Some((unseen_epoch, unseen_version)) = &channel.unseen_note_version {
|
||||
if epoch > *unseen_epoch
|
||||
|| epoch == *unseen_epoch && version.observed_all(unseen_version)
|
||||
{
|
||||
channel.unseen_note_version = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_message_changed(&mut self, channel_id: ChannelId) {
|
||||
pub fn acknowledge_message_id(&mut self, channel_id: ChannelId, message_id: u64) {
|
||||
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
|
||||
Arc::make_mut(channel).has_new_messages = false;
|
||||
let channel = Arc::make_mut(channel);
|
||||
if let Some(unseen_message_id) = channel.unseen_message_id {
|
||||
if message_id >= unseen_message_id {
|
||||
channel.unseen_message_id = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn note_changed(&mut self, channel_id: ChannelId, epoch: u64, version: &clock::Global) {
|
||||
insert_note_changed(&mut self.channels_by_id, channel_id, epoch, version);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ChannelIndex {
|
||||
|
@ -88,15 +109,14 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn note_changed(&mut self, channel_id: ChannelId) {
|
||||
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
|
||||
Arc::make_mut(channel).has_note_changed = true;
|
||||
}
|
||||
pub fn note_changed(&mut self, channel_id: ChannelId, epoch: u64, version: &clock::Global) {
|
||||
insert_note_changed(&mut self.channels_by_id, channel_id, epoch, &version);
|
||||
}
|
||||
|
||||
pub fn new_messages(&mut self, channel_id: ChannelId) {
|
||||
pub fn new_messages(&mut self, channel_id: ChannelId, message_id: u64) {
|
||||
if let Some(channel) = self.channels_by_id.get_mut(&channel_id) {
|
||||
Arc::make_mut(channel).has_new_messages = true;
|
||||
let unseen_message_id = Arc::make_mut(channel).unseen_message_id.get_or_insert(0);
|
||||
*unseen_message_id = message_id.max(*unseen_message_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,8 +129,8 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
Arc::new(Channel {
|
||||
id: channel_proto.id,
|
||||
name: channel_proto.name,
|
||||
has_note_changed: false,
|
||||
has_new_messages: false,
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
}),
|
||||
);
|
||||
self.insert_root(channel_proto.id);
|
||||
|
@ -186,3 +206,21 @@ fn channel_path_sorting_key<'a>(
|
|||
path.iter()
|
||||
.map(|id| Some(channels_by_id.get(id)?.name.as_str()))
|
||||
}
|
||||
|
||||
fn insert_note_changed(
|
||||
channels_by_id: &mut BTreeMap<ChannelId, Arc<Channel>>,
|
||||
channel_id: u64,
|
||||
epoch: u64,
|
||||
version: &clock::Global,
|
||||
) {
|
||||
if let Some(channel) = channels_by_id.get_mut(&channel_id) {
|
||||
let unseen_version = Arc::make_mut(channel)
|
||||
.unseen_note_version
|
||||
.get_or_insert((0, clock::Global::new()));
|
||||
if epoch > unseen_version.0 {
|
||||
*unseen_version = (epoch, version.clone());
|
||||
} else {
|
||||
unseen_version.1.join(&version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue