mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-13 05:42:59 +00:00
Turn UserStore
into a model
This commit is contained in:
parent
6f2c8ffb00
commit
32111092bd
8 changed files with 150 additions and 131 deletions
|
@ -1289,7 +1289,7 @@ mod tests {
|
||||||
github, AppState, Config,
|
github, AppState, Config,
|
||||||
};
|
};
|
||||||
use async_std::{sync::RwLockReadGuard, task};
|
use async_std::{sync::RwLockReadGuard, task};
|
||||||
use gpui::TestAppContext;
|
use gpui::{ModelHandle, TestAppContext};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::{mpsc, watch};
|
use postage::{mpsc, watch};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -1780,24 +1780,24 @@ mod tests {
|
||||||
// Create an org that includes these 2 users.
|
// Create an org that includes these 2 users.
|
||||||
let db = &server.app_state.db;
|
let db = &server.app_state.db;
|
||||||
let org_id = db.create_org("Test Org", "test-org").await.unwrap();
|
let org_id = db.create_org("Test Org", "test-org").await.unwrap();
|
||||||
db.add_org_member(org_id, current_user_id(&user_store_a), false)
|
db.add_org_member(org_id, current_user_id(&user_store_a, &cx_a), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.add_org_member(org_id, current_user_id(&user_store_b), false)
|
db.add_org_member(org_id, current_user_id(&user_store_b, &cx_b), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Create a channel that includes all the users.
|
// Create a channel that includes all the users.
|
||||||
let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
|
let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
|
||||||
db.add_channel_member(channel_id, current_user_id(&user_store_a), false)
|
db.add_channel_member(channel_id, current_user_id(&user_store_a, &cx_a), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.add_channel_member(channel_id, current_user_id(&user_store_b), false)
|
db.add_channel_member(channel_id, current_user_id(&user_store_b, &cx_b), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.create_channel_message(
|
db.create_channel_message(
|
||||||
channel_id,
|
channel_id,
|
||||||
current_user_id(&user_store_b),
|
current_user_id(&user_store_b, &cx_b),
|
||||||
"hello A, it's B.",
|
"hello A, it's B.",
|
||||||
OffsetDateTime::now_utc(),
|
OffsetDateTime::now_utc(),
|
||||||
1,
|
1,
|
||||||
|
@ -1912,10 +1912,10 @@ mod tests {
|
||||||
let db = &server.app_state.db;
|
let db = &server.app_state.db;
|
||||||
let org_id = db.create_org("Test Org", "test-org").await.unwrap();
|
let org_id = db.create_org("Test Org", "test-org").await.unwrap();
|
||||||
let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
|
let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
|
||||||
db.add_org_member(org_id, current_user_id(&user_store_a), false)
|
db.add_org_member(org_id, current_user_id(&user_store_a, &cx_a), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.add_channel_member(channel_id, current_user_id(&user_store_a), false)
|
db.add_channel_member(channel_id, current_user_id(&user_store_a, &cx_a), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -1964,7 +1964,6 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_chat_reconnection(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
|
async fn test_chat_reconnection(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
|
||||||
cx_a.foreground().forbid_parking();
|
cx_a.foreground().forbid_parking();
|
||||||
let http = FakeHttpClient::new(|_| async move { Ok(surf::http::Response::new(404)) });
|
|
||||||
|
|
||||||
// Connect to a server as 2 clients.
|
// Connect to a server as 2 clients.
|
||||||
let mut server = TestServer::start().await;
|
let mut server = TestServer::start().await;
|
||||||
|
@ -1975,24 +1974,24 @@ mod tests {
|
||||||
// Create an org that includes these 2 users.
|
// Create an org that includes these 2 users.
|
||||||
let db = &server.app_state.db;
|
let db = &server.app_state.db;
|
||||||
let org_id = db.create_org("Test Org", "test-org").await.unwrap();
|
let org_id = db.create_org("Test Org", "test-org").await.unwrap();
|
||||||
db.add_org_member(org_id, current_user_id(&user_store_a), false)
|
db.add_org_member(org_id, current_user_id(&user_store_a, &cx_a), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.add_org_member(org_id, current_user_id(&user_store_b), false)
|
db.add_org_member(org_id, current_user_id(&user_store_b, &cx_b), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Create a channel that includes all the users.
|
// Create a channel that includes all the users.
|
||||||
let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
|
let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
|
||||||
db.add_channel_member(channel_id, current_user_id(&user_store_a), false)
|
db.add_channel_member(channel_id, current_user_id(&user_store_a, &cx_a), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.add_channel_member(channel_id, current_user_id(&user_store_b), false)
|
db.add_channel_member(channel_id, current_user_id(&user_store_b, &cx_b), false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.create_channel_message(
|
db.create_channel_message(
|
||||||
channel_id,
|
channel_id,
|
||||||
current_user_id(&user_store_b),
|
current_user_id(&user_store_b, &cx_b),
|
||||||
"hello A, it's B.",
|
"hello A, it's B.",
|
||||||
OffsetDateTime::now_utc(),
|
OffsetDateTime::now_utc(),
|
||||||
2,
|
2,
|
||||||
|
@ -2000,8 +1999,6 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let user_store_a =
|
|
||||||
UserStore::new(client_a.clone(), http.clone(), cx_a.background().as_ref());
|
|
||||||
let channels_a = cx_a.add_model(|cx| ChannelList::new(user_store_a, client_a, cx));
|
let channels_a = cx_a.add_model(|cx| ChannelList::new(user_store_a, client_a, cx));
|
||||||
channels_a
|
channels_a
|
||||||
.condition(&mut cx_a, |list, _| list.available_channels().is_some())
|
.condition(&mut cx_a, |list, _| list.available_channels().is_some())
|
||||||
|
@ -2054,7 +2051,7 @@ mod tests {
|
||||||
|
|
||||||
// Disconnect client B, ensuring we can still access its cached channel data.
|
// Disconnect client B, ensuring we can still access its cached channel data.
|
||||||
server.forbid_connections();
|
server.forbid_connections();
|
||||||
server.disconnect_client(current_user_id(&user_store_b));
|
server.disconnect_client(current_user_id(&user_store_b, &cx_b));
|
||||||
while !matches!(
|
while !matches!(
|
||||||
status_b.recv().await,
|
status_b.recv().await,
|
||||||
Some(rpc::Status::ReconnectionError { .. })
|
Some(rpc::Status::ReconnectionError { .. })
|
||||||
|
@ -2206,7 +2203,7 @@ mod tests {
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> (Arc<Client>, Arc<UserStore>) {
|
) -> (Arc<Client>, ModelHandle<UserStore>) {
|
||||||
let user_id = self.app_state.db.create_user(name, false).await.unwrap();
|
let user_id = self.app_state.db.create_user(name, false).await.unwrap();
|
||||||
let client_name = name.to_string();
|
let client_name = name.to_string();
|
||||||
let mut client = Client::new();
|
let mut client = Client::new();
|
||||||
|
@ -2254,8 +2251,9 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let user_store = UserStore::new(client.clone(), http, &cx.background());
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||||
let mut authed_user = user_store.watch_current_user();
|
let mut authed_user =
|
||||||
|
user_store.read_with(cx, |user_store, _| user_store.watch_current_user());
|
||||||
while authed_user.recv().await.unwrap().is_none() {}
|
while authed_user.recv().await.unwrap().is_none() {}
|
||||||
|
|
||||||
(client, user_store)
|
(client, user_store)
|
||||||
|
@ -2314,8 +2312,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_user_id(user_store: &Arc<UserStore>) -> UserId {
|
fn current_user_id(user_store: &ModelHandle<UserStore>, cx: &TestAppContext) -> UserId {
|
||||||
UserId::from_proto(user_store.current_user().unwrap().id)
|
UserId::from_proto(
|
||||||
|
user_store.read_with(cx, |user_store, _| user_store.current_user().unwrap().id),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> {
|
fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
sum_tree::{self, Bias, SumTree},
|
sum_tree::{self, Bias, SumTree},
|
||||||
Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
|
AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
|
||||||
};
|
};
|
||||||
use postage::prelude::Stream;
|
use postage::prelude::Stream;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
@ -26,7 +26,7 @@ pub struct ChannelList {
|
||||||
available_channels: Option<Vec<ChannelDetails>>,
|
available_channels: Option<Vec<ChannelDetails>>,
|
||||||
channels: HashMap<u64, WeakModelHandle<Channel>>,
|
channels: HashMap<u64, WeakModelHandle<Channel>>,
|
||||||
rpc: Arc<Client>,
|
rpc: Arc<Client>,
|
||||||
user_store: Arc<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
_task: Task<Option<()>>,
|
_task: Task<Option<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ pub struct Channel {
|
||||||
messages: SumTree<ChannelMessage>,
|
messages: SumTree<ChannelMessage>,
|
||||||
loaded_all_messages: bool,
|
loaded_all_messages: bool,
|
||||||
next_pending_message_id: usize,
|
next_pending_message_id: usize,
|
||||||
user_store: Arc<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
rpc: Arc<Client>,
|
rpc: Arc<Client>,
|
||||||
rng: StdRng,
|
rng: StdRng,
|
||||||
_subscription: rpc::Subscription,
|
_subscription: rpc::Subscription,
|
||||||
|
@ -87,7 +87,7 @@ impl Entity for ChannelList {
|
||||||
|
|
||||||
impl ChannelList {
|
impl ChannelList {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
user_store: Arc<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
rpc: Arc<rpc::Client>,
|
rpc: Arc<rpc::Client>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -186,7 +186,7 @@ impl Entity for Channel {
|
||||||
impl Channel {
|
impl Channel {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
details: ChannelDetails,
|
details: ChannelDetails,
|
||||||
user_store: Arc<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
rpc: Arc<Client>,
|
rpc: Arc<Client>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -199,7 +199,8 @@ impl Channel {
|
||||||
cx.spawn(|channel, mut cx| {
|
cx.spawn(|channel, mut cx| {
|
||||||
async move {
|
async move {
|
||||||
let response = rpc.request(proto::JoinChannel { channel_id }).await?;
|
let response = rpc.request(proto::JoinChannel { channel_id }).await?;
|
||||||
let messages = messages_from_proto(response.messages, &user_store).await?;
|
let messages =
|
||||||
|
messages_from_proto(response.messages, &user_store, &mut cx).await?;
|
||||||
let loaded_all_messages = response.done;
|
let loaded_all_messages = response.done;
|
||||||
|
|
||||||
channel.update(&mut cx, |channel, cx| {
|
channel.update(&mut cx, |channel, cx| {
|
||||||
|
@ -241,6 +242,7 @@ impl Channel {
|
||||||
|
|
||||||
let current_user = self
|
let current_user = self
|
||||||
.user_store
|
.user_store
|
||||||
|
.read(cx)
|
||||||
.current_user()
|
.current_user()
|
||||||
.ok_or_else(|| anyhow!("current_user is not present"))?;
|
.ok_or_else(|| anyhow!("current_user is not present"))?;
|
||||||
|
|
||||||
|
@ -272,6 +274,7 @@ impl Channel {
|
||||||
let message = ChannelMessage::from_proto(
|
let message = ChannelMessage::from_proto(
|
||||||
response.message.ok_or_else(|| anyhow!("invalid message"))?,
|
response.message.ok_or_else(|| anyhow!("invalid message"))?,
|
||||||
&user_store,
|
&user_store,
|
||||||
|
&mut cx,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
|
@ -301,7 +304,8 @@ impl Channel {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let loaded_all_messages = response.done;
|
let loaded_all_messages = response.done;
|
||||||
let messages = messages_from_proto(response.messages, &user_store).await?;
|
let messages =
|
||||||
|
messages_from_proto(response.messages, &user_store, &mut cx).await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.loaded_all_messages = loaded_all_messages;
|
this.loaded_all_messages = loaded_all_messages;
|
||||||
this.insert_messages(messages, cx);
|
this.insert_messages(messages, cx);
|
||||||
|
@ -324,7 +328,7 @@ impl Channel {
|
||||||
cx.spawn(|this, mut cx| {
|
cx.spawn(|this, mut cx| {
|
||||||
async move {
|
async move {
|
||||||
let response = rpc.request(proto::JoinChannel { channel_id }).await?;
|
let response = rpc.request(proto::JoinChannel { channel_id }).await?;
|
||||||
let messages = messages_from_proto(response.messages, &user_store).await?;
|
let messages = messages_from_proto(response.messages, &user_store, &mut cx).await?;
|
||||||
let loaded_all_messages = response.done;
|
let loaded_all_messages = response.done;
|
||||||
|
|
||||||
let pending_messages = this.update(&mut cx, |this, cx| {
|
let pending_messages = this.update(&mut cx, |this, cx| {
|
||||||
|
@ -359,6 +363,7 @@ impl Channel {
|
||||||
let message = ChannelMessage::from_proto(
|
let message = ChannelMessage::from_proto(
|
||||||
response.message.ok_or_else(|| anyhow!("invalid message"))?,
|
response.message.ok_or_else(|| anyhow!("invalid message"))?,
|
||||||
&user_store,
|
&user_store,
|
||||||
|
&mut cx,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
|
@ -413,7 +418,7 @@ impl Channel {
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| {
|
cx.spawn(|this, mut cx| {
|
||||||
async move {
|
async move {
|
||||||
let message = ChannelMessage::from_proto(message, &user_store).await?;
|
let message = ChannelMessage::from_proto(message, &user_store, &mut cx).await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.insert_messages(SumTree::from_item(message, &()), cx)
|
this.insert_messages(SumTree::from_item(message, &()), cx)
|
||||||
});
|
});
|
||||||
|
@ -486,7 +491,8 @@ impl Channel {
|
||||||
|
|
||||||
async fn messages_from_proto(
|
async fn messages_from_proto(
|
||||||
proto_messages: Vec<proto::ChannelMessage>,
|
proto_messages: Vec<proto::ChannelMessage>,
|
||||||
user_store: &UserStore,
|
user_store: &ModelHandle<UserStore>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
) -> Result<SumTree<ChannelMessage>> {
|
) -> Result<SumTree<ChannelMessage>> {
|
||||||
let unique_user_ids = proto_messages
|
let unique_user_ids = proto_messages
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -494,11 +500,15 @@ async fn messages_from_proto(
|
||||||
.collect::<HashSet<_>>()
|
.collect::<HashSet<_>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
user_store.load_users(unique_user_ids).await?;
|
user_store
|
||||||
|
.update(cx, |user_store, cx| {
|
||||||
|
user_store.load_users(unique_user_ids, cx)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
let mut messages = Vec::with_capacity(proto_messages.len());
|
let mut messages = Vec::with_capacity(proto_messages.len());
|
||||||
for message in proto_messages {
|
for message in proto_messages {
|
||||||
messages.push(ChannelMessage::from_proto(message, &user_store).await?);
|
messages.push(ChannelMessage::from_proto(message, user_store, cx).await?);
|
||||||
}
|
}
|
||||||
let mut result = SumTree::new();
|
let mut result = SumTree::new();
|
||||||
result.extend(messages, &());
|
result.extend(messages, &());
|
||||||
|
@ -517,9 +527,14 @@ impl From<proto::Channel> for ChannelDetails {
|
||||||
impl ChannelMessage {
|
impl ChannelMessage {
|
||||||
pub async fn from_proto(
|
pub async fn from_proto(
|
||||||
message: proto::ChannelMessage,
|
message: proto::ChannelMessage,
|
||||||
user_store: &UserStore,
|
user_store: &ModelHandle<UserStore>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let sender = user_store.fetch_user(message.sender_id).await?;
|
let sender = user_store
|
||||||
|
.update(cx, |user_store, cx| {
|
||||||
|
user_store.fetch_user(message.sender_id, cx)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
Ok(ChannelMessage {
|
Ok(ChannelMessage {
|
||||||
id: ChannelMessageId::Saved(message.id),
|
id: ChannelMessageId::Saved(message.id),
|
||||||
body: message.body,
|
body: message.body,
|
||||||
|
@ -595,7 +610,7 @@ mod tests {
|
||||||
let mut client = Client::new();
|
let mut client = Client::new();
|
||||||
let http_client = FakeHttpClient::new(|_| async move { Ok(Response::new(404)) });
|
let http_client = FakeHttpClient::new(|_| async move { Ok(Response::new(404)) });
|
||||||
let server = FakeServer::for_client(user_id, &mut client, &cx).await;
|
let server = FakeServer::for_client(user_id, &mut client, &cx).await;
|
||||||
let user_store = UserStore::new(client.clone(), http_client, cx.background().as_ref());
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||||
|
|
||||||
let channel_list = cx.add_model(|cx| ChannelList::new(user_store, client.clone(), cx));
|
let channel_list = cx.add_model(|cx| ChannelList::new(user_store, client.clone(), cx));
|
||||||
channel_list.read_with(&cx, |list, _| assert_eq!(list.available_channels(), None));
|
channel_list.read_with(&cx, |list, _| assert_eq!(list.available_channels(), None));
|
||||||
|
|
|
@ -28,7 +28,6 @@ use channel::ChannelList;
|
||||||
use gpui::{action, keymap::Binding, ModelHandle};
|
use gpui::{action, keymap::Binding, ModelHandle};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use presence::Presence;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
|
@ -46,10 +45,9 @@ pub struct AppState {
|
||||||
pub languages: Arc<language::LanguageRegistry>,
|
pub languages: Arc<language::LanguageRegistry>,
|
||||||
pub themes: Arc<settings::ThemeRegistry>,
|
pub themes: Arc<settings::ThemeRegistry>,
|
||||||
pub rpc: Arc<rpc::Client>,
|
pub rpc: Arc<rpc::Client>,
|
||||||
pub user_store: Arc<user::UserStore>,
|
pub user_store: ModelHandle<user::UserStore>,
|
||||||
pub fs: Arc<dyn fs::Fs>,
|
pub fs: Arc<dyn fs::Fs>,
|
||||||
pub channel_list: ModelHandle<ChannelList>,
|
pub channel_list: ModelHandle<ChannelList>,
|
||||||
pub presence: ModelHandle<Presence>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
|
pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
use crate::presence::Presence;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::Empty, Element, ElementBox, Entity, ModelHandle, RenderContext, View, ViewContext,
|
elements::Empty, Element, ElementBox, Entity, ModelHandle, RenderContext, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::user::UserStore;
|
||||||
|
|
||||||
pub struct PeoplePanel {
|
pub struct PeoplePanel {
|
||||||
presence: ModelHandle<Presence>,
|
user_store: ModelHandle<UserStore>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PeoplePanel {
|
impl PeoplePanel {
|
||||||
pub fn new(presence: ModelHandle<Presence>, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
|
||||||
cx.observe(&presence, |_, _, cx| cx.notify());
|
cx.observe(&user_store, |_, _, cx| cx.notify());
|
||||||
Self { presence }
|
Self { user_store }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,24 +106,25 @@ impl Entity for Presence {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collaborator {
|
// impl Collaborator {
|
||||||
async fn from_proto(
|
// async fn from_proto(
|
||||||
collaborator: proto::Collaborator,
|
// collaborator: proto::Collaborator,
|
||||||
user_store: &Arc<UserStore>,
|
// user_store: &Arc<UserStore>,
|
||||||
) -> Result<Self> {
|
// cx: &mut AsyncAppContext,
|
||||||
let user = user_store.fetch_user(collaborator.user_id).await?;
|
// ) -> Result<Self> {
|
||||||
let mut worktrees = Vec::new();
|
// let user = user_store.fetch_user(collaborator.user_id).await?;
|
||||||
for worktree in collaborator.worktrees {
|
// let mut worktrees = Vec::new();
|
||||||
let mut participants = Vec::new();
|
// for worktree in collaborator.worktrees {
|
||||||
for participant_id in worktree.participants {
|
// let mut participants = Vec::new();
|
||||||
participants.push(user_store.fetch_user(participant_id).await?);
|
// for participant_id in worktree.participants {
|
||||||
}
|
// participants.push(user_store.fetch_user(participant_id).await?);
|
||||||
worktrees.push(WorktreeMetadata {
|
// }
|
||||||
root_name: worktree.root_name,
|
// worktrees.push(WorktreeMetadata {
|
||||||
is_shared: worktree.is_shared,
|
// root_name: worktree.root_name,
|
||||||
participants,
|
// is_shared: worktree.is_shared,
|
||||||
});
|
// participants,
|
||||||
}
|
// });
|
||||||
Ok(Self { user, worktrees })
|
// }
|
||||||
}
|
// Ok(Self { user, worktrees })
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
|
@ -169,14 +169,13 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
|
||||||
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
||||||
let rpc = rpc::Client::new();
|
let rpc = rpc::Client::new();
|
||||||
let http = FakeHttpClient::new(|_| async move { Ok(ServerResponse::new(404)) });
|
let http = FakeHttpClient::new(|_| async move { Ok(ServerResponse::new(404)) });
|
||||||
let user_store = UserStore::new(rpc.clone(), http, cx.background());
|
let user_store = cx.add_model(|cx| UserStore::new(rpc.clone(), http, cx));
|
||||||
Arc::new(AppState {
|
Arc::new(AppState {
|
||||||
settings_tx: Arc::new(Mutex::new(settings_tx)),
|
settings_tx: Arc::new(Mutex::new(settings_tx)),
|
||||||
settings,
|
settings,
|
||||||
themes,
|
themes,
|
||||||
languages: languages.clone(),
|
languages: languages.clone(),
|
||||||
channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), rpc.clone(), cx)),
|
channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), rpc.clone(), cx)),
|
||||||
presence: cx.add_model(|cx| Presence::new(user_store.clone(), rpc.clone(), cx)),
|
|
||||||
rpc,
|
rpc,
|
||||||
user_store,
|
user_store,
|
||||||
fs: Arc::new(RealFs),
|
fs: Arc::new(RealFs),
|
||||||
|
|
100
zed/src/user.rs
100
zed/src/user.rs
|
@ -5,13 +5,9 @@ use crate::{
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use gpui::{executor, ImageData, Task};
|
use gpui::{Entity, ImageData, ModelContext, Task};
|
||||||
use parking_lot::Mutex;
|
use postage::{prelude::Stream, sink::Sink, watch};
|
||||||
use postage::{oneshot, prelude::Stream, sink::Sink, watch};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Weak},
|
|
||||||
};
|
|
||||||
use zrpc::proto;
|
use zrpc::proto;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -22,41 +18,38 @@ pub struct User {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UserStore {
|
pub struct UserStore {
|
||||||
users: Mutex<HashMap<u64, Arc<User>>>,
|
users: HashMap<u64, Arc<User>>,
|
||||||
current_user: watch::Receiver<Option<Arc<User>>>,
|
current_user: watch::Receiver<Option<Arc<User>>>,
|
||||||
rpc: Arc<Client>,
|
rpc: Arc<Client>,
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<dyn HttpClient>,
|
||||||
_maintain_current_user: Task<()>,
|
_maintain_current_user: Task<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Event {}
|
||||||
|
|
||||||
|
impl Entity for UserStore {
|
||||||
|
type Event = Event;
|
||||||
|
}
|
||||||
|
|
||||||
impl UserStore {
|
impl UserStore {
|
||||||
pub fn new(
|
pub fn new(rpc: Arc<Client>, http: Arc<dyn HttpClient>, cx: &mut ModelContext<Self>) -> Self {
|
||||||
rpc: Arc<Client>,
|
|
||||||
http: Arc<dyn HttpClient>,
|
|
||||||
executor: &executor::Background,
|
|
||||||
) -> Arc<Self> {
|
|
||||||
let (mut current_user_tx, current_user_rx) = watch::channel();
|
let (mut current_user_tx, current_user_rx) = watch::channel();
|
||||||
let (mut this_tx, mut this_rx) = oneshot::channel::<Weak<Self>>();
|
Self {
|
||||||
let this = Arc::new(Self {
|
|
||||||
users: Default::default(),
|
users: Default::default(),
|
||||||
current_user: current_user_rx,
|
current_user: current_user_rx,
|
||||||
rpc: rpc.clone(),
|
rpc: rpc.clone(),
|
||||||
http,
|
http,
|
||||||
_maintain_current_user: executor.spawn(async move {
|
_maintain_current_user: cx.spawn_weak(|this, mut cx| async move {
|
||||||
let this = if let Some(this) = this_rx.recv().await {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let mut status = rpc.status();
|
let mut status = rpc.status();
|
||||||
while let Some(status) = status.recv().await {
|
while let Some(status) = status.recv().await {
|
||||||
match status {
|
match status {
|
||||||
Status::Connected { .. } => {
|
Status::Connected { .. } => {
|
||||||
if let Some((this, user_id)) = this.upgrade().zip(rpc.user_id()) {
|
if let Some((this, user_id)) = this.upgrade(&cx).zip(rpc.user_id()) {
|
||||||
current_user_tx
|
let user = this
|
||||||
.send(this.fetch_user(user_id).log_err().await)
|
.update(&mut cx, |this, cx| this.fetch_user(user_id, cx))
|
||||||
.await
|
.log_err()
|
||||||
.ok();
|
.await;
|
||||||
|
current_user_tx.send(user).await.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Status::SignedOut => {
|
Status::SignedOut => {
|
||||||
|
@ -66,49 +59,60 @@ impl UserStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
});
|
}
|
||||||
let weak = Arc::downgrade(&this);
|
|
||||||
executor
|
|
||||||
.spawn(async move { this_tx.send(weak).await })
|
|
||||||
.detach();
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_users(&self, mut user_ids: Vec<u64>) -> Result<()> {
|
|
||||||
{
|
|
||||||
let users = self.users.lock();
|
|
||||||
user_ids.retain(|id| !users.contains_key(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_users(
|
||||||
|
&mut self,
|
||||||
|
mut user_ids: Vec<u64>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let rpc = self.rpc.clone();
|
||||||
|
let http = self.http.clone();
|
||||||
|
user_ids.retain(|id| !self.users.contains_key(id));
|
||||||
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
if !user_ids.is_empty() {
|
if !user_ids.is_empty() {
|
||||||
let response = self.rpc.request(proto::GetUsers { user_ids }).await?;
|
let response = rpc.request(proto::GetUsers { user_ids }).await?;
|
||||||
let new_users = future::join_all(
|
let new_users = future::join_all(
|
||||||
response
|
response
|
||||||
.users
|
.users
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|user| User::new(user, self.http.as_ref())),
|
.map(|user| User::new(user, http.as_ref())),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let mut users = self.users.lock();
|
|
||||||
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
this.update(&mut cx, |this, _| {
|
||||||
for user in new_users {
|
for user in new_users {
|
||||||
users.insert(user.id, Arc::new(user));
|
this.users.insert(user.id, Arc::new(user));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_user(&self, user_id: u64) -> Result<Arc<User>> {
|
pub fn fetch_user(
|
||||||
if let Some(user) = self.users.lock().get(&user_id).cloned() {
|
&mut self,
|
||||||
return Ok(user);
|
user_id: u64,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Task<Result<Arc<User>>> {
|
||||||
|
if let Some(user) = self.users.get(&user_id).cloned() {
|
||||||
|
return cx.spawn_weak(|_, _| async move { Ok(user) });
|
||||||
}
|
}
|
||||||
|
|
||||||
self.load_users(vec![user_id]).await?;
|
let load_users = self.load_users(vec![user_id], cx);
|
||||||
self.users
|
cx.spawn(|this, mut cx| async move {
|
||||||
.lock()
|
load_users.await?;
|
||||||
|
this.update(&mut cx, |this, _| {
|
||||||
|
this.users
|
||||||
.get(&user_id)
|
.get(&user_id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| anyhow!("server responded with no users"))
|
.ok_or_else(|| anyhow!("server responded with no users"))
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_user(&self) -> Option<Arc<User>> {
|
pub fn current_user(&self) -> Option<Arc<User>> {
|
||||||
|
|
|
@ -333,7 +333,7 @@ pub struct Workspace {
|
||||||
pub settings: watch::Receiver<Settings>,
|
pub settings: watch::Receiver<Settings>,
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
rpc: Arc<rpc::Client>,
|
rpc: Arc<rpc::Client>,
|
||||||
user_store: Arc<user::UserStore>,
|
user_store: ModelHandle<user::UserStore>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
modal: Option<AnyViewHandle>,
|
modal: Option<AnyViewHandle>,
|
||||||
center: PaneGroup,
|
center: PaneGroup,
|
||||||
|
@ -381,11 +381,11 @@ impl Workspace {
|
||||||
);
|
);
|
||||||
right_sidebar.add_item(
|
right_sidebar.add_item(
|
||||||
"icons/user-16.svg",
|
"icons/user-16.svg",
|
||||||
cx.add_view(|cx| PeoplePanel::new(app_state.presence.clone(), cx))
|
cx.add_view(|cx| PeoplePanel::new(app_state.user_store.clone(), cx))
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut current_user = app_state.user_store.watch_current_user().clone();
|
let mut current_user = app_state.user_store.read(cx).watch_current_user().clone();
|
||||||
let mut connection_status = app_state.rpc.status().clone();
|
let mut connection_status = app_state.rpc.status().clone();
|
||||||
let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
|
let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
|
||||||
current_user.recv().await;
|
current_user.recv().await;
|
||||||
|
@ -965,6 +965,7 @@ impl Workspace {
|
||||||
let theme = &self.settings.borrow().theme;
|
let theme = &self.settings.borrow().theme;
|
||||||
let avatar = if let Some(avatar) = self
|
let avatar = if let Some(avatar) = self
|
||||||
.user_store
|
.user_store
|
||||||
|
.read(cx)
|
||||||
.current_user()
|
.current_user()
|
||||||
.and_then(|user| user.avatar.clone())
|
.and_then(|user| user.avatar.clone())
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue