WIP
This commit is contained in:
parent
5423012368
commit
6ffeb048b3
5 changed files with 132 additions and 152 deletions
|
@ -11,12 +11,11 @@ use async_tungstenite::tungstenite::{
|
||||||
http::{Request, StatusCode},
|
http::{Request, StatusCode},
|
||||||
};
|
};
|
||||||
use futures::{
|
use futures::{
|
||||||
future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _,
|
future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, TryStreamExt,
|
||||||
TryStreamExt,
|
|
||||||
};
|
};
|
||||||
use gpui2::{
|
use gpui2::{
|
||||||
serde_json, AnyHandle, AnyWeakHandle, AnyWindowHandle, AppContext, AsyncAppContext,
|
serde_json, AnyHandle, AnyWeakHandle, AnyWindowHandle, AppContext, AsyncAppContext, Handle,
|
||||||
AsyncWindowContext, Handle, SemanticVersion, Task, ViewContext, WeakHandle, WindowId,
|
SemanticVersion, Task, ViewContext,
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -240,7 +239,7 @@ struct ClientState {
|
||||||
Box<dyn AnyTypedEnvelope>,
|
Box<dyn AnyTypedEnvelope>,
|
||||||
&Arc<Client>,
|
&Arc<Client>,
|
||||||
AsyncAppContext,
|
AsyncAppContext,
|
||||||
) -> LocalBoxFuture<'static, Result<()>>,
|
) -> BoxFuture<'static, Result<()>>,
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
@ -248,7 +247,7 @@ struct ClientState {
|
||||||
enum WeakSubscriber {
|
enum WeakSubscriber {
|
||||||
Entity {
|
Entity {
|
||||||
handle: AnyWeakHandle,
|
handle: AnyWeakHandle,
|
||||||
window_id: Option<WindowId>,
|
window_handle: Option<AnyWindowHandle>,
|
||||||
},
|
},
|
||||||
Pending(Vec<Box<dyn AnyTypedEnvelope>>),
|
Pending(Vec<Box<dyn AnyTypedEnvelope>>),
|
||||||
}
|
}
|
||||||
|
@ -336,7 +335,7 @@ where
|
||||||
id,
|
id,
|
||||||
WeakSubscriber::Entity {
|
WeakSubscriber::Entity {
|
||||||
handle: model.downgrade().into(),
|
handle: model.downgrade().into(),
|
||||||
window_id: None,
|
window_handle: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
drop(state);
|
drop(state);
|
||||||
|
@ -511,7 +510,7 @@ impl Client {
|
||||||
},
|
},
|
||||||
&cx,
|
&cx,
|
||||||
);
|
);
|
||||||
cx.background().timer(delay).await;
|
cx.executor().timer(delay).await;
|
||||||
delay = delay
|
delay = delay
|
||||||
.mul_f32(rng.gen_range(1.0..=2.0))
|
.mul_f32(rng.gen_range(1.0..=2.0))
|
||||||
.min(reconnect_interval);
|
.min(reconnect_interval);
|
||||||
|
@ -522,7 +521,7 @@ impl Client {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Status::SignedOut | Status::UpgradeRequired => {
|
Status::SignedOut | Status::UpgradeRequired => {
|
||||||
cx.read(|cx| self.telemetry.set_authenticated_user_info(None, false, cx));
|
cx.update(|cx| self.telemetry.set_authenticated_user_info(None, false, cx));
|
||||||
state._reconnect_task.take();
|
state._reconnect_task.take();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -533,13 +532,16 @@ impl Client {
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
remote_id: u64,
|
remote_id: u64,
|
||||||
cx: &mut ViewContext<T>,
|
cx: &mut ViewContext<T>,
|
||||||
) -> Subscription {
|
) -> Subscription
|
||||||
|
where
|
||||||
|
T: 'static + Send + Sync,
|
||||||
|
{
|
||||||
let id = (TypeId::of::<T>(), remote_id);
|
let id = (TypeId::of::<T>(), remote_id);
|
||||||
self.state.write().entities_by_type_and_remote_id.insert(
|
self.state.write().entities_by_type_and_remote_id.insert(
|
||||||
id,
|
id,
|
||||||
WeakSubscriber::Entity {
|
WeakSubscriber::Entity {
|
||||||
handle: cx.handle().into_any(),
|
handle: cx.handle().into(),
|
||||||
window_id: Some(cx.window_id()),
|
window_handle: Some(cx.window_handle()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Subscription::Entity {
|
Subscription::Entity {
|
||||||
|
@ -573,33 +575,28 @@ impl Client {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn add_message_handler<M, E, H, F>(
|
pub fn add_message_handler<M, E, H, F>(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
model: Handle<E>,
|
entity: Handle<E>,
|
||||||
handler: H,
|
handler: H,
|
||||||
) -> Subscription
|
) -> Subscription
|
||||||
where
|
where
|
||||||
M: EnvelopedMessage,
|
M: EnvelopedMessage,
|
||||||
E: 'static + Send + Sync,
|
E: 'static + Send + Sync,
|
||||||
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||||
F: 'static + Future<Output = Result<()>>,
|
F: 'static + Future<Output = Result<()>> + Send,
|
||||||
{
|
{
|
||||||
let message_type_id = TypeId::of::<M>();
|
let message_type_id = TypeId::of::<M>();
|
||||||
|
|
||||||
let mut state = self.state.write();
|
let mut state = self.state.write();
|
||||||
state
|
state
|
||||||
.models_by_message_type
|
.models_by_message_type
|
||||||
.insert(message_type_id, model.downgrade().into_any());
|
.insert(message_type_id, entity.downgrade().into());
|
||||||
|
|
||||||
let prev_handler = state.message_handlers.insert(
|
let prev_handler = state.message_handlers.insert(
|
||||||
message_type_id,
|
message_type_id,
|
||||||
Arc::new(move |handle, envelope, client, cx| {
|
Arc::new(move |subscriber, envelope, client, cx| {
|
||||||
let handle = if let Subscriber::Model(handle) = handle {
|
let subscriber = subscriber.handle.downcast::<E>().unwrap();
|
||||||
handle
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
let model = handle.downcast::<E>().unwrap();
|
|
||||||
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
||||||
handler(model, *envelope, client.clone(), cx).boxed_local()
|
handler(subscriber, *envelope, client.clone(), cx).boxed()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if prev_handler.is_some() {
|
if prev_handler.is_some() {
|
||||||
|
@ -627,7 +624,7 @@ impl Client {
|
||||||
M: RequestMessage,
|
M: RequestMessage,
|
||||||
E: 'static + Send + Sync,
|
E: 'static + Send + Sync,
|
||||||
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||||
F: 'static + Future<Output = Result<M::Response>>,
|
F: 'static + Future<Output = Result<M::Response>> + Send,
|
||||||
{
|
{
|
||||||
self.add_message_handler(model, move |handle, envelope, this, cx| {
|
self.add_message_handler(model, move |handle, envelope, this, cx| {
|
||||||
Self::respond_to_request(
|
Self::respond_to_request(
|
||||||
|
@ -638,37 +635,12 @@ impl Client {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
|
||||||
where
|
|
||||||
M: EntityMessage,
|
|
||||||
H: 'static
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ Fn(WeakHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncWindowContext) -> F,
|
|
||||||
F: 'static + Future<Output = Result<()>>,
|
|
||||||
{
|
|
||||||
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
|
|
||||||
if let Some(window_handle) = subscriber.window_handle {
|
|
||||||
cx.update_window(subscriber, |cx| {
|
|
||||||
handler(
|
|
||||||
subscriber.handle.downcast::<E>().unwrap(),
|
|
||||||
message,
|
|
||||||
client,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_model_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
pub fn add_model_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
||||||
where
|
where
|
||||||
M: EntityMessage,
|
M: EntityMessage,
|
||||||
E: 'static + Send + Sync,
|
E: 'static + Send + Sync,
|
||||||
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||||
F: 'static + Future<Output = Result<()>>,
|
F: 'static + Future<Output = Result<()>> + Send,
|
||||||
{
|
{
|
||||||
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
|
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
|
||||||
handler(
|
handler(
|
||||||
|
@ -687,7 +659,7 @@ impl Client {
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync
|
+ Sync
|
||||||
+ Fn(Subscriber, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
+ Fn(Subscriber, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||||
F: 'static + Future<Output = Result<()>>,
|
F: 'static + Future<Output = Result<()>> + Send,
|
||||||
{
|
{
|
||||||
let model_type_id = TypeId::of::<E>();
|
let model_type_id = TypeId::of::<E>();
|
||||||
let message_type_id = TypeId::of::<M>();
|
let message_type_id = TypeId::of::<M>();
|
||||||
|
@ -713,7 +685,7 @@ impl Client {
|
||||||
message_type_id,
|
message_type_id,
|
||||||
Arc::new(move |handle, envelope, client, cx| {
|
Arc::new(move |handle, envelope, client, cx| {
|
||||||
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
||||||
handler(handle, *envelope, client.clone(), cx).boxed_local()
|
handler(handle, *envelope, client.clone(), cx).boxed()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if prev_handler.is_some() {
|
if prev_handler.is_some() {
|
||||||
|
@ -726,7 +698,7 @@ impl Client {
|
||||||
M: EntityMessage + RequestMessage,
|
M: EntityMessage + RequestMessage,
|
||||||
E: 'static + Send + Sync,
|
E: 'static + Send + Sync,
|
||||||
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
|
||||||
F: 'static + Future<Output = Result<M::Response>>,
|
F: 'static + Future<Output = Result<M::Response>> + Send,
|
||||||
{
|
{
|
||||||
self.add_model_message_handler(move |entity, envelope, client, cx| {
|
self.add_model_message_handler(move |entity, envelope, client, cx| {
|
||||||
Self::respond_to_request::<M, _>(
|
Self::respond_to_request::<M, _>(
|
||||||
|
@ -737,25 +709,6 @@ impl Client {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view_request_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
|
|
||||||
where
|
|
||||||
M: EntityMessage + RequestMessage,
|
|
||||||
E: 'static + Send + Sync,
|
|
||||||
H: 'static
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncWindowContext) -> F,
|
|
||||||
F: 'static + Future<Output = Result<M::Response>>,
|
|
||||||
{
|
|
||||||
self.add_view_message_handler(move |entity, envelope, client, cx| {
|
|
||||||
Self::respond_to_request::<M, _>(
|
|
||||||
envelope.receipt(),
|
|
||||||
handler(entity, envelope, client.clone(), cx),
|
|
||||||
client,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn respond_to_request<T: RequestMessage, F: Future<Output = Result<T::Response>>>(
|
async fn respond_to_request<T: RequestMessage, F: Future<Output = Result<T::Response>>>(
|
||||||
receipt: Receipt<T>,
|
receipt: Receipt<T>,
|
||||||
response: F,
|
response: F,
|
||||||
|
@ -778,11 +731,11 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
|
pub async fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
|
||||||
read_credentials_from_keychain(cx).is_some()
|
read_credentials_from_keychain(cx).await.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_recursion(?Send)]
|
#[async_recursion]
|
||||||
pub async fn authenticate_and_connect(
|
pub async fn authenticate_and_connect(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
try_keychain: bool,
|
try_keychain: bool,
|
||||||
|
@ -840,7 +793,7 @@ impl Client {
|
||||||
self.set_status(Status::Reconnecting, cx);
|
self.set_status(Status::Reconnecting, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut timeout = futures::FutureExt::fuse(cx.executor()?.timer(CONNECTION_TIMEOUT));
|
let mut timeout = futures::FutureExt::fuse(cx.executor().timer(CONNECTION_TIMEOUT));
|
||||||
futures::select_biased! {
|
futures::select_biased! {
|
||||||
connection = self.establish_connection(&credentials, cx).fuse() => {
|
connection = self.establish_connection(&credentials, cx).fuse() => {
|
||||||
match connection {
|
match connection {
|
||||||
|
@ -891,7 +844,7 @@ impl Client {
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
cx: &AsyncAppContext,
|
cx: &AsyncAppContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let executor = cx.executor()?;
|
let executor = cx.executor();
|
||||||
log::info!("add connection to peer");
|
log::info!("add connection to peer");
|
||||||
let (connection_id, handle_io, mut incoming) = self.peer.add_connection(conn, {
|
let (connection_id, handle_io, mut incoming) = self.peer.add_connection(conn, {
|
||||||
let executor = executor.clone();
|
let executor = executor.clone();
|
||||||
|
@ -955,7 +908,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})?
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.spawn({
|
cx.spawn({
|
||||||
|
@ -978,7 +931,8 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})?;
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1042,13 +996,8 @@ impl Client {
|
||||||
credentials: &Credentials,
|
credentials: &Credentials,
|
||||||
cx: &AsyncAppContext,
|
cx: &AsyncAppContext,
|
||||||
) -> Task<Result<Connection, EstablishConnectionError>> {
|
) -> Task<Result<Connection, EstablishConnectionError>> {
|
||||||
let executor = match cx.executor() {
|
|
||||||
Ok(executor) => executor,
|
|
||||||
Err(error) => return Task::ready(Err(error)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let use_preview_server = cx
|
let use_preview_server = cx
|
||||||
.try_read_global(|channel: &ReleaseChannel, _| channel != ReleaseChannel::Stable)
|
.try_read_global(|channel: &ReleaseChannel, _| *channel != ReleaseChannel::Stable)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
let request = Request::builder()
|
let request = Request::builder()
|
||||||
|
@ -1059,7 +1008,7 @@ impl Client {
|
||||||
.header("x-zed-protocol-version", rpc::PROTOCOL_VERSION);
|
.header("x-zed-protocol-version", rpc::PROTOCOL_VERSION);
|
||||||
|
|
||||||
let http = self.http.clone();
|
let http = self.http.clone();
|
||||||
executor.spawn(async move {
|
cx.executor().spawn(async move {
|
||||||
let mut rpc_url = Self::get_rpc_url(http, use_preview_server).await?;
|
let mut rpc_url = Self::get_rpc_url(http, use_preview_server).await?;
|
||||||
let rpc_host = rpc_url
|
let rpc_host = rpc_url
|
||||||
.host_str()
|
.host_str()
|
||||||
|
@ -1130,15 +1079,15 @@ impl Client {
|
||||||
write!(&mut url, "&impersonate={}", impersonate_login).unwrap();
|
write!(&mut url, "&impersonate={}", impersonate_login).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
platform.open_url(&url);
|
cx.run_on_main(|cx| cx.open_url(&url))?.await;
|
||||||
|
|
||||||
// Receive the HTTP request from the user's browser. Retrieve the user id and encrypted
|
// Receive the HTTP request from the user's browser. Retrieve the user id and encrypted
|
||||||
// access token from the query params.
|
// access token from the query params.
|
||||||
//
|
//
|
||||||
// TODO - Avoid ever starting more than one HTTP server. Maybe switch to using a
|
// TODO - Avoid ever starting more than one HTTP server. Maybe switch to using a
|
||||||
// custom URL scheme instead of this local HTTP server.
|
// custom URL scheme instead of this local HTTP server.
|
||||||
let (user_id, access_token) = executor
|
let (user_id, access_token) = cx
|
||||||
.spawn(async move {
|
.spawn(|_| async move {
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
if let Some(req) = server.recv_timeout(Duration::from_secs(1))? {
|
if let Some(req) = server.recv_timeout(Duration::from_secs(1))? {
|
||||||
let path = req.url();
|
let path = req.url();
|
||||||
|
@ -1181,14 +1130,13 @@ impl Client {
|
||||||
let access_token = private_key
|
let access_token = private_key
|
||||||
.decrypt_string(&access_token)
|
.decrypt_string(&access_token)
|
||||||
.context("failed to decrypt access token")?;
|
.context("failed to decrypt access token")?;
|
||||||
platform.activate(true);
|
cx.run_on_main(|cx| cx.activate(true))?.await;
|
||||||
|
|
||||||
Ok(Credentials {
|
Ok(Credentials {
|
||||||
user_id: user_id.parse()?,
|
user_id: user_id.parse()?,
|
||||||
access_token,
|
access_token,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|error| Task::ready(Err(error)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn authenticate_as_admin(
|
async fn authenticate_as_admin(
|
||||||
|
@ -1344,12 +1292,16 @@ impl Client {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(weak_subscriber @ _) => match weak_subscriber {
|
Some(weak_subscriber @ _) => match weak_subscriber {
|
||||||
WeakSubscriber::Model(handle) => {
|
WeakSubscriber::Entity {
|
||||||
subscriber = handle.upgrade(cx).map(Subscriber::Model);
|
handle,
|
||||||
}
|
window_handle,
|
||||||
WeakSubscriber::View(handle) => {
|
} => {
|
||||||
subscriber = Some(Subscriber::View(handle.clone()));
|
subscriber = handle.upgrade().map(|handle| Subscriber {
|
||||||
|
handle,
|
||||||
|
window_handle: window_handle.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
WeakSubscriber::Pending(_) => {}
|
WeakSubscriber::Pending(_) => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1379,8 +1331,7 @@ impl Client {
|
||||||
sender_id,
|
sender_id,
|
||||||
type_name
|
type_name
|
||||||
);
|
);
|
||||||
cx.foreground()
|
cx.spawn_on_main(|_| async move {
|
||||||
.spawn(async move {
|
|
||||||
match future.await {
|
match future.await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
||||||
use gpui2::{serde_json, AppContext, Executor, Task};
|
use gpui2::{serde_json, AppContext, AppMetadata, Executor, Task};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -15,15 +15,12 @@ pub struct Telemetry {
|
||||||
state: Mutex<TelemetryState>,
|
state: Mutex<TelemetryState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct TelemetryState {
|
struct TelemetryState {
|
||||||
metrics_id: Option<Arc<str>>, // Per logged-in user
|
metrics_id: Option<Arc<str>>, // Per logged-in user
|
||||||
installation_id: Option<Arc<str>>, // Per app installation (different for dev, preview, and stable)
|
installation_id: Option<Arc<str>>, // Per app installation (different for dev, preview, and stable)
|
||||||
session_id: Option<Arc<str>>, // Per app launch
|
session_id: Option<Arc<str>>, // Per app launch
|
||||||
app_version: Option<Arc<str>>,
|
|
||||||
release_channel: Option<&'static str>,
|
release_channel: Option<&'static str>,
|
||||||
os_name: &'static str,
|
app_metadata: AppMetadata,
|
||||||
os_version: Option<Arc<str>>,
|
|
||||||
architecture: &'static str,
|
architecture: &'static str,
|
||||||
clickhouse_events_queue: Vec<ClickhouseEventWrapper>,
|
clickhouse_events_queue: Vec<ClickhouseEventWrapper>,
|
||||||
flush_clickhouse_events_task: Option<Task<()>>,
|
flush_clickhouse_events_task: Option<Task<()>>,
|
||||||
|
@ -115,7 +112,6 @@ const DEBOUNCE_INTERVAL: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
impl Telemetry {
|
impl Telemetry {
|
||||||
pub fn new(client: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> {
|
pub fn new(client: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> {
|
||||||
let platform = cx.platform();
|
|
||||||
let release_channel = if cx.has_global::<ReleaseChannel>() {
|
let release_channel = if cx.has_global::<ReleaseChannel>() {
|
||||||
Some(cx.global::<ReleaseChannel>().display_name())
|
Some(cx.global::<ReleaseChannel>().display_name())
|
||||||
} else {
|
} else {
|
||||||
|
@ -124,12 +120,10 @@ impl Telemetry {
|
||||||
// TODO: Replace all hardware stuff with nested SystemSpecs json
|
// TODO: Replace all hardware stuff with nested SystemSpecs json
|
||||||
let this = Arc::new(Self {
|
let this = Arc::new(Self {
|
||||||
http_client: client,
|
http_client: client,
|
||||||
executor: cx.background().clone(),
|
executor: cx.executor().clone(),
|
||||||
state: Mutex::new(TelemetryState {
|
state: Mutex::new(TelemetryState {
|
||||||
os_name: platform.os_name().into(),
|
app_metadata: cx.app_metadata(),
|
||||||
os_version: platform.os_version().ok().map(|v| v.to_string().into()),
|
|
||||||
architecture: env::consts::ARCH,
|
architecture: env::consts::ARCH,
|
||||||
app_version: platform.app_version().ok().map(|v| v.to_string().into()),
|
|
||||||
release_channel,
|
release_channel,
|
||||||
installation_id: None,
|
installation_id: None,
|
||||||
metrics_id: None,
|
metrics_id: None,
|
||||||
|
@ -196,7 +190,7 @@ impl Telemetry {
|
||||||
core_count: system.cpus().len() as u32,
|
core_count: system.cpus().len() as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
let telemetry_settings = cx.update(|cx| *settings::get::<TelemetrySettings>(cx));
|
let telemetry_settings = cx.update(|cx| *settings2::get::<TelemetrySettings>(cx));
|
||||||
|
|
||||||
this.report_clickhouse_event(memory_event, telemetry_settings);
|
this.report_clickhouse_event(memory_event, telemetry_settings);
|
||||||
this.report_clickhouse_event(cpu_event, telemetry_settings);
|
this.report_clickhouse_event(cpu_event, telemetry_settings);
|
||||||
|
@ -211,7 +205,7 @@ impl Telemetry {
|
||||||
is_staff: bool,
|
is_staff: bool,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) {
|
) {
|
||||||
if !settings::get::<TelemetrySettings>(cx).metrics {
|
if !settings2::get::<TelemetrySettings>(cx).metrics {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ use refineable::Refineable;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
current_platform, image_cache::ImageCache, Action, AssetSource, Context, DispatchPhase,
|
current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context,
|
||||||
DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId,
|
DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap,
|
||||||
MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet,
|
LayoutId, MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet,
|
||||||
SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
|
SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
|
||||||
WindowHandle, WindowId,
|
WindowHandle, WindowId,
|
||||||
};
|
};
|
||||||
|
@ -49,13 +49,25 @@ impl App {
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<dyn HttpClient>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let executor = platform.executor();
|
let executor = platform.executor();
|
||||||
|
assert!(
|
||||||
|
executor.is_main_thread(),
|
||||||
|
"must construct App on main thread"
|
||||||
|
);
|
||||||
|
|
||||||
|
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||||
let entities = EntityMap::new();
|
let entities = EntityMap::new();
|
||||||
let unit_entity = entities.insert(entities.reserve(), ());
|
let unit_entity = entities.insert(entities.reserve(), ());
|
||||||
|
let app_metadata = AppMetadata {
|
||||||
|
os_name: platform.os_name(),
|
||||||
|
os_version: platform.os_version().unwrap_or_default(),
|
||||||
|
app_version: platform.app_version().unwrap_or_default(),
|
||||||
|
};
|
||||||
Self(Arc::new_cyclic(|this| {
|
Self(Arc::new_cyclic(|this| {
|
||||||
Mutex::new(AppContext {
|
Mutex::new(AppContext {
|
||||||
this: this.clone(),
|
this: this.clone(),
|
||||||
text_system: Arc::new(TextSystem::new(platform.text_system())),
|
text_system,
|
||||||
platform: MainThreadOnly::new(platform, executor.clone()),
|
platform: MainThreadOnly::new(platform, executor.clone()),
|
||||||
|
app_metadata,
|
||||||
flushing_effects: false,
|
flushing_effects: false,
|
||||||
pending_updates: 0,
|
pending_updates: 0,
|
||||||
next_frame_callbacks: Default::default(),
|
next_frame_callbacks: Default::default(),
|
||||||
|
@ -128,16 +140,8 @@ impl App {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app_version(&self) -> Result<SemanticVersion> {
|
pub fn metadata(&self) -> AppMetadata {
|
||||||
self.0.lock().platform.borrow_on_main_thread().app_version()
|
self.0.lock().app_metadata.clone()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn os_name(&self) -> &'static str {
|
|
||||||
self.0.lock().platform.borrow_on_main_thread().os_name()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn os_version(&self) -> Result<SemanticVersion> {
|
|
||||||
self.0.lock().platform.borrow_on_main_thread().os_version()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn executor(&self) -> Executor {
|
pub fn executor(&self) -> Executor {
|
||||||
|
@ -158,6 +162,7 @@ type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<d
|
||||||
pub struct AppContext {
|
pub struct AppContext {
|
||||||
this: Weak<Mutex<AppContext>>,
|
this: Weak<Mutex<AppContext>>,
|
||||||
pub(crate) platform: MainThreadOnly<dyn Platform>,
|
pub(crate) platform: MainThreadOnly<dyn Platform>,
|
||||||
|
app_metadata: AppMetadata,
|
||||||
text_system: Arc<TextSystem>,
|
text_system: Arc<TextSystem>,
|
||||||
flushing_effects: bool,
|
flushing_effects: bool,
|
||||||
pending_updates: usize,
|
pending_updates: usize,
|
||||||
|
@ -184,6 +189,10 @@ pub struct AppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppContext {
|
impl AppContext {
|
||||||
|
pub fn app_metadata(&self) -> AppMetadata {
|
||||||
|
self.app_metadata.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
self.push_effect(Effect::Refresh);
|
self.push_effect(Effect::Refresh);
|
||||||
}
|
}
|
||||||
|
@ -379,7 +388,10 @@ impl AppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_async(&self) -> AsyncAppContext {
|
pub fn to_async(&self) -> AsyncAppContext {
|
||||||
AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
|
AsyncAppContext {
|
||||||
|
app: unsafe { mem::transmute(self.this.clone()) },
|
||||||
|
executor: self.executor.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn executor(&self) -> &Executor {
|
pub fn executor(&self) -> &Executor {
|
||||||
|
@ -639,7 +651,7 @@ impl MainThread<AppContext> {
|
||||||
self.platform.borrow_on_main_thread()
|
self.platform.borrow_on_main_thread()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(&mut self, ignoring_other_apps: bool) {
|
pub fn activate(&self, ignoring_other_apps: bool) {
|
||||||
self.platform().activate(ignoring_other_apps);
|
self.platform().activate(ignoring_other_apps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,6 +667,10 @@ impl MainThread<AppContext> {
|
||||||
self.platform().delete_credentials(url)
|
self.platform().delete_credentials(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_url(&self, url: &str) {
|
||||||
|
self.platform().open_url(url);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open_window<S: 'static + Send + Sync>(
|
pub fn open_window<S: 'static + Send + Sync>(
|
||||||
&mut self,
|
&mut self,
|
||||||
options: crate::WindowOptions,
|
options: crate::WindowOptions,
|
||||||
|
|
|
@ -8,7 +8,10 @@ use parking_lot::Mutex;
|
||||||
use std::{future::Future, sync::Weak};
|
use std::{future::Future, sync::Weak};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>);
|
pub struct AsyncAppContext {
|
||||||
|
pub(crate) app: Weak<Mutex<AppContext>>,
|
||||||
|
pub(crate) executor: Executor,
|
||||||
|
}
|
||||||
|
|
||||||
impl Context for AsyncAppContext {
|
impl Context for AsyncAppContext {
|
||||||
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
|
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
|
||||||
|
@ -19,7 +22,7 @@ impl Context for AsyncAppContext {
|
||||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
||||||
) -> Self::Result<Handle<T>> {
|
) -> Self::Result<Handle<T>> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut lock = app.lock(); // Need this to compile
|
let mut lock = app.lock(); // Need this to compile
|
||||||
|
@ -32,7 +35,7 @@ impl Context for AsyncAppContext {
|
||||||
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
||||||
) -> Self::Result<R> {
|
) -> Self::Result<R> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut lock = app.lock(); // Need this to compile
|
let mut lock = app.lock(); // Need this to compile
|
||||||
|
@ -43,7 +46,7 @@ impl Context for AsyncAppContext {
|
||||||
impl AsyncAppContext {
|
impl AsyncAppContext {
|
||||||
pub fn refresh(&mut self) -> Result<()> {
|
pub fn refresh(&mut self) -> Result<()> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut lock = app.lock(); // Need this to compile
|
let mut lock = app.lock(); // Need this to compile
|
||||||
|
@ -51,13 +54,17 @@ impl AsyncAppContext {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn executor(&self) -> Result<Executor> {
|
pub fn executor(&self) -> &Executor {
|
||||||
|
&self.executor
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let lock = app.lock(); // Need this to compile
|
let mut lock = app.lock();
|
||||||
Ok(lock.executor().clone())
|
Ok(f(&mut *lock))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_window<R>(
|
pub fn read_window<R>(
|
||||||
|
@ -66,7 +73,7 @@ impl AsyncAppContext {
|
||||||
update: impl FnOnce(&WindowContext) -> R,
|
update: impl FnOnce(&WindowContext) -> R,
|
||||||
) -> Result<R> {
|
) -> Result<R> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut app_context = app.lock();
|
let mut app_context = app.lock();
|
||||||
|
@ -79,27 +86,32 @@ impl AsyncAppContext {
|
||||||
update: impl FnOnce(&mut WindowContext) -> R,
|
update: impl FnOnce(&mut WindowContext) -> R,
|
||||||
) -> Result<R> {
|
) -> Result<R> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut app_context = app.lock();
|
let mut app_context = app.lock();
|
||||||
app_context.update_window(handle.id, update)
|
app_context.update_window(handle.id, update)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn<Fut, R>(
|
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
|
||||||
&self,
|
|
||||||
f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
|
|
||||||
) -> Result<Task<R>>
|
|
||||||
where
|
where
|
||||||
Fut: Future<Output = R> + Send + 'static,
|
Fut: Future<Output = R> + Send + 'static,
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
{
|
{
|
||||||
let app = self
|
let this = self.clone();
|
||||||
.0
|
self.executor.spawn(async move { f(this).await })
|
||||||
.upgrade()
|
}
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
|
||||||
let app_context = app.lock();
|
pub fn spawn_on_main<Fut, R>(
|
||||||
Ok(app_context.spawn(f))
|
&self,
|
||||||
|
f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
|
||||||
|
) -> Task<R>
|
||||||
|
where
|
||||||
|
Fut: Future<Output = R> + 'static,
|
||||||
|
R: Send + 'static,
|
||||||
|
{
|
||||||
|
let this = self.clone();
|
||||||
|
self.executor.spawn_on_main(|| f(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_on_main<R>(
|
pub fn run_on_main<R>(
|
||||||
|
@ -110,7 +122,7 @@ impl AsyncAppContext {
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
{
|
{
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut app_context = app.lock();
|
let mut app_context = app.lock();
|
||||||
|
@ -119,7 +131,7 @@ impl AsyncAppContext {
|
||||||
|
|
||||||
pub fn has_global<G: 'static + Send + Sync>(&self) -> Result<bool> {
|
pub fn has_global<G: 'static + Send + Sync>(&self) -> Result<bool> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let lock = app.lock(); // Need this to compile
|
let lock = app.lock(); // Need this to compile
|
||||||
|
@ -131,7 +143,7 @@ impl AsyncAppContext {
|
||||||
read: impl FnOnce(&G, &AppContext) -> R,
|
read: impl FnOnce(&G, &AppContext) -> R,
|
||||||
) -> Result<R> {
|
) -> Result<R> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let lock = app.lock(); // Need this to compile
|
let lock = app.lock(); // Need this to compile
|
||||||
|
@ -142,7 +154,7 @@ impl AsyncAppContext {
|
||||||
&self,
|
&self,
|
||||||
read: impl FnOnce(&G, &AppContext) -> R,
|
read: impl FnOnce(&G, &AppContext) -> R,
|
||||||
) -> Option<R> {
|
) -> Option<R> {
|
||||||
let app = self.0.upgrade()?;
|
let app = self.app.upgrade()?;
|
||||||
let lock = app.lock(); // Need this to compile
|
let lock = app.lock(); // Need this to compile
|
||||||
Some(read(lock.try_global()?, &lock))
|
Some(read(lock.try_global()?, &lock))
|
||||||
}
|
}
|
||||||
|
@ -152,7 +164,7 @@ impl AsyncAppContext {
|
||||||
update: impl FnOnce(&mut G, &mut AppContext) -> R,
|
update: impl FnOnce(&mut G, &mut AppContext) -> R,
|
||||||
) -> Result<R> {
|
) -> Result<R> {
|
||||||
let app = self
|
let app = self
|
||||||
.0
|
.app
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("app was released"))?;
|
.ok_or_else(|| anyhow!("app was released"))?;
|
||||||
let mut lock = app.lock(); // Need this to compile
|
let mut lock = app.lock(); // Need this to compile
|
||||||
|
|
|
@ -181,6 +181,13 @@ pub trait PlatformTextSystem: Send + Sync {
|
||||||
) -> Vec<usize>;
|
) -> Vec<usize>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AppMetadata {
|
||||||
|
pub os_name: &'static str,
|
||||||
|
pub os_version: SemanticVersion,
|
||||||
|
pub app_version: SemanticVersion,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
pub enum AtlasKey {
|
pub enum AtlasKey {
|
||||||
Glyph(RenderGlyphParams),
|
Glyph(RenderGlyphParams),
|
||||||
|
@ -404,7 +411,7 @@ impl Default for CursorStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct SemanticVersion {
|
pub struct SemanticVersion {
|
||||||
major: usize,
|
major: usize,
|
||||||
minor: usize,
|
minor: usize,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue