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}