WIP
This commit is contained in:
parent
428c491542
commit
f0019e3725
8 changed files with 135 additions and 31 deletions
|
@ -2282,6 +2282,16 @@ impl<'a, T: View> ViewContext<'a, T> {
|
|||
let handle = self.handle();
|
||||
self.app.spawn(|cx| f(handle, cx))
|
||||
}
|
||||
|
||||
pub fn spawn_weak<F, Fut, S>(&self, f: F) -> Task<S>
|
||||
where
|
||||
F: FnOnce(WeakViewHandle<T>, AsyncAppContext) -> Fut,
|
||||
Fut: 'static + Future<Output = S>,
|
||||
S: 'static,
|
||||
{
|
||||
let handle = self.handle().downgrade();
|
||||
self.app.spawn(|cx| f(handle, cx))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenderContext<'a, T: View> {
|
||||
|
|
|
@ -1512,7 +1512,7 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_store_a = Arc::new(UserStore::new(client_a.clone()));
|
||||
let user_store_a = UserStore::new(client_a.clone(), cx_a.background().as_ref());
|
||||
let channels_a = cx_a.add_model(|cx| ChannelList::new(user_store_a, client_a, cx));
|
||||
channels_a
|
||||
.condition(&mut cx_a, |list, _| list.available_channels().is_some())
|
||||
|
@ -1537,7 +1537,7 @@ mod tests {
|
|||
})
|
||||
.await;
|
||||
|
||||
let user_store_b = Arc::new(UserStore::new(client_b.clone()));
|
||||
let user_store_b = UserStore::new(client_b.clone(), cx_b.background().as_ref());
|
||||
let channels_b = cx_b.add_model(|cx| ChannelList::new(user_store_b, client_b, cx));
|
||||
channels_b
|
||||
.condition(&mut cx_b, |list, _| list.available_channels().is_some())
|
||||
|
@ -1637,7 +1637,7 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_store_a = Arc::new(UserStore::new(client_a.clone()));
|
||||
let user_store_a = UserStore::new(client_a.clone(), cx_a.background().as_ref());
|
||||
let channels_a = cx_a.add_model(|cx| ChannelList::new(user_store_a, client_a, cx));
|
||||
channels_a
|
||||
.condition(&mut cx_a, |list, _| list.available_channels().is_some())
|
||||
|
@ -1713,7 +1713,7 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_store_a = Arc::new(UserStore::new(client_a.clone()));
|
||||
let user_store_a = UserStore::new(client_a.clone(), cx_a.background().as_ref());
|
||||
let channels_a = cx_a.add_model(|cx| ChannelList::new(user_store_a, client_a, cx));
|
||||
channels_a
|
||||
.condition(&mut cx_a, |list, _| list.available_channels().is_some())
|
||||
|
@ -1739,7 +1739,7 @@ mod tests {
|
|||
})
|
||||
.await;
|
||||
|
||||
let user_store_b = Arc::new(UserStore::new(client_b.clone()));
|
||||
let user_store_b = UserStore::new(client_b.clone(), cx_b.background().as_ref());
|
||||
let channels_b = cx_b.add_model(|cx| ChannelList::new(user_store_b, client_b, cx));
|
||||
channels_b
|
||||
.condition(&mut cx_b, |list, _| list.available_channels().is_some())
|
||||
|
|
|
@ -118,7 +118,7 @@ impl ChannelList {
|
|||
cx.notify();
|
||||
});
|
||||
}
|
||||
rpc::Status::Disconnected { .. } => {
|
||||
rpc::Status::SignedOut { .. } => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.available_channels = None;
|
||||
this.channels.clear();
|
||||
|
@ -503,7 +503,7 @@ mod tests {
|
|||
let user_id = 5;
|
||||
let mut client = Client::new();
|
||||
let server = FakeServer::for_client(user_id, &mut client, &cx).await;
|
||||
let user_store = Arc::new(UserStore::new(client.clone()));
|
||||
let user_store = UserStore::new(client.clone(), cx.background().as_ref());
|
||||
|
||||
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));
|
||||
|
|
|
@ -37,7 +37,7 @@ fn main() {
|
|||
|
||||
app.run(move |cx| {
|
||||
let rpc = rpc::Client::new();
|
||||
let user_store = Arc::new(UserStore::new(rpc.clone()));
|
||||
let user_store = UserStore::new(rpc.clone(), cx.background());
|
||||
let app_state = Arc::new(AppState {
|
||||
languages: languages.clone(),
|
||||
settings_tx: Arc::new(Mutex::new(settings_tx)),
|
||||
|
|
|
@ -39,7 +39,7 @@ pub struct Client {
|
|||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Status {
|
||||
Disconnected,
|
||||
SignedOut,
|
||||
Authenticating,
|
||||
Connecting {
|
||||
user_id: u64,
|
||||
|
@ -73,7 +73,7 @@ struct ClientState {
|
|||
impl Default for ClientState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
status: watch::channel_with(Status::Disconnected),
|
||||
status: watch::channel_with(Status::SignedOut),
|
||||
entity_id_extractors: Default::default(),
|
||||
model_handlers: Default::default(),
|
||||
_maintain_connection: None,
|
||||
|
@ -167,7 +167,7 @@ impl Client {
|
|||
}
|
||||
}));
|
||||
}
|
||||
Status::Disconnected => {
|
||||
Status::SignedOut => {
|
||||
state._maintain_connection.take();
|
||||
}
|
||||
_ => {}
|
||||
|
@ -232,7 +232,7 @@ impl Client {
|
|||
cx: &AsyncAppContext,
|
||||
) -> anyhow::Result<()> {
|
||||
let was_disconnected = match *self.status().borrow() {
|
||||
Status::Disconnected => true,
|
||||
Status::SignedOut => true,
|
||||
Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => {
|
||||
false
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ impl Client {
|
|||
cx.foreground()
|
||||
.spawn(async move {
|
||||
match handle_io.await {
|
||||
Ok(()) => this.set_status(Status::Disconnected, &cx),
|
||||
Ok(()) => this.set_status(Status::SignedOut, &cx),
|
||||
Err(err) => {
|
||||
log::error!("connection error: {:?}", err);
|
||||
this.set_status(Status::ConnectionLost, &cx);
|
||||
|
@ -470,7 +470,7 @@ impl Client {
|
|||
pub async fn disconnect(self: &Arc<Self>, cx: &AsyncAppContext) -> Result<()> {
|
||||
let conn_id = self.connection_id()?;
|
||||
self.peer.disconnect(conn_id).await;
|
||||
self.set_status(Status::Disconnected, cx);
|
||||
self.set_status(Status::SignedOut, cx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
|
|||
let languages = Arc::new(LanguageRegistry::new());
|
||||
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
||||
let rpc = rpc::Client::new();
|
||||
let user_store = Arc::new(UserStore::new(rpc.clone()));
|
||||
let user_store = UserStore::new(rpc.clone(), cx.background());
|
||||
Arc::new(AppState {
|
||||
settings_tx: Arc::new(Mutex::new(settings_tx)),
|
||||
settings,
|
||||
|
|
|
@ -1,22 +1,73 @@
|
|||
use crate::rpc::Client;
|
||||
use crate::{
|
||||
rpc::{Client, Status},
|
||||
util::TryFutureExt,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{elements::Image, executor, ImageData, Task};
|
||||
use parking_lot::Mutex;
|
||||
use postage::{prelude::Stream, sink::Sink, watch};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use surf::{
|
||||
http::{Method, Request},
|
||||
HttpClient, Url,
|
||||
};
|
||||
use zrpc::proto;
|
||||
|
||||
pub use proto::User;
|
||||
pub struct User {
|
||||
id: u64,
|
||||
github_login: String,
|
||||
avatar: Option<ImageData>,
|
||||
}
|
||||
|
||||
pub struct UserStore {
|
||||
users: Mutex<HashMap<u64, Arc<User>>>,
|
||||
current_user: watch::Receiver<Option<Arc<User>>>,
|
||||
rpc: Arc<Client>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_maintain_current_user: Option<Task<()>>,
|
||||
}
|
||||
|
||||
impl UserStore {
|
||||
pub fn new(rpc: Arc<Client>) -> Self {
|
||||
Self {
|
||||
pub fn new(
|
||||
rpc: Arc<Client>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
executor: &executor::Background,
|
||||
) -> Arc<Self> {
|
||||
let (mut current_user_tx, current_user_rx) = watch::channel();
|
||||
|
||||
let mut this = Arc::new(Self {
|
||||
users: Default::default(),
|
||||
rpc,
|
||||
}
|
||||
current_user: current_user_rx,
|
||||
rpc: rpc.clone(),
|
||||
http,
|
||||
_maintain_current_user: None,
|
||||
});
|
||||
|
||||
let task = {
|
||||
let this = Arc::downgrade(&this);
|
||||
executor.spawn(async move {
|
||||
let mut status = rpc.status();
|
||||
while let Some(status) = status.recv().await {
|
||||
match status {
|
||||
Status::Connected { user_id, .. } => {
|
||||
if let Some(this) = this.upgrade() {
|
||||
current_user_tx
|
||||
.send(this.fetch_user(user_id).log_err().await)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
Status::SignedOut => {
|
||||
current_user_tx.send(None).await.ok();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
Arc::get_mut(&mut this).unwrap()._maintain_current_user = Some(task);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub async fn load_users(&self, mut user_ids: Vec<u64>) -> Result<()> {
|
||||
|
@ -56,4 +107,29 @@ impl UserStore {
|
|||
Err(anyhow!("server responded with no users"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_user(&self) -> &watch::Receiver<Option<Arc<User>>> {
|
||||
&self.current_user
|
||||
}
|
||||
}
|
||||
|
||||
impl User {
|
||||
async fn new(message: proto::User, http: &dyn HttpClient) -> Self {
|
||||
let avatar = fetch_avatar(http, &message.avatar_url).await.log_err();
|
||||
User {
|
||||
id: message.id,
|
||||
github_login: message.github_login,
|
||||
avatar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_avatar(http: &dyn HttpClient, url: &str) -> Result<Arc<ImageData>> {
|
||||
let url = Url::parse(url)?;
|
||||
let request = Request::new(Method::Get, url);
|
||||
let response = http.send(request).await?;
|
||||
let bytes = response.body_bytes().await?;
|
||||
let format = image::guess_format(&bytes)?;
|
||||
let image = image::load_from_memory_with_format(&bytes, format)?.into_bgra8();
|
||||
Ok(ImageData::new(image))
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use gpui::{
|
|||
use log::error;
|
||||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
use postage::watch;
|
||||
use postage::{prelude::Stream, watch};
|
||||
use sidebar::{Side, Sidebar, ToggleSidebarItem};
|
||||
use smol::prelude::*;
|
||||
use std::{
|
||||
|
@ -356,6 +356,7 @@ pub struct Workspace {
|
|||
(usize, Arc<Path>),
|
||||
postage::watch::Receiver<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>,
|
||||
>,
|
||||
_observe_current_user: Task<()>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
|
@ -389,6 +390,18 @@ impl Workspace {
|
|||
);
|
||||
right_sidebar.add_item("icons/user-16.svg", cx.add_view(|_| ProjectBrowser).into());
|
||||
|
||||
let mut current_user = app_state.user_store.current_user().clone();
|
||||
let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
|
||||
current_user.recv().await;
|
||||
while current_user.recv().await.is_some() {
|
||||
cx.update(|cx| {
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(cx, |_, cx| cx.notify());
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
Workspace {
|
||||
modal: None,
|
||||
center: PaneGroup::new(pane.id()),
|
||||
|
@ -404,6 +417,7 @@ impl Workspace {
|
|||
worktrees: Default::default(),
|
||||
items: Default::default(),
|
||||
loading_items: Default::default(),
|
||||
_observe_current_user,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -940,17 +954,21 @@ impl Workspace {
|
|||
&self.active_pane
|
||||
}
|
||||
|
||||
fn render_account_status(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
fn render_current_user(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let theme = &self.settings.borrow().theme;
|
||||
let avatar = if let Some(current_user) = self.user_store.current_user().borrow().as_ref() {
|
||||
todo!()
|
||||
} else {
|
||||
Svg::new("icons/signed-out-12.svg")
|
||||
.with_color(theme.workspace.titlebar.icon_signed_out)
|
||||
.boxed()
|
||||
};
|
||||
|
||||
ConstrainedBox::new(
|
||||
Align::new(
|
||||
ConstrainedBox::new(
|
||||
Svg::new("icons/signed-out-12.svg")
|
||||
.with_color(theme.workspace.titlebar.icon_signed_out)
|
||||
.boxed(),
|
||||
)
|
||||
.with_width(theme.workspace.titlebar.icon_width)
|
||||
.boxed(),
|
||||
ConstrainedBox::new(avatar)
|
||||
.with_width(theme.workspace.titlebar.icon_width)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
|
@ -988,7 +1006,7 @@ impl View for Workspace {
|
|||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Align::new(self.render_account_status(cx)).right().boxed(),
|
||||
Align::new(self.render_current_user(cx)).right().boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue