zed_predict_tos.rs

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