onboarding: Remove accept AI ToS from within Zed (#36612)
Users now accept ToS from Zed's website when they sign in to Zed the first time. So it's no longer possible that a signed in account could not have accepted the ToS. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
3d2fa72d1f
commit
8204ef1e51
16 changed files with 44 additions and 499 deletions
|
@ -1595,11 +1595,6 @@ impl ActiveThread {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if model.provider.must_accept_terms(cx) {
|
|
||||||
cx.notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let edited_text = state.editor.read(cx).text(cx);
|
let edited_text = state.editor.read(cx).text(cx);
|
||||||
|
|
||||||
let creases = state.editor.update(cx, extract_message_creases);
|
let creases = state.editor.update(cx, extract_message_creases);
|
||||||
|
|
|
@ -93,14 +93,6 @@ impl AgentConfiguration {
|
||||||
let scroll_handle = ScrollHandle::new();
|
let scroll_handle = ScrollHandle::new();
|
||||||
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
|
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
|
||||||
|
|
||||||
let mut expanded_provider_configurations = HashMap::default();
|
|
||||||
if LanguageModelRegistry::read_global(cx)
|
|
||||||
.provider(&ZED_CLOUD_PROVIDER_ID)
|
|
||||||
.is_some_and(|cloud_provider| cloud_provider.must_accept_terms(cx))
|
|
||||||
{
|
|
||||||
expanded_provider_configurations.insert(ZED_CLOUD_PROVIDER_ID, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
fs,
|
fs,
|
||||||
language_registry,
|
language_registry,
|
||||||
|
@ -109,7 +101,7 @@ impl AgentConfiguration {
|
||||||
configuration_views_by_provider: HashMap::default(),
|
configuration_views_by_provider: HashMap::default(),
|
||||||
context_server_store,
|
context_server_store,
|
||||||
expanded_context_server_tools: HashMap::default(),
|
expanded_context_server_tools: HashMap::default(),
|
||||||
expanded_provider_configurations,
|
expanded_provider_configurations: HashMap::default(),
|
||||||
tools,
|
tools,
|
||||||
_registry_subscription: registry_subscription,
|
_registry_subscription: registry_subscription,
|
||||||
scroll_handle,
|
scroll_handle,
|
||||||
|
|
|
@ -54,9 +54,7 @@ use gpui::{
|
||||||
Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
|
Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use language_model::{
|
use language_model::{ConfigurationError, ConfiguredModel, LanguageModelRegistry};
|
||||||
ConfigurationError, ConfiguredModel, LanguageModelProviderTosView, LanguageModelRegistry,
|
|
||||||
};
|
|
||||||
use project::{DisableAiSettings, Project, ProjectPath, Worktree};
|
use project::{DisableAiSettings, Project, ProjectPath, Worktree};
|
||||||
use prompt_store::{PromptBuilder, PromptStore, UserPromptId};
|
use prompt_store::{PromptBuilder, PromptStore, UserPromptId};
|
||||||
use rules_library::{RulesLibrary, open_rules_library};
|
use rules_library::{RulesLibrary, open_rules_library};
|
||||||
|
@ -3203,17 +3201,6 @@ impl AgentPanel {
|
||||||
ConfigurationError::ModelNotFound
|
ConfigurationError::ModelNotFound
|
||||||
| ConfigurationError::ProviderNotAuthenticated(_)
|
| ConfigurationError::ProviderNotAuthenticated(_)
|
||||||
| ConfigurationError::NoProvider => callout.into_any_element(),
|
| ConfigurationError::NoProvider => callout.into_any_element(),
|
||||||
ConfigurationError::ProviderPendingTermsAcceptance(provider) => {
|
|
||||||
Banner::new()
|
|
||||||
.severity(Severity::Warning)
|
|
||||||
.child(h_flex().w_full().children(
|
|
||||||
provider.render_accept_terms(
|
|
||||||
LanguageModelProviderTosView::ThreadEmptyState,
|
|
||||||
cx,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -378,18 +378,13 @@ impl MessageEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_model(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn send_to_model(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let Some(ConfiguredModel { model, provider }) = self
|
let Some(ConfiguredModel { model, .. }) = self
|
||||||
.thread
|
.thread
|
||||||
.update(cx, |thread, cx| thread.get_or_init_configured_model(cx))
|
.update(cx, |thread, cx| thread.get_or_init_configured_model(cx))
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if provider.must_accept_terms(cx) {
|
|
||||||
cx.notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (user_message, user_message_creases) = self.editor.update(cx, |editor, cx| {
|
let (user_message, user_message_creases) = self.editor.update(cx, |editor, cx| {
|
||||||
let creases = extract_message_creases(editor, cx);
|
let creases = extract_message_creases(editor, cx);
|
||||||
let text = editor.text(cx);
|
let text = editor.text(cx);
|
||||||
|
|
|
@ -190,7 +190,6 @@ pub struct TextThreadEditor {
|
||||||
invoked_slash_command_creases: HashMap<InvokedSlashCommandId, CreaseId>,
|
invoked_slash_command_creases: HashMap<InvokedSlashCommandId, CreaseId>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
last_error: Option<AssistError>,
|
last_error: Option<AssistError>,
|
||||||
show_accept_terms: bool,
|
|
||||||
pub(crate) slash_menu_handle:
|
pub(crate) slash_menu_handle:
|
||||||
PopoverMenuHandle<Picker<slash_command_picker::SlashCommandDelegate>>,
|
PopoverMenuHandle<Picker<slash_command_picker::SlashCommandDelegate>>,
|
||||||
// dragged_file_worktrees is used to keep references to worktrees that were added
|
// dragged_file_worktrees is used to keep references to worktrees that were added
|
||||||
|
@ -289,7 +288,6 @@ impl TextThreadEditor {
|
||||||
invoked_slash_command_creases: HashMap::default(),
|
invoked_slash_command_creases: HashMap::default(),
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
last_error: None,
|
last_error: None,
|
||||||
show_accept_terms: false,
|
|
||||||
slash_menu_handle: Default::default(),
|
slash_menu_handle: Default::default(),
|
||||||
dragged_file_worktrees: Vec::new(),
|
dragged_file_worktrees: Vec::new(),
|
||||||
language_model_selector: cx.new(|cx| {
|
language_model_selector: cx.new(|cx| {
|
||||||
|
@ -367,20 +365,7 @@ impl TextThreadEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_model(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn send_to_model(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let provider = LanguageModelRegistry::read_global(cx)
|
|
||||||
.default_model()
|
|
||||||
.map(|default| default.provider);
|
|
||||||
if provider
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|provider| provider.must_accept_terms(cx))
|
|
||||||
{
|
|
||||||
self.show_accept_terms = true;
|
|
||||||
cx.notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.last_error = None;
|
self.last_error = None;
|
||||||
|
|
||||||
if let Some(user_message) = self.context.update(cx, |context, cx| context.assist(cx)) {
|
if let Some(user_message) = self.context.update(cx, |context, cx| context.assist(cx)) {
|
||||||
let new_selection = {
|
let new_selection = {
|
||||||
let cursor = user_message
|
let cursor = user_message
|
||||||
|
@ -1930,7 +1915,6 @@ impl TextThreadEditor {
|
||||||
ConfigurationError::NoProvider
|
ConfigurationError::NoProvider
|
||||||
| ConfigurationError::ModelNotFound
|
| ConfigurationError::ModelNotFound
|
||||||
| ConfigurationError::ProviderNotAuthenticated(_) => true,
|
| ConfigurationError::ProviderNotAuthenticated(_) => true,
|
||||||
ConfigurationError::ProviderPendingTermsAcceptance(_) => self.show_accept_terms,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use client::{Client, UserStore, zed_urls};
|
use client::{Client, UserStore, zed_urls};
|
||||||
use gpui::{AnyElement, Entity, IntoElement, ParentElement};
|
use gpui::{AnyElement, Entity, IntoElement, ParentElement};
|
||||||
use ui::{Divider, RegisterComponent, TintColor, Tooltip, prelude::*};
|
use ui::{Divider, RegisterComponent, Tooltip, prelude::*};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum SignInStatus {
|
pub enum SignInStatus {
|
||||||
|
@ -43,12 +43,10 @@ impl From<client::Status> for SignInStatus {
|
||||||
#[derive(RegisterComponent, IntoElement)]
|
#[derive(RegisterComponent, IntoElement)]
|
||||||
pub struct ZedAiOnboarding {
|
pub struct ZedAiOnboarding {
|
||||||
pub sign_in_status: SignInStatus,
|
pub sign_in_status: SignInStatus,
|
||||||
pub has_accepted_terms_of_service: bool,
|
|
||||||
pub plan: Option<Plan>,
|
pub plan: Option<Plan>,
|
||||||
pub account_too_young: bool,
|
pub account_too_young: bool,
|
||||||
pub continue_with_zed_ai: Arc<dyn Fn(&mut Window, &mut App)>,
|
pub continue_with_zed_ai: Arc<dyn Fn(&mut Window, &mut App)>,
|
||||||
pub sign_in: Arc<dyn Fn(&mut Window, &mut App)>,
|
pub sign_in: Arc<dyn Fn(&mut Window, &mut App)>,
|
||||||
pub accept_terms_of_service: Arc<dyn Fn(&mut Window, &mut App)>,
|
|
||||||
pub dismiss_onboarding: Option<Arc<dyn Fn(&mut Window, &mut App)>>,
|
pub dismiss_onboarding: Option<Arc<dyn Fn(&mut Window, &mut App)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,17 +62,9 @@ impl ZedAiOnboarding {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
sign_in_status: status.into(),
|
sign_in_status: status.into(),
|
||||||
has_accepted_terms_of_service: store.has_accepted_terms_of_service(),
|
|
||||||
plan: store.plan(),
|
plan: store.plan(),
|
||||||
account_too_young: store.account_too_young(),
|
account_too_young: store.account_too_young(),
|
||||||
continue_with_zed_ai,
|
continue_with_zed_ai,
|
||||||
accept_terms_of_service: Arc::new({
|
|
||||||
let store = user_store.clone();
|
|
||||||
move |_window, cx| {
|
|
||||||
let task = store.update(cx, |store, cx| store.accept_terms_of_service(cx));
|
|
||||||
task.detach_and_log_err(cx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
sign_in: Arc::new(move |_window, cx| {
|
sign_in: Arc::new(move |_window, cx| {
|
||||||
cx.spawn({
|
cx.spawn({
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
|
@ -94,42 +84,6 @@ impl ZedAiOnboarding {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_accept_terms_of_service(&self) -> AnyElement {
|
|
||||||
v_flex()
|
|
||||||
.gap_1()
|
|
||||||
.w_full()
|
|
||||||
.child(Headline::new("Accept Terms of Service"))
|
|
||||||
.child(
|
|
||||||
Label::new("We don’t sell your data, track you across the web, or compromise your privacy.")
|
|
||||||
.color(Color::Muted)
|
|
||||||
.mb_2(),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("terms_of_service", "Review Terms of Service")
|
|
||||||
.full_width()
|
|
||||||
.style(ButtonStyle::Outlined)
|
|
||||||
.icon(IconName::ArrowUpRight)
|
|
||||||
.icon_color(Color::Muted)
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
.on_click(move |_, _window, cx| {
|
|
||||||
telemetry::event!("Review Terms of Service Clicked");
|
|
||||||
cx.open_url(&zed_urls::terms_of_service(cx))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("accept_terms", "Accept")
|
|
||||||
.full_width()
|
|
||||||
.style(ButtonStyle::Tinted(TintColor::Accent))
|
|
||||||
.on_click({
|
|
||||||
let callback = self.accept_terms_of_service.clone();
|
|
||||||
move |_, window, cx| {
|
|
||||||
telemetry::event!("Terms of Service Accepted");
|
|
||||||
(callback)(window, cx)}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
|
fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
|
||||||
let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
|
let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
|
||||||
let plan_definitions = PlanDefinitions;
|
let plan_definitions = PlanDefinitions;
|
||||||
|
@ -359,14 +313,10 @@ impl ZedAiOnboarding {
|
||||||
impl RenderOnce for ZedAiOnboarding {
|
impl RenderOnce for ZedAiOnboarding {
|
||||||
fn render(self, _window: &mut ui::Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, _window: &mut ui::Window, cx: &mut App) -> impl IntoElement {
|
||||||
if matches!(self.sign_in_status, SignInStatus::SignedIn) {
|
if matches!(self.sign_in_status, SignInStatus::SignedIn) {
|
||||||
if self.has_accepted_terms_of_service {
|
match self.plan {
|
||||||
match self.plan {
|
None | Some(Plan::ZedFree) => self.render_free_plan_state(cx),
|
||||||
None | Some(Plan::ZedFree) => self.render_free_plan_state(cx),
|
Some(Plan::ZedProTrial) => self.render_trial_state(cx),
|
||||||
Some(Plan::ZedProTrial) => self.render_trial_state(cx),
|
Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
|
||||||
Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.render_accept_terms_of_service()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.render_sign_in_disclaimer(cx)
|
self.render_sign_in_disclaimer(cx)
|
||||||
|
@ -390,18 +340,15 @@ impl Component for ZedAiOnboarding {
|
||||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||||
fn onboarding(
|
fn onboarding(
|
||||||
sign_in_status: SignInStatus,
|
sign_in_status: SignInStatus,
|
||||||
has_accepted_terms_of_service: bool,
|
|
||||||
plan: Option<Plan>,
|
plan: Option<Plan>,
|
||||||
account_too_young: bool,
|
account_too_young: bool,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
ZedAiOnboarding {
|
ZedAiOnboarding {
|
||||||
sign_in_status,
|
sign_in_status,
|
||||||
has_accepted_terms_of_service,
|
|
||||||
plan,
|
plan,
|
||||||
account_too_young,
|
account_too_young,
|
||||||
continue_with_zed_ai: Arc::new(|_, _| {}),
|
continue_with_zed_ai: Arc::new(|_, _| {}),
|
||||||
sign_in: Arc::new(|_, _| {}),
|
sign_in: Arc::new(|_, _| {}),
|
||||||
accept_terms_of_service: Arc::new(|_, _| {}),
|
|
||||||
dismiss_onboarding: None,
|
dismiss_onboarding: None,
|
||||||
}
|
}
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
|
@ -415,27 +362,23 @@ impl Component for ZedAiOnboarding {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
single_example(
|
single_example(
|
||||||
"Not Signed-in",
|
"Not Signed-in",
|
||||||
onboarding(SignInStatus::SignedOut, false, None, false),
|
onboarding(SignInStatus::SignedOut, None, false),
|
||||||
),
|
|
||||||
single_example(
|
|
||||||
"Not Accepted ToS",
|
|
||||||
onboarding(SignInStatus::SignedIn, false, None, false),
|
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Young Account",
|
"Young Account",
|
||||||
onboarding(SignInStatus::SignedIn, true, None, true),
|
onboarding(SignInStatus::SignedIn, None, true),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Free Plan",
|
"Free Plan",
|
||||||
onboarding(SignInStatus::SignedIn, true, Some(Plan::ZedFree), false),
|
onboarding(SignInStatus::SignedIn, Some(Plan::ZedFree), false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Pro Trial",
|
"Pro Trial",
|
||||||
onboarding(SignInStatus::SignedIn, true, Some(Plan::ZedProTrial), false),
|
onboarding(SignInStatus::SignedIn, Some(Plan::ZedProTrial), false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Pro Plan",
|
"Pro Plan",
|
||||||
onboarding(SignInStatus::SignedIn, true, Some(Plan::ZedPro), false),
|
onboarding(SignInStatus::SignedIn, Some(Plan::ZedPro), false),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{Client, Status, TypedEnvelope, proto};
|
use super::{Client, Status, TypedEnvelope, proto};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use cloud_api_client::websocket_protocol::MessageToClient;
|
use cloud_api_client::websocket_protocol::MessageToClient;
|
||||||
use cloud_api_client::{GetAuthenticatedUserResponse, PlanInfo};
|
use cloud_api_client::{GetAuthenticatedUserResponse, PlanInfo};
|
||||||
|
@ -116,7 +116,6 @@ pub struct UserStore {
|
||||||
edit_prediction_usage: Option<EditPredictionUsage>,
|
edit_prediction_usage: Option<EditPredictionUsage>,
|
||||||
plan_info: Option<PlanInfo>,
|
plan_info: Option<PlanInfo>,
|
||||||
current_user: watch::Receiver<Option<Arc<User>>>,
|
current_user: watch::Receiver<Option<Arc<User>>>,
|
||||||
accepted_tos_at: Option<Option<cloud_api_client::Timestamp>>,
|
|
||||||
contacts: Vec<Arc<Contact>>,
|
contacts: Vec<Arc<Contact>>,
|
||||||
incoming_contact_requests: Vec<Arc<User>>,
|
incoming_contact_requests: Vec<Arc<User>>,
|
||||||
outgoing_contact_requests: Vec<Arc<User>>,
|
outgoing_contact_requests: Vec<Arc<User>>,
|
||||||
|
@ -194,7 +193,6 @@ impl UserStore {
|
||||||
plan_info: None,
|
plan_info: None,
|
||||||
model_request_usage: None,
|
model_request_usage: None,
|
||||||
edit_prediction_usage: None,
|
edit_prediction_usage: None,
|
||||||
accepted_tos_at: None,
|
|
||||||
contacts: Default::default(),
|
contacts: Default::default(),
|
||||||
incoming_contact_requests: Default::default(),
|
incoming_contact_requests: Default::default(),
|
||||||
participant_indices: Default::default(),
|
participant_indices: Default::default(),
|
||||||
|
@ -271,7 +269,6 @@ impl UserStore {
|
||||||
Status::SignedOut => {
|
Status::SignedOut => {
|
||||||
current_user_tx.send(None).await.ok();
|
current_user_tx.send(None).await.ok();
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.accepted_tos_at = None;
|
|
||||||
cx.emit(Event::PrivateUserInfoUpdated);
|
cx.emit(Event::PrivateUserInfoUpdated);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
this.clear_contacts()
|
this.clear_contacts()
|
||||||
|
@ -791,19 +788,6 @@ impl UserStore {
|
||||||
.set_authenticated_user_info(Some(response.user.metrics_id.clone()), staff);
|
.set_authenticated_user_info(Some(response.user.metrics_id.clone()), staff);
|
||||||
}
|
}
|
||||||
|
|
||||||
let accepted_tos_at = {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
if std::env::var("ZED_IGNORE_ACCEPTED_TOS").is_ok() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
response.user.accepted_tos_at
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
response.user.accepted_tos_at
|
|
||||||
};
|
|
||||||
|
|
||||||
self.accepted_tos_at = Some(accepted_tos_at);
|
|
||||||
self.model_request_usage = Some(ModelRequestUsage(RequestUsage {
|
self.model_request_usage = Some(ModelRequestUsage(RequestUsage {
|
||||||
limit: response.plan.usage.model_requests.limit,
|
limit: response.plan.usage.model_requests.limit,
|
||||||
amount: response.plan.usage.model_requests.used as i32,
|
amount: response.plan.usage.model_requests.used as i32,
|
||||||
|
@ -846,32 +830,6 @@ impl UserStore {
|
||||||
self.current_user.clone()
|
self.current_user.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_accepted_terms_of_service(&self) -> bool {
|
|
||||||
self.accepted_tos_at
|
|
||||||
.is_some_and(|accepted_tos_at| accepted_tos_at.is_some())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accept_terms_of_service(&self, cx: &Context<Self>) -> Task<Result<()>> {
|
|
||||||
if self.current_user().is_none() {
|
|
||||||
return Task::ready(Err(anyhow!("no current user")));
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = self.client.clone();
|
|
||||||
cx.spawn(async move |this, cx| -> anyhow::Result<()> {
|
|
||||||
let client = client.upgrade().context("client not found")?;
|
|
||||||
let response = client
|
|
||||||
.cloud_client()
|
|
||||||
.accept_terms_of_service()
|
|
||||||
.await
|
|
||||||
.context("error accepting tos")?;
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.accepted_tos_at = Some(response.user.accepted_tos_at);
|
|
||||||
cx.emit(Event::PrivateUserInfoUpdated);
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_users(
|
fn load_users(
|
||||||
&self,
|
&self,
|
||||||
request: impl RequestMessage<Response = UsersResponse>,
|
request: impl RequestMessage<Response = UsersResponse>,
|
||||||
|
|
|
@ -115,34 +115,6 @@ impl CloudApiClient {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn accept_terms_of_service(&self) -> Result<AcceptTermsOfServiceResponse> {
|
|
||||||
let request = self.build_request(
|
|
||||||
Request::builder().method(Method::POST).uri(
|
|
||||||
self.http_client
|
|
||||||
.build_zed_cloud_url("/client/terms_of_service/accept", &[])?
|
|
||||||
.as_ref(),
|
|
||||||
),
|
|
||||||
AsyncBody::default(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut response = self.http_client.send(request).await?;
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
let mut body = String::new();
|
|
||||||
response.body_mut().read_to_string(&mut body).await?;
|
|
||||||
|
|
||||||
anyhow::bail!(
|
|
||||||
"Failed to accept terms of service.\nStatus: {:?}\nBody: {body}",
|
|
||||||
response.status()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut body = String::new();
|
|
||||||
response.body_mut().read_to_string(&mut body).await?;
|
|
||||||
|
|
||||||
Ok(serde_json::from_str(&body)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_llm_token(
|
pub async fn create_llm_token(
|
||||||
&self,
|
&self,
|
||||||
system_id: Option<String>,
|
system_id: Option<String>,
|
||||||
|
|
|
@ -89,9 +89,6 @@ pub trait EditPredictionProvider: 'static + Sized {
|
||||||
debounce: bool,
|
debounce: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
);
|
);
|
||||||
fn needs_terms_acceptance(&self, _cx: &App) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn cycle(
|
fn cycle(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
|
@ -124,7 +121,6 @@ pub trait EditPredictionProviderHandle {
|
||||||
fn data_collection_state(&self, cx: &App) -> DataCollectionState;
|
fn data_collection_state(&self, cx: &App) -> DataCollectionState;
|
||||||
fn usage(&self, cx: &App) -> Option<EditPredictionUsage>;
|
fn usage(&self, cx: &App) -> Option<EditPredictionUsage>;
|
||||||
fn toggle_data_collection(&self, cx: &mut App);
|
fn toggle_data_collection(&self, cx: &mut App);
|
||||||
fn needs_terms_acceptance(&self, cx: &App) -> bool;
|
|
||||||
fn is_refreshing(&self, cx: &App) -> bool;
|
fn is_refreshing(&self, cx: &App) -> bool;
|
||||||
fn refresh(
|
fn refresh(
|
||||||
&self,
|
&self,
|
||||||
|
@ -196,10 +192,6 @@ where
|
||||||
self.read(cx).is_enabled(buffer, cursor_position, cx)
|
self.read(cx).is_enabled(buffer, cursor_position, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn needs_terms_acceptance(&self, cx: &App) -> bool {
|
|
||||||
self.read(cx).needs_terms_acceptance(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_refreshing(&self, cx: &App) -> bool {
|
fn is_refreshing(&self, cx: &App) -> bool {
|
||||||
self.read(cx).is_refreshing()
|
self.read(cx).is_refreshing()
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,13 +242,9 @@ impl Render for EditPredictionButton {
|
||||||
IconName::ZedPredictDisabled
|
IconName::ZedPredictDisabled
|
||||||
};
|
};
|
||||||
|
|
||||||
if zeta::should_show_upsell_modal(&self.user_store, cx) {
|
if zeta::should_show_upsell_modal() {
|
||||||
let tooltip_meta = if self.user_store.read(cx).current_user().is_some() {
|
let tooltip_meta = if self.user_store.read(cx).current_user().is_some() {
|
||||||
if self.user_store.read(cx).has_accepted_terms_of_service() {
|
"Choose a Plan"
|
||||||
"Choose a Plan"
|
|
||||||
} else {
|
|
||||||
"Accept the Terms of Service"
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
"Sign In"
|
"Sign In"
|
||||||
};
|
};
|
||||||
|
|
|
@ -253,7 +253,6 @@ pub type RenderDiffHunkControlsFn = Arc<
|
||||||
enum ReportEditorEvent {
|
enum ReportEditorEvent {
|
||||||
Saved { auto_saved: bool },
|
Saved { auto_saved: bool },
|
||||||
EditorOpened,
|
EditorOpened,
|
||||||
ZetaTosClicked,
|
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +261,6 @@ impl ReportEditorEvent {
|
||||||
match self {
|
match self {
|
||||||
Self::Saved { .. } => "Editor Saved",
|
Self::Saved { .. } => "Editor Saved",
|
||||||
Self::EditorOpened => "Editor Opened",
|
Self::EditorOpened => "Editor Opened",
|
||||||
Self::ZetaTosClicked => "Edit Prediction Provider ToS Clicked",
|
|
||||||
Self::Closed => "Editor Closed",
|
Self::Closed => "Editor Closed",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9180,45 +9178,6 @@ impl Editor {
|
||||||
let provider = self.edit_prediction_provider.as_ref()?;
|
let provider = self.edit_prediction_provider.as_ref()?;
|
||||||
let provider_icon = Self::get_prediction_provider_icon_name(&self.edit_prediction_provider);
|
let provider_icon = Self::get_prediction_provider_icon_name(&self.edit_prediction_provider);
|
||||||
|
|
||||||
if provider.provider.needs_terms_acceptance(cx) {
|
|
||||||
return Some(
|
|
||||||
h_flex()
|
|
||||||
.min_w(min_width)
|
|
||||||
.flex_1()
|
|
||||||
.px_2()
|
|
||||||
.py_1()
|
|
||||||
.gap_3()
|
|
||||||
.elevation_2(cx)
|
|
||||||
.hover(|style| style.bg(cx.theme().colors().element_hover))
|
|
||||||
.id("accept-terms")
|
|
||||||
.cursor_pointer()
|
|
||||||
.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
|
|
||||||
.on_click(cx.listener(|this, _event, window, cx| {
|
|
||||||
cx.stop_propagation();
|
|
||||||
this.report_editor_event(ReportEditorEvent::ZetaTosClicked, None, cx);
|
|
||||||
window.dispatch_action(
|
|
||||||
zed_actions::OpenZedPredictOnboarding.boxed_clone(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}))
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.flex_1()
|
|
||||||
.gap_2()
|
|
||||||
.child(Icon::new(provider_icon))
|
|
||||||
.child(Label::new("Accept Terms of Service"))
|
|
||||||
.child(div().w_full())
|
|
||||||
.child(
|
|
||||||
Icon::new(IconName::ArrowUpRight)
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(IconSize::Small),
|
|
||||||
)
|
|
||||||
.into_any_element(),
|
|
||||||
)
|
|
||||||
.into_any(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_refreshing = provider.provider.is_refreshing(cx);
|
let is_refreshing = provider.provider.is_refreshing(cx);
|
||||||
|
|
||||||
fn pending_completion_container(icon: IconName) -> Div {
|
fn pending_completion_container(icon: IconName) -> Div {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use client::Client;
|
||||||
use cloud_llm_client::{CompletionMode, CompletionRequestStatus};
|
use cloud_llm_client::{CompletionMode, CompletionRequestStatus};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::{StreamExt, future::BoxFuture, stream::BoxStream};
|
use futures::{StreamExt, future::BoxFuture, stream::BoxStream};
|
||||||
use gpui::{AnyElement, AnyView, App, AsyncApp, SharedString, Task, Window};
|
use gpui::{AnyView, App, AsyncApp, SharedString, Task, Window};
|
||||||
use http_client::{StatusCode, http};
|
use http_client::{StatusCode, http};
|
||||||
use icons::IconName;
|
use icons::IconName;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -640,16 +640,6 @@ pub trait LanguageModelProvider: 'static {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> AnyView;
|
) -> AnyView;
|
||||||
fn must_accept_terms(&self, _cx: &App) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn render_accept_terms(
|
|
||||||
&self,
|
|
||||||
_view: LanguageModelProviderTosView,
|
|
||||||
_cx: &mut App,
|
|
||||||
) -> Option<AnyElement> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>>;
|
fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,6 @@ pub enum ConfigurationError {
|
||||||
ModelNotFound,
|
ModelNotFound,
|
||||||
#[error("{} LLM provider is not configured.", .0.name().0)]
|
#[error("{} LLM provider is not configured.", .0.name().0)]
|
||||||
ProviderNotAuthenticated(Arc<dyn LanguageModelProvider>),
|
ProviderNotAuthenticated(Arc<dyn LanguageModelProvider>),
|
||||||
#[error("Using the {} LLM provider requires accepting the Terms of Service.",
|
|
||||||
.0.name().0)]
|
|
||||||
ProviderPendingTermsAcceptance(Arc<dyn LanguageModelProvider>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for ConfigurationError {
|
impl std::fmt::Debug for ConfigurationError {
|
||||||
|
@ -37,9 +34,6 @@ impl std::fmt::Debug for ConfigurationError {
|
||||||
Self::ProviderNotAuthenticated(provider) => {
|
Self::ProviderNotAuthenticated(provider) => {
|
||||||
write!(f, "ProviderNotAuthenticated({})", provider.id())
|
write!(f, "ProviderNotAuthenticated({})", provider.id())
|
||||||
}
|
}
|
||||||
Self::ProviderPendingTermsAcceptance(provider) => {
|
|
||||||
write!(f, "ProviderPendingTermsAcceptance({})", provider.id())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,12 +192,6 @@ impl LanguageModelRegistry {
|
||||||
return Some(ConfigurationError::ProviderNotAuthenticated(model.provider));
|
return Some(ConfigurationError::ProviderNotAuthenticated(model.provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
if model.provider.must_accept_terms(cx) {
|
|
||||||
return Some(ConfigurationError::ProviderPendingTermsAcceptance(
|
|
||||||
model.provider,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,9 @@ use language_model::{
|
||||||
AuthenticateError, LanguageModel, LanguageModelCacheConfiguration,
|
AuthenticateError, LanguageModel, LanguageModelCacheConfiguration,
|
||||||
LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelId, LanguageModelName,
|
LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelId, LanguageModelName,
|
||||||
LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName,
|
LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName,
|
||||||
LanguageModelProviderState, LanguageModelProviderTosView, LanguageModelRequest,
|
LanguageModelProviderState, LanguageModelRequest, LanguageModelToolChoice,
|
||||||
LanguageModelToolChoice, LanguageModelToolSchemaFormat, LlmApiToken,
|
LanguageModelToolSchemaFormat, LlmApiToken, ModelRequestLimitReachedError,
|
||||||
ModelRequestLimitReachedError, PaymentRequiredError, RateLimiter, RefreshLlmTokenListener,
|
PaymentRequiredError, RateLimiter, RefreshLlmTokenListener,
|
||||||
};
|
};
|
||||||
use release_channel::AppVersion;
|
use release_channel::AppVersion;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
@ -118,7 +118,6 @@ pub struct State {
|
||||||
llm_api_token: LlmApiToken,
|
llm_api_token: LlmApiToken,
|
||||||
user_store: Entity<UserStore>,
|
user_store: Entity<UserStore>,
|
||||||
status: client::Status,
|
status: client::Status,
|
||||||
accept_terms_of_service_task: Option<Task<Result<()>>>,
|
|
||||||
models: Vec<Arc<cloud_llm_client::LanguageModel>>,
|
models: Vec<Arc<cloud_llm_client::LanguageModel>>,
|
||||||
default_model: Option<Arc<cloud_llm_client::LanguageModel>>,
|
default_model: Option<Arc<cloud_llm_client::LanguageModel>>,
|
||||||
default_fast_model: Option<Arc<cloud_llm_client::LanguageModel>>,
|
default_fast_model: Option<Arc<cloud_llm_client::LanguageModel>>,
|
||||||
|
@ -142,7 +141,6 @@ impl State {
|
||||||
llm_api_token: LlmApiToken::default(),
|
llm_api_token: LlmApiToken::default(),
|
||||||
user_store,
|
user_store,
|
||||||
status,
|
status,
|
||||||
accept_terms_of_service_task: None,
|
|
||||||
models: Vec::new(),
|
models: Vec::new(),
|
||||||
default_model: None,
|
default_model: None,
|
||||||
default_fast_model: None,
|
default_fast_model: None,
|
||||||
|
@ -197,24 +195,6 @@ impl State {
|
||||||
state.update(cx, |_, cx| cx.notify())
|
state.update(cx, |_, cx| cx.notify())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_accepted_terms_of_service(&self, cx: &App) -> bool {
|
|
||||||
self.user_store.read(cx).has_accepted_terms_of_service()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accept_terms_of_service(&mut self, cx: &mut Context<Self>) {
|
|
||||||
let user_store = self.user_store.clone();
|
|
||||||
self.accept_terms_of_service_task = Some(cx.spawn(async move |this, cx| {
|
|
||||||
let _ = user_store
|
|
||||||
.update(cx, |store, cx| store.accept_terms_of_service(cx))?
|
|
||||||
.await;
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.accept_terms_of_service_task = None;
|
|
||||||
cx.notify()
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_models(&mut self, response: ListModelsResponse, cx: &mut Context<Self>) {
|
fn update_models(&mut self, response: ListModelsResponse, cx: &mut Context<Self>) {
|
||||||
let mut models = Vec::new();
|
let mut models = Vec::new();
|
||||||
|
|
||||||
|
@ -384,7 +364,7 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
|
||||||
|
|
||||||
fn is_authenticated(&self, cx: &App) -> bool {
|
fn is_authenticated(&self, cx: &App) -> bool {
|
||||||
let state = self.state.read(cx);
|
let state = self.state.read(cx);
|
||||||
!state.is_signed_out(cx) && state.has_accepted_terms_of_service(cx)
|
!state.is_signed_out(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn authenticate(&self, _cx: &mut App) -> Task<Result<(), AuthenticateError>> {
|
fn authenticate(&self, _cx: &mut App) -> Task<Result<(), AuthenticateError>> {
|
||||||
|
@ -401,112 +381,11 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn must_accept_terms(&self, cx: &App) -> bool {
|
|
||||||
!self.state.read(cx).has_accepted_terms_of_service(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_accept_terms(
|
|
||||||
&self,
|
|
||||||
view: LanguageModelProviderTosView,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> Option<AnyElement> {
|
|
||||||
let state = self.state.read(cx);
|
|
||||||
if state.has_accepted_terms_of_service(cx) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(
|
|
||||||
render_accept_terms(view, state.accept_terms_of_service_task.is_some(), {
|
|
||||||
let state = self.state.clone();
|
|
||||||
move |_window, cx| {
|
|
||||||
state.update(cx, |state, cx| state.accept_terms_of_service(cx));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into_any_element(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_credentials(&self, _cx: &mut App) -> Task<Result<()>> {
|
fn reset_credentials(&self, _cx: &mut App) -> Task<Result<()>> {
|
||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_accept_terms(
|
|
||||||
view_kind: LanguageModelProviderTosView,
|
|
||||||
accept_terms_of_service_in_progress: bool,
|
|
||||||
accept_terms_callback: impl Fn(&mut Window, &mut App) + 'static,
|
|
||||||
) -> impl IntoElement {
|
|
||||||
let thread_fresh_start = matches!(view_kind, LanguageModelProviderTosView::ThreadFreshStart);
|
|
||||||
let thread_empty_state = matches!(view_kind, LanguageModelProviderTosView::ThreadEmptyState);
|
|
||||||
|
|
||||||
let terms_button = Button::new("terms_of_service", "Terms of Service")
|
|
||||||
.style(ButtonStyle::Subtle)
|
|
||||||
.icon(IconName::ArrowUpRight)
|
|
||||||
.icon_color(Color::Muted)
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
.when(thread_empty_state, |this| this.label_size(LabelSize::Small))
|
|
||||||
.on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
|
|
||||||
|
|
||||||
let button_container = h_flex().child(
|
|
||||||
Button::new("accept_terms", "I accept the Terms of Service")
|
|
||||||
.when(!thread_empty_state, |this| {
|
|
||||||
this.full_width()
|
|
||||||
.style(ButtonStyle::Tinted(TintColor::Accent))
|
|
||||||
.icon(IconName::Check)
|
|
||||||
.icon_position(IconPosition::Start)
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
})
|
|
||||||
.when(thread_empty_state, |this| {
|
|
||||||
this.style(ButtonStyle::Tinted(TintColor::Warning))
|
|
||||||
.label_size(LabelSize::Small)
|
|
||||||
})
|
|
||||||
.disabled(accept_terms_of_service_in_progress)
|
|
||||||
.on_click(move |_, window, cx| (accept_terms_callback)(window, cx)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if thread_empty_state {
|
|
||||||
h_flex()
|
|
||||||
.w_full()
|
|
||||||
.flex_wrap()
|
|
||||||
.justify_between()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.child(
|
|
||||||
Label::new("To start using Zed AI, please read and accept the")
|
|
||||||
.size(LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(terms_button),
|
|
||||||
)
|
|
||||||
.child(button_container)
|
|
||||||
} else {
|
|
||||||
v_flex()
|
|
||||||
.w_full()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.flex_wrap()
|
|
||||||
.when(thread_fresh_start, |this| this.justify_center())
|
|
||||||
.child(Label::new(
|
|
||||||
"To start using Zed AI, please read and accept the",
|
|
||||||
))
|
|
||||||
.child(terms_button),
|
|
||||||
)
|
|
||||||
.child({
|
|
||||||
match view_kind {
|
|
||||||
LanguageModelProviderTosView::TextThreadPopup => {
|
|
||||||
button_container.w_full().justify_end()
|
|
||||||
}
|
|
||||||
LanguageModelProviderTosView::Configuration => {
|
|
||||||
button_container.w_full().justify_start()
|
|
||||||
}
|
|
||||||
LanguageModelProviderTosView::ThreadFreshStart => {
|
|
||||||
button_container.w_full().justify_center()
|
|
||||||
}
|
|
||||||
LanguageModelProviderTosView::ThreadEmptyState => div().w_0(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CloudLanguageModel {
|
pub struct CloudLanguageModel {
|
||||||
id: LanguageModelId,
|
id: LanguageModelId,
|
||||||
model: Arc<cloud_llm_client::LanguageModel>,
|
model: Arc<cloud_llm_client::LanguageModel>,
|
||||||
|
@ -1107,10 +986,7 @@ struct ZedAiConfiguration {
|
||||||
plan: Option<Plan>,
|
plan: Option<Plan>,
|
||||||
subscription_period: Option<(DateTime<Utc>, DateTime<Utc>)>,
|
subscription_period: Option<(DateTime<Utc>, DateTime<Utc>)>,
|
||||||
eligible_for_trial: bool,
|
eligible_for_trial: bool,
|
||||||
has_accepted_terms_of_service: bool,
|
|
||||||
account_too_young: bool,
|
account_too_young: bool,
|
||||||
accept_terms_of_service_in_progress: bool,
|
|
||||||
accept_terms_of_service_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
|
|
||||||
sign_in_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
|
sign_in_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1176,58 +1052,30 @@ impl RenderOnce for ZedAiConfiguration {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
v_flex()
|
v_flex().gap_2().w_full().map(|this| {
|
||||||
.gap_2()
|
if self.account_too_young {
|
||||||
.w_full()
|
this.child(young_account_banner).child(
|
||||||
.when(!self.has_accepted_terms_of_service, |this| {
|
Button::new("upgrade", "Upgrade to Pro")
|
||||||
this.child(render_accept_terms(
|
.style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
|
||||||
LanguageModelProviderTosView::Configuration,
|
.full_width()
|
||||||
self.accept_terms_of_service_in_progress,
|
.on_click(|_, _, cx| cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx))),
|
||||||
{
|
)
|
||||||
let callback = self.accept_terms_of_service_callback.clone();
|
} else {
|
||||||
move |window, cx| (callback)(window, cx)
|
this.text_sm()
|
||||||
},
|
.child(subscription_text)
|
||||||
))
|
.child(manage_subscription_buttons)
|
||||||
})
|
}
|
||||||
.map(|this| {
|
})
|
||||||
if self.has_accepted_terms_of_service && self.account_too_young {
|
|
||||||
this.child(young_account_banner).child(
|
|
||||||
Button::new("upgrade", "Upgrade to Pro")
|
|
||||||
.style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
|
|
||||||
.full_width()
|
|
||||||
.on_click(|_, _, cx| {
|
|
||||||
cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else if self.has_accepted_terms_of_service {
|
|
||||||
this.text_sm()
|
|
||||||
.child(subscription_text)
|
|
||||||
.child(manage_subscription_buttons)
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.when(self.has_accepted_terms_of_service, |this| this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConfigurationView {
|
struct ConfigurationView {
|
||||||
state: Entity<State>,
|
state: Entity<State>,
|
||||||
accept_terms_of_service_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
|
|
||||||
sign_in_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
|
sign_in_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigurationView {
|
impl ConfigurationView {
|
||||||
fn new(state: Entity<State>) -> Self {
|
fn new(state: Entity<State>) -> Self {
|
||||||
let accept_terms_of_service_callback = Arc::new({
|
|
||||||
let state = state.clone();
|
|
||||||
move |_window: &mut Window, cx: &mut App| {
|
|
||||||
state.update(cx, |state, cx| {
|
|
||||||
state.accept_terms_of_service(cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let sign_in_callback = Arc::new({
|
let sign_in_callback = Arc::new({
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
move |_window: &mut Window, cx: &mut App| {
|
move |_window: &mut Window, cx: &mut App| {
|
||||||
|
@ -1239,7 +1087,6 @@ impl ConfigurationView {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
accept_terms_of_service_callback,
|
|
||||||
sign_in_callback,
|
sign_in_callback,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1255,10 +1102,7 @@ impl Render for ConfigurationView {
|
||||||
plan: user_store.plan(),
|
plan: user_store.plan(),
|
||||||
subscription_period: user_store.subscription_period(),
|
subscription_period: user_store.subscription_period(),
|
||||||
eligible_for_trial: user_store.trial_started_at().is_none(),
|
eligible_for_trial: user_store.trial_started_at().is_none(),
|
||||||
has_accepted_terms_of_service: state.has_accepted_terms_of_service(cx),
|
|
||||||
account_too_young: user_store.account_too_young(),
|
account_too_young: user_store.account_too_young(),
|
||||||
accept_terms_of_service_in_progress: state.accept_terms_of_service_task.is_some(),
|
|
||||||
accept_terms_of_service_callback: self.accept_terms_of_service_callback.clone(),
|
|
||||||
sign_in_callback: self.sign_in_callback.clone(),
|
sign_in_callback: self.sign_in_callback.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1283,7 +1127,6 @@ impl Component for ZedAiConfiguration {
|
||||||
plan: Option<Plan>,
|
plan: Option<Plan>,
|
||||||
eligible_for_trial: bool,
|
eligible_for_trial: bool,
|
||||||
account_too_young: bool,
|
account_too_young: bool,
|
||||||
has_accepted_terms_of_service: bool,
|
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
ZedAiConfiguration {
|
ZedAiConfiguration {
|
||||||
is_connected,
|
is_connected,
|
||||||
|
@ -1292,10 +1135,7 @@ impl Component for ZedAiConfiguration {
|
||||||
.is_some()
|
.is_some()
|
||||||
.then(|| (Utc::now(), Utc::now() + chrono::Duration::days(7))),
|
.then(|| (Utc::now(), Utc::now() + chrono::Duration::days(7))),
|
||||||
eligible_for_trial,
|
eligible_for_trial,
|
||||||
has_accepted_terms_of_service,
|
|
||||||
account_too_young,
|
account_too_young,
|
||||||
accept_terms_of_service_in_progress: false,
|
|
||||||
accept_terms_of_service_callback: Arc::new(|_, _| {}),
|
|
||||||
sign_in_callback: Arc::new(|_, _| {}),
|
sign_in_callback: Arc::new(|_, _| {}),
|
||||||
}
|
}
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
|
@ -1306,33 +1146,30 @@ impl Component for ZedAiConfiguration {
|
||||||
.p_4()
|
.p_4()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
.children(vec![
|
.children(vec![
|
||||||
single_example(
|
single_example("Not connected", configuration(false, None, false, false)),
|
||||||
"Not connected",
|
|
||||||
configuration(false, None, false, false, true),
|
|
||||||
),
|
|
||||||
single_example(
|
single_example(
|
||||||
"Accept Terms of Service",
|
"Accept Terms of Service",
|
||||||
configuration(true, None, true, false, false),
|
configuration(true, None, true, false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"No Plan - Not eligible for trial",
|
"No Plan - Not eligible for trial",
|
||||||
configuration(true, None, false, false, true),
|
configuration(true, None, false, false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"No Plan - Eligible for trial",
|
"No Plan - Eligible for trial",
|
||||||
configuration(true, None, true, false, true),
|
configuration(true, None, true, false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Free Plan",
|
"Free Plan",
|
||||||
configuration(true, Some(Plan::ZedFree), true, false, true),
|
configuration(true, Some(Plan::ZedFree), true, false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Zed Pro Trial Plan",
|
"Zed Pro Trial Plan",
|
||||||
configuration(true, Some(Plan::ZedProTrial), true, false, true),
|
configuration(true, Some(Plan::ZedProTrial), true, false),
|
||||||
),
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Zed Pro Plan",
|
"Zed Pro Plan",
|
||||||
configuration(true, Some(Plan::ZedPro), true, false, true),
|
configuration(true, Some(Plan::ZedPro), true, false),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
|
|
|
@ -75,13 +75,10 @@ pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
|
||||||
let new_provider = all_language_settings(None, cx).edit_predictions.provider;
|
let new_provider = all_language_settings(None, cx).edit_predictions.provider;
|
||||||
|
|
||||||
if new_provider != provider {
|
if new_provider != provider {
|
||||||
let tos_accepted = user_store.read(cx).has_accepted_terms_of_service();
|
|
||||||
|
|
||||||
telemetry::event!(
|
telemetry::event!(
|
||||||
"Edit Prediction Provider Changed",
|
"Edit Prediction Provider Changed",
|
||||||
from = provider,
|
from = provider,
|
||||||
to = new_provider,
|
to = new_provider,
|
||||||
zed_ai_tos_accepted = tos_accepted,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
provider = new_provider;
|
provider = new_provider;
|
||||||
|
@ -92,28 +89,6 @@ pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
|
||||||
user_store.clone(),
|
user_store.clone(),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !tos_accepted {
|
|
||||||
match provider {
|
|
||||||
EditPredictionProvider::Zed => {
|
|
||||||
let Some(window) = cx.active_window() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
window
|
|
||||||
.update(cx, |_, window, cx| {
|
|
||||||
window.dispatch_action(
|
|
||||||
Box::new(zed_actions::OpenZedPredictOnboarding),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
EditPredictionProvider::None
|
|
||||||
| EditPredictionProvider::Copilot
|
|
||||||
| EditPredictionProvider::Supermaven => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -118,12 +118,8 @@ impl Dismissable for ZedPredictUpsell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_show_upsell_modal(user_store: &Entity<UserStore>, cx: &App) -> bool {
|
pub fn should_show_upsell_modal() -> bool {
|
||||||
if user_store.read(cx).has_accepted_terms_of_service() {
|
!ZedPredictUpsell::dismissed()
|
||||||
!ZedPredictUpsell::dismissed()
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1547,16 +1543,6 @@ impl edit_prediction::EditPredictionProvider for ZetaEditPredictionProvider {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn needs_terms_acceptance(&self, cx: &App) -> bool {
|
|
||||||
!self
|
|
||||||
.zeta
|
|
||||||
.read(cx)
|
|
||||||
.user_store
|
|
||||||
.read(cx)
|
|
||||||
.has_accepted_terms_of_service()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_refreshing(&self) -> bool {
|
fn is_refreshing(&self) -> bool {
|
||||||
!self.pending_completions.is_empty()
|
!self.pending_completions.is_empty()
|
||||||
}
|
}
|
||||||
|
@ -1569,10 +1555,6 @@ impl edit_prediction::EditPredictionProvider for ZetaEditPredictionProvider {
|
||||||
_debounce: bool,
|
_debounce: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
if self.needs_terms_acceptance(cx) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.zeta.read(cx).update_required {
|
if self.zeta.read(cx).update_required {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue