214 lines · 7.3 KB · Rust
RawDownload
use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, Focusable, Render, WeakEntity};use project::project_settings::ProjectSettings;use remote::RemoteConnectionOptions;use settings::Settings;use ui::{ElevationIndex, Modal, ModalFooter, ModalHeader, Section, prelude::*};use workspace::{    ModalView, MultiWorkspace, OpenOptions, Workspace, notifications::DetachAndPromptErr,}; use crate::open_remote_project; enum Host {    CollabGuestProject,    RemoteServerProject(RemoteConnectionOptions, bool),} pub struct DisconnectedOverlay {    workspace: WeakEntity<Workspace>,    host: Host,    focus_handle: FocusHandle,    finished: bool,} impl EventEmitter<DismissEvent> for DisconnectedOverlay {}impl Focusable for DisconnectedOverlay {    fn focus_handle(&self, _cx: &gpui::App) -> gpui::FocusHandle {        self.focus_handle.clone()    }}impl ModalView for DisconnectedOverlay {    fn on_before_dismiss(        &mut self,        _window: &mut Window,        _: &mut Context<Self>,    ) -> workspace::DismissDecision {        workspace::DismissDecision::Dismiss(self.finished)    }    fn fade_out_background(&self) -> bool {        true    }} impl DisconnectedOverlay {    pub fn register(        workspace: &mut Workspace,        window: Option<&mut Window>,        cx: &mut Context<Workspace>,    ) {        let Some(window) = window else {            return;        };        cx.subscribe_in(            workspace.project(),            window,            |workspace, project, event, window, cx| {                if !matches!(                    event,                    project::Event::DisconnectedFromHost                        | project::Event::DisconnectedFromRemote { .. }                ) {                    return;                }                let handle = cx.entity().downgrade();                 let remote_connection_options = project.read(cx).remote_connection_options(cx);                let host = if let Some(remote_connection_options) = remote_connection_options {                    Host::RemoteServerProject(                        remote_connection_options,                        matches!(                            event,                            project::Event::DisconnectedFromRemote {                                server_not_running: true                            }                        ),                    )                } else {                    Host::CollabGuestProject                };                 workspace.toggle_modal(window, cx, |_, cx| DisconnectedOverlay {                    finished: false,                    workspace: handle,                    host,                    focus_handle: cx.focus_handle(),                });            },        )        .detach();    }     fn handle_reconnect(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {        self.finished = true;        cx.emit(DismissEvent);         if let Host::RemoteServerProject(remote_connection_options, _) = &self.host {            self.reconnect_to_remote_project(remote_connection_options.clone(), window, cx);        }    }     fn reconnect_to_remote_project(        &self,        connection_options: RemoteConnectionOptions,        window: &mut Window,        cx: &mut Context<Self>,    ) {        let Some(workspace) = self.workspace.upgrade() else {            return;        };         let Some(window_handle) = window.window_handle().downcast::<MultiWorkspace>() else {            return;        };         let app_state = workspace.read(cx).app_state().clone();        let paths = workspace            .read(cx)            .root_paths(cx)            .iter()            .map(|path| path.to_path_buf())            .collect();         cx.spawn_in(window, async move |_, cx| {            open_remote_project(                connection_options,                paths,                app_state,                OpenOptions {                    requesting_window: Some(window_handle),                    ..Default::default()                },                cx,            )            .await?;            Ok(())        })        .detach_and_prompt_err("Failed to reconnect", window, cx, |_, _, _| None);    }     fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {        self.finished = true;        cx.emit(DismissEvent)    }} impl Render for DisconnectedOverlay {    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let can_reconnect = matches!(self.host, Host::RemoteServerProject(..));         let message = match &self.host {            Host::CollabGuestProject => {                "Your connection to the remote project has been lost.".to_string()            }            Host::RemoteServerProject(options, server_not_running) => {                let autosave = if ProjectSettings::get_global(cx)                    .session                    .restore_unsaved_buffers                {                    "\nUnsaved changes are stored locally."                } else {                    ""                };                let reason = if *server_not_running {                    "process exiting unexpectedly"                } else {                    "not responding"                };                format!(                    "Your connection to {} has been lost due to the server {reason}.{autosave}",                    options.display_name(),                )            }        };         div()            .track_focus(&self.focus_handle(cx))            .elevation_3(cx)            .on_action(cx.listener(Self::cancel))            .occlude()            .w(rems(24.))            .max_h(rems(40.))            .child(                Modal::new("disconnected", None)                    .header(                        ModalHeader::new()                            .show_dismiss_button(true)                            .child(Headline::new("Disconnected").size(HeadlineSize::Small)),                    )                    .section(Section::new().child(Label::new(message)))                    .footer(                        ModalFooter::new().end_slot(                            h_flex()                                .gap_2()                                .child(                                    Button::new("close-window", "Close Window")                                        .style(ButtonStyle::Filled)                                        .layer(ElevationIndex::ModalSurface)                                        .on_click(cx.listener(move |_, _, window, _| {                                            window.remove_window();                                        })),                                )                                .when(can_reconnect, |el| {                                    el.child(                                        Button::new("reconnect", "Reconnect")                                            .style(ButtonStyle::Filled)                                            .layer(ElevationIndex::ModalSurface)                                            .start_icon(Icon::new(IconName::ArrowCircle))                                            .on_click(cx.listener(Self::handle_reconnect)),                                    )                                }),                        ),                    ),            )    }}