Cargo.lock 🔗
@@ -18952,6 +18952,7 @@ dependencies = [
"paths",
"postage",
"project",
+ "proto",
"regex",
"release_channel",
"reqwest_client",
Marshall Bowers created
This PR updates the edit prediction onboarding modal with steps about
subscribing to a plan.
When the user is not subscribed to a plan, we display a link to the
account page to sign up for one:
<img width="612" alt="Screenshot 2025-05-09 at 6 04 05 PM"
src="https://github.com/user-attachments/assets/0300194a-c419-43d9-8214-080674d31e12"
/>
If the user is already subscribed to a plan we indicate which plan they
are on and how many edit predictions they get with it:
<img width="616" alt="Screenshot 2025-05-09 at 6 03 16 PM"
src="https://github.com/user-attachments/assets/e2506096-e499-41f2-ba1f-fca768cb48b9"
/>
<img width="595" alt="Screenshot 2025-05-09 at 5 46 18 PM"
src="https://github.com/user-attachments/assets/de82f8c2-cad8-45fb-8988-26606a8dc3e1"
/>
Release Notes:
- N/A
Cargo.lock | 1
crates/inline_completion_button/src/inline_completion_button.rs | 10
crates/zeta/Cargo.toml | 1
crates/zeta/src/onboarding_modal.rs | 49 ++
4 files changed, 57 insertions(+), 4 deletions(-)
@@ -18952,6 +18952,7 @@ dependencies = [
"paths",
"postage",
"project",
+ "proto",
"regex",
"release_channel",
"reqwest_client",
@@ -237,11 +237,17 @@ impl Render for InlineCompletionButton {
let current_user_terms_accepted =
self.user_store.read(cx).current_user_has_accepted_terms();
+ let has_subscription = self.user_store.read(cx).current_plan().is_some()
+ && self.user_store.read(cx).subscription_period().is_some();
- if !current_user_terms_accepted.unwrap_or(false) {
+ if !has_subscription || !current_user_terms_accepted.unwrap_or(false) {
let signed_in = current_user_terms_accepted.is_some();
let tooltip_meta = if signed_in {
- "Read Terms of Service"
+ if has_subscription {
+ "Read Terms of Service"
+ } else {
+ "Choose a Plan"
+ }
} else {
"Sign in to use"
};
@@ -39,6 +39,7 @@ migrator.workspace = true
paths.workspace = true
postage.workspace = true
project.workspace = true
+proto.workspace = true
regex.workspace = true
release_channel.workspace = true
serde.workspace = true
@@ -2,7 +2,7 @@ use std::{sync::Arc, time::Duration};
use crate::{ZED_PREDICT_DATA_COLLECTION_CHOICE, onboarding_event};
use anyhow::Context as _;
-use client::{Client, UserStore};
+use client::{Client, UserStore, zed_urls};
use db::kvp::KEY_VALUE_STORE;
use fs::Fs;
use gpui::{
@@ -246,6 +246,12 @@ impl Render for ZedPredictModal {
let window_height = window.viewport_size().height;
let max_height = window_height - px(200.);
+ let has_subscription_period = self.user_store.read(cx).subscription_period().is_some();
+ let plan = self.user_store.read(cx).current_plan().filter(|_| {
+ // Since the user might be on the legacy free plan we filter based on whether we have a subscription period.
+ has_subscription_period
+ });
+
let base = v_flex()
.id("edit-prediction-onboarding")
.key_context("ZedPredictModal")
@@ -377,6 +383,45 @@ impl Render for ZedPredictModal {
};
base.child(Label::new(copy).color(Color::Muted))
+ .child(h_flex().map(|parent| {
+ if let Some(plan) = plan {
+ parent.child(
+ Checkbox::new("plan", ToggleState::Selected)
+ .fill()
+ .disabled(true)
+ .label(format!(
+ "You get {} edit predictions through your {}.",
+ if plan == proto::Plan::Free {
+ "2,000"
+ } else {
+ "unlimited"
+ },
+ match plan {
+ proto::Plan::Free => "Zed Free plan",
+ proto::Plan::ZedPro => "Zed Pro plan",
+ proto::Plan::ZedProTrial => "Zed Pro trial",
+ }
+ )),
+ )
+ } else {
+ parent
+ .child(
+ Checkbox::new("plan-required", ToggleState::Unselected)
+ .fill()
+ .disabled(true)
+ .label("To get started with edit prediction"),
+ )
+ .child(
+ Button::new("subscribe", "choose a plan")
+ .icon(IconName::ArrowUpRight)
+ .icon_size(IconSize::Indicator)
+ .icon_color(Color::Muted)
+ .on_click(|_event, _window, cx| {
+ cx.open_url(&zed_urls::account_url(cx));
+ }),
+ )
+ }
+ }))
.child(
h_flex()
.child(
@@ -447,7 +492,7 @@ impl Render for ZedPredictModal {
.w_full()
.child(
Button::new("accept-tos", "Enable Edit Prediction")
- .disabled(!self.terms_of_service)
+ .disabled(plan.is_none() || !self.terms_of_service)
.style(ButtonStyle::Tinted(TintColor::Accent))
.full_width()
.on_click(cx.listener(Self::accept_and_enable)),