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}