Start on Presence as the backing model for the people panel
This commit is contained in:
+12
-2
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
auth,
|
||||
db::{ChannelId, MessageId, User, UserId},
|
||||
db::{ChannelId, MessageId, UserId},
|
||||
AppState,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
@@ -280,11 +280,20 @@ impl Server {
|
||||
request: TypedEnvelope<proto::OpenWorktree>,
|
||||
) -> tide::Result<()> {
|
||||
let receipt = request.receipt();
|
||||
let user_id = self
|
||||
.state
|
||||
.read()
|
||||
.await
|
||||
.user_id_for_connection(request.sender_id)?;
|
||||
|
||||
let mut collaborator_user_ids = Vec::new();
|
||||
for github_login in request.payload.collaborator_logins {
|
||||
match self.app_state.db.create_user(&github_login, false).await {
|
||||
Ok(user_id) => collaborator_user_ids.push(user_id),
|
||||
Ok(collaborator_user_id) => {
|
||||
if collaborator_user_id != user_id {
|
||||
collaborator_user_ids.push(collaborator_user_id);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let message = err.to_string();
|
||||
self.peer
|
||||
@@ -717,6 +726,7 @@ impl Server {
|
||||
is_online: true,
|
||||
});
|
||||
host.worktrees.push(proto::CollaboratorWorktree {
|
||||
root_name: worktree.root_name.clone(),
|
||||
is_shared: worktree.share().is_ok(),
|
||||
participants,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,8 @@ mod fuzzy;
|
||||
pub mod http;
|
||||
pub mod language;
|
||||
pub mod menus;
|
||||
pub mod people_panel;
|
||||
pub mod presence;
|
||||
pub mod project_browser;
|
||||
pub mod rpc;
|
||||
pub mod settings;
|
||||
@@ -26,6 +28,7 @@ use channel::ChannelList;
|
||||
use gpui::{action, keymap::Binding, ModelHandle};
|
||||
use parking_lot::Mutex;
|
||||
use postage::watch;
|
||||
use presence::Presence;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use settings::Settings;
|
||||
@@ -46,6 +49,7 @@ pub struct AppState {
|
||||
pub user_store: Arc<user::UserStore>,
|
||||
pub fs: Arc<dyn fs::Fs>,
|
||||
pub channel_list: ModelHandle<ChannelList>,
|
||||
pub presence: ModelHandle<Presence>,
|
||||
}
|
||||
|
||||
pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
|
||||
|
||||
+4
-1
@@ -13,7 +13,9 @@ use zed::{
|
||||
channel::ChannelList,
|
||||
chat_panel, editor, file_finder,
|
||||
fs::RealFs,
|
||||
http, language, menus, rpc, settings, theme_selector,
|
||||
http, language, menus,
|
||||
presence::Presence,
|
||||
rpc, settings, theme_selector,
|
||||
user::UserStore,
|
||||
workspace::{self, OpenNew, OpenParams, OpenPaths},
|
||||
AppState,
|
||||
@@ -45,6 +47,7 @@ fn main() {
|
||||
settings,
|
||||
themes,
|
||||
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,
|
||||
user_store,
|
||||
fs: Arc::new(RealFs),
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::presence::Presence;
|
||||
use gpui::{
|
||||
elements::Empty, Element, ElementBox, Entity, ModelHandle, RenderContext, View, ViewContext,
|
||||
};
|
||||
|
||||
pub struct PeoplePanel {
|
||||
presence: ModelHandle<Presence>,
|
||||
}
|
||||
|
||||
impl PeoplePanel {
|
||||
pub fn new(presence: ModelHandle<Presence>, cx: &mut ViewContext<Self>) -> Self {
|
||||
cx.observe(&presence, |_, _, cx| cx.notify());
|
||||
Self { presence }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Event {}
|
||||
|
||||
impl Entity for PeoplePanel {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl View for PeoplePanel {
|
||||
fn ui_name() -> &'static str {
|
||||
"PeoplePanel"
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
|
||||
Empty::new().boxed()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
use crate::{
|
||||
rpc::Client,
|
||||
user::{User, UserStore},
|
||||
util::TryFutureExt,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::{Entity, ModelContext, Task};
|
||||
use postage::prelude::Stream;
|
||||
use smol::future::FutureExt;
|
||||
use std::{collections::HashSet, sync::Arc, time::Duration};
|
||||
use zrpc::proto;
|
||||
|
||||
pub struct Presence {
|
||||
collaborators: Vec<Collaborator>,
|
||||
user_store: Arc<UserStore>,
|
||||
rpc: Arc<Client>,
|
||||
_maintain_people: Task<()>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Collaborator {
|
||||
user: Arc<User>,
|
||||
worktrees: Vec<CollaboratorWorktree>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CollaboratorWorktree {
|
||||
root_name: String,
|
||||
is_shared: bool,
|
||||
participants: Vec<Arc<User>>,
|
||||
}
|
||||
|
||||
impl Presence {
|
||||
pub fn new(user_store: Arc<UserStore>, rpc: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
|
||||
let _maintain_collaborators = cx.spawn_weak(|this, mut cx| {
|
||||
let user_store = user_store.clone();
|
||||
let foreground = cx.foreground();
|
||||
async move {
|
||||
let mut current_user = user_store.watch_current_user();
|
||||
loop {
|
||||
let timer = foreground.timer(Duration::from_secs(2));
|
||||
let next_current_user = async {
|
||||
current_user.recv().await;
|
||||
};
|
||||
|
||||
next_current_user.race(timer).await;
|
||||
if current_user.borrow().is_some() {
|
||||
if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
|
||||
this.update(&mut cx, |this, cx| this.refresh(cx))
|
||||
.log_err()
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
collaborators: Vec::new(),
|
||||
user_store,
|
||||
rpc,
|
||||
_maintain_people: _maintain_collaborators,
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
cx.spawn(|this, mut cx| {
|
||||
let rpc = self.rpc.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
async move {
|
||||
let response = rpc.request(proto::GetCollaborators {}).await?;
|
||||
let mut user_ids = HashSet::new();
|
||||
for collaborator in &response.collaborators {
|
||||
user_ids.insert(collaborator.user_id);
|
||||
user_ids.extend(
|
||||
collaborator
|
||||
.worktrees
|
||||
.iter()
|
||||
.flat_map(|w| &w.participants)
|
||||
.copied(),
|
||||
);
|
||||
}
|
||||
user_store
|
||||
.load_users(user_ids.into_iter().collect())
|
||||
.await?;
|
||||
|
||||
let mut collaborators = Vec::new();
|
||||
for collaborator in response.collaborators {
|
||||
collaborators.push(Collaborator::from_proto(collaborator, &user_store).await?);
|
||||
}
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.collaborators = collaborators;
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Event {}
|
||||
|
||||
impl Entity for Presence {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl Collaborator {
|
||||
async fn from_proto(
|
||||
collaborator: proto::Collaborator,
|
||||
user_store: &Arc<UserStore>,
|
||||
) -> Result<Self> {
|
||||
let user = user_store.fetch_user(collaborator.user_id).await?;
|
||||
let mut worktrees = Vec::new();
|
||||
for worktree in collaborator.worktrees {
|
||||
let mut participants = Vec::new();
|
||||
for participant_id in worktree.participants {
|
||||
participants.push(user_store.fetch_user(participant_id).await?);
|
||||
}
|
||||
worktrees.push(CollaboratorWorktree {
|
||||
root_name: worktree.root_name,
|
||||
is_shared: worktree.is_shared,
|
||||
participants,
|
||||
});
|
||||
}
|
||||
Ok(Self { user, worktrees })
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use crate::{
|
||||
fs::RealFs,
|
||||
http::{HttpClient, Request, Response, ServerResponse},
|
||||
language::LanguageRegistry,
|
||||
presence::Presence,
|
||||
rpc::{self, Client, Credentials, EstablishConnectionError},
|
||||
settings::{self, ThemeRegistry},
|
||||
time::ReplicaId,
|
||||
@@ -175,6 +176,7 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
|
||||
themes,
|
||||
languages: languages.clone(),
|
||||
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,
|
||||
user_store,
|
||||
fs: Arc::new(RealFs),
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::{
|
||||
editor::Buffer,
|
||||
fs::Fs,
|
||||
language::LanguageRegistry,
|
||||
people_panel::PeoplePanel,
|
||||
project_browser::ProjectBrowser,
|
||||
rpc,
|
||||
settings::Settings,
|
||||
@@ -378,7 +379,11 @@ impl Workspace {
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
right_sidebar.add_item("icons/user-16.svg", cx.add_view(|_| ProjectBrowser).into());
|
||||
right_sidebar.add_item(
|
||||
"icons/user-16.svg",
|
||||
cx.add_view(|cx| PeoplePanel::new(app_state.presence.clone(), cx))
|
||||
.into(),
|
||||
);
|
||||
|
||||
let mut current_user = app_state.user_store.watch_current_user().clone();
|
||||
let mut connection_status = app_state.rpc.status().clone();
|
||||
|
||||
@@ -342,6 +342,7 @@ message Collaborator {
|
||||
}
|
||||
|
||||
message CollaboratorWorktree {
|
||||
bool is_shared = 1;
|
||||
repeated uint64 participants = 2;
|
||||
string root_name = 1;
|
||||
bool is_shared = 2;
|
||||
repeated uint64 participants = 3;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user