onboarding: Add young account treatment to AI upsell card (#35785)

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-08-07 10:42:46 -03:00 committed by GitHub
parent 03876d076e
commit e227b5ac30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 139 additions and 75 deletions

View file

@ -1,26 +1,33 @@
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use client::{Client, zed_urls}; use client::{Client, UserStore, zed_urls};
use cloud_llm_client::Plan; use cloud_llm_client::Plan;
use gpui::{ use gpui::{
Animation, AnimationExt, AnyElement, App, IntoElement, RenderOnce, Transformation, Window, Animation, AnimationExt, AnyElement, App, Entity, IntoElement, RenderOnce, Transformation,
percentage, Window, percentage,
}; };
use ui::{Divider, Vector, VectorName, prelude::*}; use ui::{Divider, Vector, VectorName, prelude::*};
use crate::{SignInStatus, plan_definitions::PlanDefinitions}; use crate::{SignInStatus, YoungAccountBanner, plan_definitions::PlanDefinitions};
#[derive(IntoElement, RegisterComponent)] #[derive(IntoElement, RegisterComponent)]
pub struct AiUpsellCard { pub struct AiUpsellCard {
pub sign_in_status: SignInStatus, pub sign_in_status: SignInStatus,
pub sign_in: Arc<dyn Fn(&mut Window, &mut App)>, pub sign_in: Arc<dyn Fn(&mut Window, &mut App)>,
pub account_too_young: bool,
pub user_plan: Option<Plan>, pub user_plan: Option<Plan>,
pub tab_index: Option<isize>, pub tab_index: Option<isize>,
} }
impl AiUpsellCard { impl AiUpsellCard {
pub fn new(client: Arc<Client>, user_plan: Option<Plan>) -> Self { pub fn new(
client: Arc<Client>,
user_store: &Entity<UserStore>,
user_plan: Option<Plan>,
cx: &mut App,
) -> Self {
let status = *client.status().borrow(); let status = *client.status().borrow();
let store = user_store.read(cx);
Self { Self {
user_plan, user_plan,
@ -32,6 +39,7 @@ impl AiUpsellCard {
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);
}), }),
account_too_young: store.account_too_young(),
tab_index: None, tab_index: None,
} }
} }
@ -40,6 +48,7 @@ impl AiUpsellCard {
impl RenderOnce for AiUpsellCard { impl RenderOnce for AiUpsellCard {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let plan_definitions = PlanDefinitions; let plan_definitions = PlanDefinitions;
let young_account_banner = YoungAccountBanner;
let pro_section = v_flex() let pro_section = v_flex()
.flex_grow() .flex_grow()
@ -158,36 +167,70 @@ impl RenderOnce for AiUpsellCard {
SignInStatus::SignedIn => match self.user_plan { SignInStatus::SignedIn => match self.user_plan {
None | Some(Plan::ZedFree) => card None | Some(Plan::ZedFree) => card
.child(Label::new("Try Zed AI").size(LabelSize::Large)) .child(Label::new("Try Zed AI").size(LabelSize::Large))
.child( .map(|this| {
div() if self.account_too_young {
.max_w_3_4() this.child(young_account_banner).child(
.mb_2() v_flex()
.child(Label::new(description).color(Color::Muted)), .mt_2()
) .gap_1()
.child(plans_section) .child(
.child( h_flex()
footer_container .gap_2()
.child( .child(
Button::new("start_trial", "Start 14-day Free Pro Trial") Label::new("Pro")
.full_width() .size(LabelSize::Small)
.style(ButtonStyle::Tinted(ui::TintColor::Accent)) .color(Color::Accent)
.when_some(self.tab_index, |this, tab_index| { .buffer_font(cx),
this.tab_index(tab_index) )
}) .child(Divider::horizontal()),
.on_click(move |_, _window, cx| { )
telemetry::event!( .child(plan_definitions.pro_plan(true))
"Start Trial Clicked", .child(
state = "post-sign-in" Button::new("pro", "Get Started")
); .full_width()
cx.open_url(&zed_urls::start_trial_url(cx)) .style(ButtonStyle::Tinted(ui::TintColor::Accent))
}), .on_click(move |_, _window, cx| {
telemetry::event!(
"Upgrade To Pro Clicked",
state = "young-account"
);
cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx))
}),
),
) )
} else {
this.child(
div()
.max_w_3_4()
.mb_2()
.child(Label::new(description).color(Color::Muted)),
)
.child(plans_section)
.child( .child(
Label::new("No credit card required") footer_container
.size(LabelSize::Small) .child(
.color(Color::Muted), Button::new("start_trial", "Start 14-day Free Pro Trial")
), .full_width()
), .style(ButtonStyle::Tinted(ui::TintColor::Accent))
.when_some(self.tab_index, |this, tab_index| {
this.tab_index(tab_index)
})
.on_click(move |_, _window, cx| {
telemetry::event!(
"Start Trial Clicked",
state = "post-sign-in"
);
cx.open_url(&zed_urls::start_trial_url(cx))
}),
)
.child(
Label::new("No credit card required")
.size(LabelSize::Small)
.color(Color::Muted),
),
)
}
}),
Some(Plan::ZedProTrial) => card Some(Plan::ZedProTrial) => card
.child(pro_trial_stamp) .child(pro_trial_stamp)
.child(Label::new("You're in the Zed Pro Trial").size(LabelSize::Large)) .child(Label::new("You're in the Zed Pro Trial").size(LabelSize::Large))
@ -255,48 +298,68 @@ impl Component for AiUpsellCard {
Some( Some(
v_flex() v_flex()
.gap_4() .gap_4()
.children(vec![example_group(vec![ .items_center()
single_example( .max_w_4_5()
"Signed Out State", .child(single_example(
AiUpsellCard { "Signed Out State",
sign_in_status: SignInStatus::SignedOut, AiUpsellCard {
sign_in: Arc::new(|_, _| {}), sign_in_status: SignInStatus::SignedOut,
user_plan: None, sign_in: Arc::new(|_, _| {}),
tab_index: Some(0), account_too_young: false,
} user_plan: None,
.into_any_element(), tab_index: Some(0),
), }
single_example( .into_any_element(),
"Free Plan", ))
AiUpsellCard { .child(example_group_with_title(
sign_in_status: SignInStatus::SignedIn, "Signed In States",
sign_in: Arc::new(|_, _| {}), vec![
user_plan: Some(Plan::ZedFree), single_example(
tab_index: Some(1), "Free Plan",
} AiUpsellCard {
.into_any_element(), sign_in_status: SignInStatus::SignedIn,
), sign_in: Arc::new(|_, _| {}),
single_example( account_too_young: false,
"Pro Trial", user_plan: Some(Plan::ZedFree),
AiUpsellCard { tab_index: Some(1),
sign_in_status: SignInStatus::SignedIn, }
sign_in: Arc::new(|_, _| {}), .into_any_element(),
user_plan: Some(Plan::ZedProTrial), ),
tab_index: Some(1), single_example(
} "Free Plan but Young Account",
.into_any_element(), AiUpsellCard {
), sign_in_status: SignInStatus::SignedIn,
single_example( sign_in: Arc::new(|_, _| {}),
"Pro Plan", account_too_young: true,
AiUpsellCard { user_plan: Some(Plan::ZedFree),
sign_in_status: SignInStatus::SignedIn, tab_index: Some(1),
sign_in: Arc::new(|_, _| {}), }
user_plan: Some(Plan::ZedPro), .into_any_element(),
tab_index: Some(1), ),
} single_example(
.into_any_element(), "Pro Trial",
), AiUpsellCard {
])]) sign_in_status: SignInStatus::SignedIn,
sign_in: Arc::new(|_, _| {}),
account_too_young: false,
user_plan: Some(Plan::ZedProTrial),
tab_index: Some(1),
}
.into_any_element(),
),
single_example(
"Pro Plan",
AiUpsellCard {
sign_in_status: SignInStatus::SignedIn,
sign_in: Arc::new(|_, _| {}),
account_too_young: false,
user_plan: Some(Plan::ZedPro),
tab_index: Some(1),
}
.into_any_element(),
),
],
))
.into_any_element(), .into_any_element(),
) )
} }

View file

@ -286,6 +286,7 @@ pub(crate) fn render_ai_setup_page(
.child(AiUpsellCard { .child(AiUpsellCard {
sign_in_status: SignInStatus::SignedIn, sign_in_status: SignInStatus::SignedIn,
sign_in: Arc::new(|_, _| {}), sign_in: Arc::new(|_, _| {}),
account_too_young: user_store.read(cx).account_too_young(),
user_plan: user_store.read(cx).plan(), user_plan: user_store.read(cx).plan(),
tab_index: Some({ tab_index: Some({
tab_index += 1; tab_index += 1;