147 lines · 5.1 KB · Rust
RawDownload
use askpass::EncryptedPassword;use editor::Editor;use futures::channel::oneshot;use gpui::{AppContext, DismissEvent, Entity, EventEmitter, Focusable, Styled};use ui::{    ActiveTheme, AnyElement, App, Button, Clickable, Color, Context, DynamicSpacing, Headline,    HeadlineSize, Icon, IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon,    LabelSize, ParentElement, Render, SharedString, StyledExt, StyledTypography, Window, div,    h_flex, v_flex,};use util::maybe;use workspace::ModalView;use zeroize::Zeroize; pub(crate) struct AskPassModal {    operation: SharedString,    prompt: SharedString,    editor: Entity<Editor>,    tx: Option<oneshot::Sender<EncryptedPassword>>,} impl EventEmitter<DismissEvent> for AskPassModal {}impl ModalView for AskPassModal {}impl Focusable for AskPassModal {    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {        self.editor.focus_handle(cx)    }} impl AskPassModal {    pub fn new(        operation: SharedString,        prompt: SharedString,        tx: oneshot::Sender<EncryptedPassword>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> Self {        let editor = cx.new(|cx| {            let mut editor = Editor::single_line(window, cx);            if prompt.contains("yes/no") || prompt.contains("Username") {                editor.set_masked(false, cx);            } else {                editor.set_masked(true, cx);            }            editor        });        Self {            operation,            prompt,            editor,            tx: Some(tx),        }    }     fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {        cx.emit(DismissEvent);    }     fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {        maybe!({            let tx = self.tx.take()?;            let mut text = self.editor.update(cx, |this, cx| {                let text = this.text(cx);                this.clear(window, cx);                text            });            let pw = askpass::EncryptedPassword::try_from(text.as_ref()).ok()?;            text.zeroize();            tx.send(pw).ok();            Some(())        });         cx.emit(DismissEvent);    }     fn render_hint(&mut self, cx: &mut Context<Self>) -> Option<AnyElement> {        let color = cx.theme().status().info_background;        if (self.prompt.contains("Password") || self.prompt.contains("Username"))            && self.prompt.contains("github.com")        {            return Some(            div()                .p_2()                .bg(color)                .border_t_1()                .border_color(cx.theme().status().info_border)                .child(                    h_flex().gap_2()                        .child(                            Icon::new(IconName::Github).size(IconSize::Small)                        )                        .child(                            Label::new("You may need to configure git for Github.")                                .size(LabelSize::Small),                        )                        .child(Button::new("learn-more", "Learn more").color(Color::Accent).label_size(LabelSize::Small).on_click(|_, _, cx| {                            cx.open_url("https://docs.github.com/en/get-started/git-basics/set-up-git#authenticating-with-github-from-git")                        })),                )                .into_any_element(),        );        }        None    }} impl Render for AskPassModal {    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        v_flex()            .key_context("PasswordPrompt")            .on_action(cx.listener(Self::cancel))            .on_action(cx.listener(Self::confirm))            .elevation_2(cx)            .size_full()            .child(                h_flex()                    .font_buffer(cx)                    .px(DynamicSpacing::Base12.rems(cx))                    .pt(DynamicSpacing::Base08.rems(cx))                    .pb(DynamicSpacing::Base04.rems(cx))                    .rounded_t_sm()                    .w_full()                    .gap_1p5()                    .child(Icon::new(IconName::GitBranch).size(IconSize::XSmall))                    .child(h_flex().gap_1().overflow_x_hidden().child(                        div().max_w_96().overflow_x_hidden().text_ellipsis().child(                            Headline::new(self.operation.clone()).size(HeadlineSize::XSmall),                        ),                    )),            )            .child(                div()                    .font_buffer(cx)                    .text_buffer(cx)                    .py_2()                    .px_3()                    .bg(cx.theme().colors().editor_background)                    .border_t_1()                    .border_color(cx.theme().colors().border_variant)                    .size_full()                    .overflow_hidden()                    .child(self.prompt.clone())                    .child(self.editor.clone()),            )            .children(self.render_hint(cx))    }}