Start on Presence as the backing model for the people panel

This commit is contained in:
Antonio Scandurra
2021-09-17 16:18:41 +02:00
parent 12fa309b7c
commit f2825cceba
8 changed files with 191 additions and 6 deletions
+12 -2
View File
@@ -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,
});
+4
View File
@@ -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
View File
@@ -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),
+31
View File
@@ -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()
}
}
+129
View File
@@ -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 })
}
}
+2
View File
@@ -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),
+6 -1
View File
@@ -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();
+3 -2
View File
@@ -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;
}