@@ -127,6 +127,12 @@ pub enum Status {
ReconnectionError { next_reconnection: Instant },
}
+impl Status {
+ pub fn is_connected(&self) -> bool {
+ matches!(self, Self::Connected { .. })
+ }
+}
+
struct ClientState {
credentials: Option<Credentials>,
status: (watch::Sender<Status>, watch::Receiver<Status>),
@@ -92,6 +92,7 @@ enum ProjectClientState {
sharing_has_stopped: bool,
remote_id: u64,
replica_id: ReplicaId,
+ _detect_unshare_task: Task<Option<()>>,
},
}
@@ -244,7 +245,7 @@ impl Project {
let mut status = rpc.status();
while let Some(status) = status.next().await {
if let Some(this) = this.upgrade(&cx) {
- let remote_id = if let client::Status::Connected { .. } = status {
+ let remote_id = if status.is_connected() {
let response = rpc.request(proto::RegisterProject {}).await?;
Some(response.project_id)
} else {
@@ -333,7 +334,7 @@ impl Project {
}
let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
- let this = cx.add_model(|cx| {
+ let this = cx.add_model(|cx: &mut ModelContext<Self>| {
let mut this = Self {
worktrees: Vec::new(),
loading_buffers: Default::default(),
@@ -346,11 +347,26 @@ impl Project {
user_store: user_store.clone(),
fs,
subscriptions: vec![client.add_model_for_remote_entity(remote_id, cx)],
- client,
+ client: client.clone(),
client_state: ProjectClientState::Remote {
sharing_has_stopped: false,
remote_id,
replica_id,
+ _detect_unshare_task: cx.spawn_weak(move |this, mut cx| {
+ async move {
+ let mut status = client.status();
+ let is_connected =
+ status.next().await.map_or(false, |s| s.is_connected());
+ // Even if we're initially connected, any future change of the status means we momentarily disconnected.
+ if !is_connected || status.next().await.is_some() {
+ if let Some(this) = this.upgrade(&cx) {
+ this.update(&mut cx, |this, cx| this.project_unshared(cx))
+ }
+ }
+ Ok(())
+ }
+ .log_err()
+ }),
},
language_servers_with_diagnostics_running: 0,
language_servers: Default::default(),
@@ -666,6 +682,18 @@ impl Project {
})
}
+ fn project_unshared(&mut self, cx: &mut ModelContext<Self>) {
+ if let ProjectClientState::Remote {
+ sharing_has_stopped,
+ ..
+ } = &mut self.client_state
+ {
+ *sharing_has_stopped = true;
+ self.collaborators.clear();
+ cx.notify();
+ }
+ }
+
pub fn is_read_only(&self) -> bool {
match &self.client_state {
ProjectClientState::Local { .. } => false,
@@ -2628,20 +2656,7 @@ impl Project {
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
- this.update(&mut cx, |this, cx| {
- if let ProjectClientState::Remote {
- sharing_has_stopped,
- ..
- } = &mut this.client_state
- {
- *sharing_has_stopped = true;
- this.collaborators.clear();
- cx.notify();
- } else {
- unreachable!()
- }
- });
-
+ this.update(&mut cx, |this, cx| this.project_unshared(cx));
Ok(())
}
@@ -39,6 +39,7 @@ pub struct Workspace {
pub right_sidebar: Sidebar,
pub status_bar: StatusBar,
pub toolbar: Toolbar,
+ pub disconnected_overlay: ContainedText,
}
#[derive(Clone, Deserialize, Default)]
@@ -1297,6 +1297,24 @@ impl Workspace {
None
}
}
+
+ fn render_disconnected_overlay(&self, cx: &AppContext) -> Option<ElementBox> {
+ if self.project.read(cx).is_read_only() {
+ let theme = &self.settings.borrow().theme;
+ Some(
+ Label::new(
+ "Your connection to the remote project has been lost.".to_string(),
+ theme.workspace.disconnected_overlay.text.clone(),
+ )
+ .aligned()
+ .contained()
+ .with_style(theme.workspace.disconnected_overlay.container)
+ .boxed(),
+ )
+ } else {
+ None
+ }
+ }
}
impl Entity for Workspace {
@@ -1339,6 +1357,7 @@ impl View for Workspace {
content.boxed()
})
.with_children(self.modal.as_ref().map(|m| ChildView::new(m).boxed()))
+ .with_children(self.render_disconnected_overlay(cx))
.flexible(1.0, true)
.boxed(),
)
@@ -60,3 +60,8 @@ emphasis = "#4ec9b0"
link_uri = { color = "#6a9955", underline = true }
link_text = { color = "#cb8f77", italic = true }
list_marker = "#4e94ce"
+
+[workspace.disconnected_overlay]
+extends = "$text.base"
+color = "#ffffff"
+background = "#000000aa"
@@ -60,3 +60,8 @@ emphasis = "#4ec9b0"
link_uri = { color = "#6a9955", underline = true }
link_text = { color = "#cb8f77", italic = true }
list_marker = "#4e94ce"
+
+[workspace.disconnected_overlay]
+extends = "$text.base"
+color = "#ffffff"
+background = "#000000aa"
@@ -60,3 +60,8 @@ emphasis = "#267f29"
link_uri = { color = "#6a9955", underline = true }
link_text = { color = "#a82121", italic = true }
list_marker = "#4e94ce"
+
+[workspace.disconnected_overlay]
+extends = "$text.base"
+color = "#ffffff"
+background = "#000000cc"