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
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2697,6 +2697,7 @@ dependencies = [
|
||||||
"http_client",
|
"http_client",
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
|
"postage",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"release_channel",
|
"release_channel",
|
||||||
"rpc",
|
"rpc",
|
||||||
|
|
|
@ -24,6 +24,7 @@ futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
postage.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
release_channel.workspace = true
|
release_channel.workspace = true
|
||||||
rpc.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 anyhow::{Context as _, Result, anyhow};
|
||||||
use channel_index::ChannelIndex;
|
use channel_index::ChannelIndex;
|
||||||
use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore};
|
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 futures::{Future, FutureExt, StreamExt, channel::mpsc, future::Shared};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
|
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
|
||||||
WeakEntity,
|
WeakEntity,
|
||||||
};
|
};
|
||||||
use language::Capability;
|
use language::Capability;
|
||||||
|
use postage::{sink::Sink, watch};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
TypedEnvelope,
|
TypedEnvelope,
|
||||||
proto::{self, ChannelRole, ChannelVisibility},
|
proto::{self, ChannelRole, ChannelVisibility},
|
||||||
|
@ -43,6 +44,7 @@ pub struct ChannelStore {
|
||||||
opened_chats: HashMap<ChannelId, OpenEntityHandle<ChannelChat>>,
|
opened_chats: HashMap<ChannelId, OpenEntityHandle<ChannelChat>>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
did_subscribe: bool,
|
did_subscribe: bool,
|
||||||
|
channels_loaded: (watch::Sender<bool>, watch::Receiver<bool>),
|
||||||
user_store: Entity<UserStore>,
|
user_store: Entity<UserStore>,
|
||||||
_rpc_subscriptions: [Subscription; 2],
|
_rpc_subscriptions: [Subscription; 2],
|
||||||
_watch_connection_status: Task<Option<()>>,
|
_watch_connection_status: Task<Option<()>>,
|
||||||
|
@ -219,6 +221,7 @@ impl ChannelStore {
|
||||||
}),
|
}),
|
||||||
channel_states: Default::default(),
|
channel_states: Default::default(),
|
||||||
did_subscribe: false,
|
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> {
|
pub fn client(&self) -> Arc<Client> {
|
||||||
self.client.clone()
|
self.client.clone()
|
||||||
}
|
}
|
||||||
|
@ -309,6 +354,7 @@ impl ChannelStore {
|
||||||
let channel_store = cx.entity();
|
let channel_store = cx.entity();
|
||||||
self.open_channel_resource(
|
self.open_channel_resource(
|
||||||
channel_id,
|
channel_id,
|
||||||
|
"notes",
|
||||||
|this| &mut this.opened_buffers,
|
|this| &mut this.opened_buffers,
|
||||||
async move |channel, cx| {
|
async move |channel, cx| {
|
||||||
ChannelBuffer::new(channel, client, user_store, channel_store, cx).await
|
ChannelBuffer::new(channel, client, user_store, channel_store, cx).await
|
||||||
|
@ -439,6 +485,7 @@ impl ChannelStore {
|
||||||
let this = cx.entity();
|
let this = cx.entity();
|
||||||
self.open_channel_resource(
|
self.open_channel_resource(
|
||||||
channel_id,
|
channel_id,
|
||||||
|
"chat",
|
||||||
|this| &mut this.opened_chats,
|
|this| &mut this.opened_chats,
|
||||||
async move |channel, cx| ChannelChat::new(channel, this, user_store, client, cx).await,
|
async move |channel, cx| ChannelChat::new(channel, this, user_store, client, cx).await,
|
||||||
cx,
|
cx,
|
||||||
|
@ -453,6 +500,7 @@ impl ChannelStore {
|
||||||
fn open_channel_resource<T, F>(
|
fn open_channel_resource<T, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
channel_id: ChannelId,
|
channel_id: ChannelId,
|
||||||
|
resource_name: &'static str,
|
||||||
get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenEntityHandle<T>>,
|
get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenEntityHandle<T>>,
|
||||||
load: F,
|
load: F,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
|
@ -462,58 +510,56 @@ impl ChannelStore {
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
let task = loop {
|
let task = loop {
|
||||||
match get_map(self).entry(channel_id) {
|
match get_map(self).get(&channel_id) {
|
||||||
hash_map::Entry::Occupied(e) => match e.get() {
|
Some(OpenEntityHandle::Open(entity)) => {
|
||||||
OpenEntityHandle::Open(entity) => {
|
if let Some(entity) = entity.upgrade() {
|
||||||
if let Some(entity) = entity.upgrade() {
|
break Task::ready(Ok(entity)).shared();
|
||||||
break Task::ready(Ok(entity)).shared();
|
} else {
|
||||||
} else {
|
get_map(self).remove(&channel_id);
|
||||||
get_map(self).remove(&channel_id);
|
continue;
|
||||||
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 {
|
pub fn is_channel_admin(&self, channel_id: ChannelId) -> bool {
|
||||||
|
@ -1147,6 +1193,8 @@ impl ChannelStore {
|
||||||
.or_default()
|
.or_default()
|
||||||
.update_latest_message_id(latest_channel_message.message_id);
|
.update_latest_message_id(latest_channel_message.message_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.channels_loaded.0.try_send(true).log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue