extensions_ui: Add telemetry for docs click-throughs from feature upsells (#14583)

Marshall Bowers created

This PR adds some telemetry when the "View docs" button is clicked on a
feature upsell.

The goal here is to get a sense for how effective these upsells are at
getting users to click through if they can't find what they're looking
for.

Release Notes:

- N/A

Change summary

crates/extensions_ui/src/components/feature_upsell.rs | 15 +++++
crates/extensions_ui/src/extensions_ui.rs             | 30 ++++++++----
2 files changed, 33 insertions(+), 12 deletions(-)

Detailed changes

crates/extensions_ui/src/components/feature_upsell.rs 🔗

@@ -1,3 +1,6 @@
+use std::sync::Arc;
+
+use client::telemetry::Telemetry;
 use gpui::{AnyElement, Div, StyleRefinement};
 use smallvec::SmallVec;
 use ui::{prelude::*, ButtonLike};
@@ -5,15 +8,17 @@ use ui::{prelude::*, ButtonLike};
 #[derive(IntoElement)]
 pub struct FeatureUpsell {
     base: Div,
+    telemetry: Arc<Telemetry>,
     text: SharedString,
     docs_url: Option<SharedString>,
     children: SmallVec<[AnyElement; 2]>,
 }
 
 impl FeatureUpsell {
-    pub fn new(text: impl Into<SharedString>) -> Self {
+    pub fn new(telemetry: Arc<Telemetry>, text: impl Into<SharedString>) -> Self {
         Self {
             base: h_flex(),
+            telemetry,
             text: text.into(),
             docs_url: None,
             children: SmallVec::new(),
@@ -62,8 +67,14 @@ impl RenderOnce for FeatureUpsell {
                                     .child(Icon::new(IconName::ArrowUpRight)),
                             )
                             .on_click({
+                                let telemetry = self.telemetry.clone();
                                 let docs_url = docs_url.clone();
-                                move |_event, cx| cx.open_url(&docs_url)
+                                move |_event, cx| {
+                                    telemetry.report_app_event(format!(
+                                        "feature upsell: viewed docs ({docs_url})"
+                                    ));
+                                    cx.open_url(&docs_url)
+                                }
                             }),
                     )
                 },

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -939,12 +939,14 @@ impl ExtensionsPage {
         let upsells_count = self.upsells.len();
 
         v_flex().children(self.upsells.iter().enumerate().map(|(ix, feature)| {
+            let telemetry = self.telemetry.clone();
             let upsell = match feature {
                 Feature::Git => FeatureUpsell::new(
+                    telemetry,
                     "Zed comes with basic Git support. More Git features are coming in the future.",
                 )
                 .docs_url("https://zed.dev/docs/git"),
-                Feature::Vim => FeatureUpsell::new("Vim support is built-in to Zed!")
+                Feature::Vim => FeatureUpsell::new(telemetry, "Vim support is built-in to Zed!")
                     .docs_url("https://zed.dev/docs/vim")
                     .child(CheckboxWithLabel::new(
                         "enable-vim",
@@ -956,7 +958,7 @@ impl ExtensionsPage {
                         },
                         cx.listener(move |this, selection, cx| {
                             this.telemetry
-                                .report_app_event("extensions: toggle vim".to_string());
+                                .report_app_event("feature upsell: toggle vim".to_string());
                             this.update_settings::<VimModeSetting>(
                                 selection,
                                 cx,
@@ -964,14 +966,22 @@ impl ExtensionsPage {
                             );
                         }),
                     )),
-                Feature::LanguageC => FeatureUpsell::new("C support is built-in to Zed!")
-                    .docs_url("https://zed.dev/docs/languages/c"),
-                Feature::LanguageCpp => FeatureUpsell::new("C++ support is built-in to Zed!")
-                    .docs_url("https://zed.dev/docs/languages/cpp"),
-                Feature::LanguagePython => FeatureUpsell::new("Python support is built-in to Zed!")
-                    .docs_url("https://zed.dev/docs/languages/python"),
-                Feature::LanguageRust => FeatureUpsell::new("Rust support is built-in to Zed!")
-                    .docs_url("https://zed.dev/docs/languages/rust"),
+                Feature::LanguageC => {
+                    FeatureUpsell::new(telemetry, "C support is built-in to Zed!")
+                        .docs_url("https://zed.dev/docs/languages/c")
+                }
+                Feature::LanguageCpp => {
+                    FeatureUpsell::new(telemetry, "C++ support is built-in to Zed!")
+                        .docs_url("https://zed.dev/docs/languages/cpp")
+                }
+                Feature::LanguagePython => {
+                    FeatureUpsell::new(telemetry, "Python support is built-in to Zed!")
+                        .docs_url("https://zed.dev/docs/languages/python")
+                }
+                Feature::LanguageRust => {
+                    FeatureUpsell::new(telemetry, "Rust support is built-in to Zed!")
+                        .docs_url("https://zed.dev/docs/languages/rust")
+                }
             };
 
             upsell.when(ix < upsells_count, |upsell| upsell.border_b_1())