client: Add CloudUserStore (#35370)

This PR adds a new `CloudUserStore` for storing information about the
user retrieved from Cloud instead of Collab.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-07-30 18:43:10 -04:00 committed by GitHub
parent 289f420504
commit bb1a7ccbba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 68 additions and 4 deletions

View file

@ -1,6 +1,7 @@
#[cfg(any(test, feature = "test-support"))]
pub mod test;
mod cloud;
mod proxy;
pub mod telemetry;
pub mod user;
@ -52,6 +53,7 @@ use tokio::net::TcpStream;
use url::Url;
use util::{ConnectionResult, ResultExt};
pub use cloud::*;
pub use rpc::*;
pub use telemetry_events::Event;
pub use user::*;
@ -621,6 +623,10 @@ impl Client {
self.http.clone()
}
pub fn cloud_client(&self) -> Arc<CloudApiClient> {
self.cloud_client.clone()
}
pub fn set_id(&self, id: u64) -> &Self {
self.id.store(id, Ordering::SeqCst);
self

View file

@ -0,0 +1,3 @@
mod user_store;
pub use user_store::*;

View file

@ -0,0 +1,41 @@
use std::sync::Arc;
use std::time::Duration;
use anyhow::Context as _;
use cloud_api_client::{AuthenticatedUser, CloudApiClient};
use gpui::{Context, Task};
use util::{ResultExt as _, maybe};
pub struct CloudUserStore {
authenticated_user: Option<AuthenticatedUser>,
_fetch_authenticated_user_task: Task<()>,
}
impl CloudUserStore {
pub fn new(cloud_client: Arc<CloudApiClient>, cx: &mut Context<Self>) -> Self {
Self {
authenticated_user: None,
_fetch_authenticated_user_task: cx.spawn(async move |this, cx| {
maybe!(async move {
loop {
if cloud_client.has_credentials() {
break;
}
cx.background_executor()
.timer(Duration::from_millis(100))
.await;
}
let response = cloud_client.get_authenticated_user().await?;
this.update(cx, |this, _cx| {
this.authenticated_user = Some(response.user);
})
})
.await
.context("failed to fetch authenticated user")
.log_err();
}),
}
}
}

View file

@ -24,6 +24,10 @@ impl CloudApiClient {
}
}
pub fn has_credentials(&self) -> bool {
self.credentials.read().is_some()
}
pub fn set_credentials(&self, user_id: u32, access_token: String) {
*self.credentials.write() = Some(Credentials {
user_id,
@ -43,7 +47,7 @@ impl CloudApiClient {
))
}
pub async fn get_authenticated_user(&self) -> Result<AuthenticatedUser> {
pub async fn get_authenticated_user(&self) -> Result<GetAuthenticatedUserResponse> {
let request = Request::builder()
.method(Method::GET)
.uri(
@ -69,8 +73,7 @@ impl CloudApiClient {
let mut body = String::new();
response.body_mut().read_to_string(&mut body).await?;
let response: GetAuthenticatedUserResponse = serde_json::from_str(&body)?;
Ok(response.user)
Ok(serde_json::from_str(&body)?)
}
}

View file

@ -8,6 +8,7 @@ use crate::{
use anyhow::anyhow;
use call::ActiveCall;
use channel::{ChannelBuffer, ChannelStore};
use client::CloudUserStore;
use client::{
self, ChannelId, Client, Connection, Credentials, EstablishConnectionError, UserStore,
proto::PeerId,
@ -281,12 +282,14 @@ impl TestServer {
.register_hosting_provider(Arc::new(git_hosting_providers::Github::public_instance()));
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
let cloud_user_store = cx.new(|cx| CloudUserStore::new(client.cloud_client(), cx));
let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
let language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
let session = cx.new(|cx| AppSession::new(Session::test(), cx));
let app_state = Arc::new(workspace::AppState {
client: client.clone(),
user_store: user_store.clone(),
cloud_user_store,
workspace_store,
languages: language_registry,
fs: fs.clone(),

View file

@ -15,6 +15,7 @@ mod toast_layer;
mod toolbar;
mod workspace_settings;
use client::CloudUserStore;
pub use toast_layer::{ToastAction, ToastLayer, ToastView};
use anyhow::{Context as _, Result, anyhow};
@ -839,6 +840,7 @@ pub struct AppState {
pub languages: Arc<LanguageRegistry>,
pub client: Arc<Client>,
pub user_store: Entity<UserStore>,
pub cloud_user_store: Entity<CloudUserStore>,
pub workspace_store: Entity<WorkspaceStore>,
pub fs: Arc<dyn fs::Fs>,
pub build_window_options: fn(Option<Uuid>, &mut App) -> WindowOptions,
@ -911,6 +913,7 @@ impl AppState {
let client = Client::new(clock, http_client.clone(), cx);
let session = cx.new(|cx| AppSession::new(Session::test(), cx));
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
let cloud_user_store = cx.new(|cx| CloudUserStore::new(client.cloud_client(), cx));
let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
theme::init(theme::LoadThemes::JustBase, cx);
@ -922,6 +925,7 @@ impl AppState {
fs,
languages,
user_store,
cloud_user_store,
workspace_store,
node_runtime: NodeRuntime::unavailable(),
build_window_options: |_, _| Default::default(),
@ -5689,6 +5693,7 @@ impl Workspace {
let client = project.read(cx).client();
let user_store = project.read(cx).user_store();
let cloud_user_store = cx.new(|cx| CloudUserStore::new(client.cloud_client(), cx));
let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
let session = cx.new(|cx| AppSession::new(Session::test(), cx));
@ -5696,6 +5701,7 @@ impl Workspace {
let app_state = Arc::new(AppState {
languages: project.read(cx).languages().clone(),
workspace_store,
cloud_user_store,
client,
user_store,
fs: project.read(cx).fs().clone(),

View file

@ -5,7 +5,7 @@ use agent_ui::AgentPanel;
use anyhow::{Context as _, Result};
use clap::{Parser, command};
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
use client::{Client, ProxySettings, UserStore, parse_zed_link};
use client::{Client, CloudUserStore, ProxySettings, UserStore, parse_zed_link};
use collab_ui::channel_view::ChannelView;
use collections::HashMap;
use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE};
@ -457,6 +457,7 @@ pub fn main() {
language::init(cx);
languages::init(languages.clone(), node_runtime.clone(), cx);
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
let cloud_user_store = cx.new(|cx| CloudUserStore::new(client.cloud_client(), cx));
let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
language_extension::init(
@ -516,6 +517,7 @@ pub fn main() {
languages: languages.clone(),
client: client.clone(),
user_store: user_store.clone(),
cloud_user_store,
fs: fs.clone(),
build_window_options,
workspace_store,