zed_predict_tos.rs

  1//! AI service Terms of Service acceptance modal.
  2
  3use client::UserStore;
  4use gpui::{
  5    App, ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent,
  6    Render,
  7};
  8use ui::{prelude::*, TintColor};
  9use workspace::{ModalView, Workspace};
 10
 11/// Terms of acceptance for AI inline prediction.
 12pub struct ZedPredictTos {
 13    focus_handle: FocusHandle,
 14    user_store: Entity<UserStore>,
 15    workspace: Entity<Workspace>,
 16    viewed: bool,
 17}
 18
 19impl ZedPredictTos {
 20    fn new(
 21        workspace: Entity<Workspace>,
 22        user_store: Entity<UserStore>,
 23        cx: &mut Context<Self>,
 24    ) -> Self {
 25        ZedPredictTos {
 26            viewed: false,
 27            focus_handle: cx.focus_handle(),
 28            user_store,
 29            workspace,
 30        }
 31    }
 32    pub fn toggle(
 33        workspace: Entity<Workspace>,
 34        user_store: Entity<UserStore>,
 35        window: &mut Window,
 36        cx: &mut App,
 37    ) {
 38        workspace.update(cx, |this, cx| {
 39            let workspace = cx.entity().clone();
 40            this.toggle_modal(window, cx, |_window, cx| {
 41                ZedPredictTos::new(workspace, user_store, cx)
 42            });
 43        });
 44    }
 45
 46    fn view_terms(&mut self, _: &ClickEvent, _window: &mut Window, cx: &mut Context<Self>) {
 47        self.viewed = true;
 48        cx.open_url("https://zed.dev/terms-of-service");
 49        cx.notify();
 50    }
 51
 52    fn accept_terms(&mut self, _: &ClickEvent, _window: &mut Window, cx: &mut Context<Self>) {
 53        let task = self
 54            .user_store
 55            .update(cx, |this, cx| this.accept_terms_of_service(cx));
 56
 57        let workspace = self.workspace.clone();
 58
 59        cx.spawn(|this, mut cx| async move {
 60            match task.await {
 61                Ok(_) => this.update(&mut cx, |_, cx| {
 62                    cx.emit(DismissEvent);
 63                }),
 64                Err(err) => workspace.update(&mut cx, |this, cx| {
 65                    this.show_error(&err, cx);
 66                }),
 67            }
 68        })
 69        .detach_and_log_err(cx);
 70    }
 71
 72    fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
 73        cx.emit(DismissEvent);
 74    }
 75}
 76
 77impl EventEmitter<DismissEvent> for ZedPredictTos {}
 78
 79impl Focusable for ZedPredictTos {
 80    fn focus_handle(&self, _cx: &App) -> FocusHandle {
 81        self.focus_handle.clone()
 82    }
 83}
 84
 85impl ModalView for ZedPredictTos {}
 86
 87impl Render for ZedPredictTos {
 88    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 89        v_flex()
 90            .id("zed predict tos")
 91            .track_focus(&self.focus_handle(cx))
 92            .on_action(cx.listener(Self::cancel))
 93            .key_context("ZedPredictTos")
 94            .elevation_3(cx)
 95            .w_96()
 96            .items_center()
 97            .p_4()
 98            .gap_2()
 99            .on_action(cx.listener(|_, _: &menu::Cancel, _window, cx| {
100                cx.emit(DismissEvent);
101            }))
102            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| {
103                this.focus_handle.focus(window);
104            }))
105            .child(
106                h_flex()
107                    .w_full()
108                    .justify_between()
109                    .child(
110                        v_flex()
111                            .gap_0p5()
112                            .child(
113                                Label::new("Zed AI")
114                                    .size(LabelSize::Small)
115                                    .color(Color::Muted),
116                            )
117                            .child(Headline::new("Edit Prediction")),
118                    )
119                    .child(Icon::new(IconName::ZedPredict).size(IconSize::XLarge)),
120            )
121            .child(
122                Label::new(
123                    "To use Zed AI's Edit Prediction feature, please read and accept our Terms of Service.",
124                )
125                .color(Color::Muted),
126            )
127            .child(
128                v_flex()
129                    .mt_2()
130                    .gap_0p5()
131                    .w_full()
132                    .child(if self.viewed {
133                        Button::new("accept-tos", "I've Read and Accept the Terms of Service")
134                            .style(ButtonStyle::Tinted(TintColor::Accent))
135                            .full_width()
136                            .on_click(cx.listener(Self::accept_terms))
137                    } else {
138                        Button::new("view-tos", "Read Terms of Service")
139                            .style(ButtonStyle::Tinted(TintColor::Accent))
140                            .icon(IconName::ArrowUpRight)
141                            .icon_size(IconSize::XSmall)
142                            .icon_position(IconPosition::End)
143                            .full_width()
144                            .on_click(cx.listener(Self::view_terms))
145                    })
146                    .child(
147                        Button::new("cancel", "Cancel")
148                            .full_width()
149                            .on_click(cx.listener(|_, _: &ClickEvent, _window, cx| {
150                                cx.emit(DismissEvent);
151                            })),
152                    ),
153            )
154    }
155}