Fetch and wait for channels when opening channel notes via URL (#33291)
Release Notes: * Collaboration: Now fetches and waits for channels when opening channel notes via URL.
This commit is contained in:
parent
24c94d474e
commit
786e724684
3 changed files with 99 additions and 49 deletions
|
@ -24,6 +24,7 @@ futures.workspace = true
|
|||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
postage.workspace = true
|
||||
rand.workspace = true
|
||||
release_channel.workspace = true
|
||||
rpc.workspace = true
|
||||
|
|
|
@ -4,13 +4,14 @@ use crate::{ChannelMessage, channel_buffer::ChannelBuffer, channel_chat::Channel
|
|||
use anyhow::{Context as _, Result, anyhow};
|
||||
use channel_index::ChannelIndex;
|
||||
use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore};
|
||||
use collections::{HashMap, HashSet, hash_map};
|
||||
use collections::{HashMap, HashSet};
|
||||
use futures::{Future, FutureExt, StreamExt, channel::mpsc, future::Shared};
|
||||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
|
||||
WeakEntity,
|
||||
};
|
||||
use language::Capability;
|
||||
use postage::{sink::Sink, watch};
|
||||
use rpc::{
|
||||
TypedEnvelope,
|
||||
proto::{self, ChannelRole, ChannelVisibility},
|
||||
|
@ -43,6 +44,7 @@ pub struct ChannelStore {
|
|||
opened_chats: HashMap<ChannelId, OpenEntityHandle<ChannelChat>>,
|
||||
client: Arc<Client>,
|
||||
did_subscribe: bool,
|
||||
channels_loaded: (watch::Sender<bool>, watch::Receiver<bool>),
|
||||
user_store: Entity<UserStore>,
|
||||
_rpc_subscriptions: [Subscription; 2],
|
||||
_watch_connection_status: Task<Option<()>>,
|
||||
|
@ -219,6 +221,7 @@ impl ChannelStore {
|
|||
}),
|
||||
channel_states: Default::default(),
|
||||
did_subscribe: false,
|
||||
channels_loaded: watch::channel_with(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +237,48 @@ impl ChannelStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn wait_for_channels(
|
||||
&mut self,
|
||||
timeout: Duration,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let mut channels_loaded_rx = self.channels_loaded.1.clone();
|
||||
if *channels_loaded_rx.borrow() {
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
let mut status_receiver = self.client.status();
|
||||
if status_receiver.borrow().is_connected() {
|
||||
self.initialize();
|
||||
}
|
||||
|
||||
let mut timer = cx.background_executor().timer(timeout).fuse();
|
||||
cx.spawn(async move |this, cx| {
|
||||
loop {
|
||||
futures::select_biased! {
|
||||
channels_loaded = channels_loaded_rx.next().fuse() => {
|
||||
if let Some(true) = channels_loaded {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
status = status_receiver.next().fuse() => {
|
||||
if let Some(status) = status {
|
||||
if status.is_connected() {
|
||||
this.update(cx, |this, _cx| {
|
||||
this.initialize();
|
||||
}).ok();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ = timer => {
|
||||
return Err(anyhow!("{:?} elapsed without receiving channels", timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn client(&self) -> Arc<Client> {
|
||||
self.client.clone()
|
||||
}
|
||||
|
@ -309,6 +354,7 @@ impl ChannelStore {
|
|||
let channel_store = cx.entity();
|
||||
self.open_channel_resource(
|
||||
channel_id,
|
||||
"notes",
|
||||
|this| &mut this.opened_buffers,
|
||||
async move |channel, cx| {
|
||||
ChannelBuffer::new(channel, client, user_store, channel_store, cx).await
|
||||
|
@ -439,6 +485,7 @@ impl ChannelStore {
|
|||
let this = cx.entity();
|
||||
self.open_channel_resource(
|
||||
channel_id,
|
||||
"chat",
|
||||
|this| &mut this.opened_chats,
|
||||
async move |channel, cx| ChannelChat::new(channel, this, user_store, client, cx).await,
|
||||
cx,
|
||||
|
@ -453,6 +500,7 @@ impl ChannelStore {
|
|||
fn open_channel_resource<T, F>(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
resource_name: &'static str,
|
||||
get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenEntityHandle<T>>,
|
||||
load: F,
|
||||
cx: &mut Context<Self>,
|
||||
|
@ -462,58 +510,56 @@ impl ChannelStore {
|
|||
T: 'static,
|
||||
{
|
||||
let task = loop {
|
||||
match get_map(self).entry(channel_id) {
|
||||
hash_map::Entry::Occupied(e) => match e.get() {
|
||||
OpenEntityHandle::Open(entity) => {
|
||||
if let Some(entity) = entity.upgrade() {
|
||||
break Task::ready(Ok(entity)).shared();
|
||||
} else {
|
||||
get_map(self).remove(&channel_id);
|
||||
continue;
|
||||
}
|
||||
match get_map(self).get(&channel_id) {
|
||||
Some(OpenEntityHandle::Open(entity)) => {
|
||||
if let Some(entity) = entity.upgrade() {
|
||||
break Task::ready(Ok(entity)).shared();
|
||||
} else {
|
||||
get_map(self).remove(&channel_id);
|
||||
continue;
|
||||
}
|
||||
OpenEntityHandle::Loading(task) => {
|
||||
break task.clone();
|
||||
}
|
||||
},
|
||||
hash_map::Entry::Vacant(e) => {
|
||||
let task = cx
|
||||
.spawn(async move |this, cx| {
|
||||
let channel = this.read_with(cx, |this, _| {
|
||||
this.channel_for_id(channel_id).cloned().ok_or_else(|| {
|
||||
Arc::new(anyhow!("no channel for id: {channel_id}"))
|
||||
})
|
||||
})??;
|
||||
|
||||
load(channel, cx).await.map_err(Arc::new)
|
||||
})
|
||||
.shared();
|
||||
|
||||
e.insert(OpenEntityHandle::Loading(task.clone()));
|
||||
cx.spawn({
|
||||
let task = task.clone();
|
||||
async move |this, cx| {
|
||||
let result = task.await;
|
||||
this.update(cx, |this, _| match result {
|
||||
Ok(model) => {
|
||||
get_map(this).insert(
|
||||
channel_id,
|
||||
OpenEntityHandle::Open(model.downgrade()),
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
get_map(this).remove(&channel_id);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
break task;
|
||||
}
|
||||
Some(OpenEntityHandle::Loading(task)) => break task.clone(),
|
||||
None => {}
|
||||
}
|
||||
|
||||
let channels_ready = self.wait_for_channels(Duration::from_secs(10), cx);
|
||||
let task = cx
|
||||
.spawn(async move |this, cx| {
|
||||
channels_ready.await?;
|
||||
let channel = this.read_with(cx, |this, _| {
|
||||
this.channel_for_id(channel_id)
|
||||
.cloned()
|
||||
.ok_or_else(|| Arc::new(anyhow!("no channel for id: {channel_id}")))
|
||||
})??;
|
||||
|
||||
load(channel, cx).await.map_err(Arc::new)
|
||||
})
|
||||
.shared();
|
||||
|
||||
get_map(self).insert(channel_id, OpenEntityHandle::Loading(task.clone()));
|
||||
let task = cx.spawn({
|
||||
async move |this, cx| {
|
||||
let result = task.await;
|
||||
this.update(cx, |this, _| match &result {
|
||||
Ok(model) => {
|
||||
get_map(this)
|
||||
.insert(channel_id, OpenEntityHandle::Open(model.downgrade()));
|
||||
}
|
||||
Err(_) => {
|
||||
get_map(this).remove(&channel_id);
|
||||
}
|
||||
})?;
|
||||
result
|
||||
}
|
||||
});
|
||||
break task.shared();
|
||||
};
|
||||
cx.background_spawn(async move { task.await.map_err(|error| anyhow!("{error}")) })
|
||||
cx.background_spawn(async move {
|
||||
task.await.map_err(|error| {
|
||||
anyhow!("{error}").context(format!("failed to open channel {resource_name}"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_channel_admin(&self, channel_id: ChannelId) -> bool {
|
||||
|
@ -1147,6 +1193,8 @@ impl ChannelStore {
|
|||
.or_default()
|
||||
.update_latest_message_id(latest_channel_message.message_id);
|
||||
}
|
||||
|
||||
self.channels_loaded.0.try_send(true).log_err();
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue