onboarding: Add more telemetry (#36121)

Anthony Eid and Marshall Bowers created

1. Welcome Page Open
2. Welcome Nav clicked
3. Skip clicked
4. Font changed
5. Import settings clicked
6. Inlay Hints
7. Git Blame
8. Format on Save
9. Font Ligature
10. Ai Enabled
11. Ai Provider Modal open


Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>

Change summary

Cargo.lock                             |  1 
crates/onboarding/Cargo.toml           |  1 
crates/onboarding/src/ai_setup_page.rs | 24 +++++++++--
crates/onboarding/src/editing_page.rs  | 59 ++++++++++++++++++++++++---
crates/onboarding/src/onboarding.rs    | 45 ++++++++++++++++++---
5 files changed, 111 insertions(+), 19 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -11172,6 +11172,7 @@ dependencies = [
  "schemars",
  "serde",
  "settings",
+ "telemetry",
  "theme",
  "ui",
  "util",

crates/onboarding/Cargo.toml 🔗

@@ -38,6 +38,7 @@ project.workspace = true
 schemars.workspace = true
 serde.workspace = true
 settings.workspace = true
+telemetry.workspace = true
 theme.workspace = true
 ui.workspace = true
 util.workspace = true

crates/onboarding/src/ai_setup_page.rs 🔗

@@ -188,6 +188,11 @@ fn render_llm_provider_card(
                                 workspace
                                     .update(cx, |workspace, cx| {
                                         workspace.toggle_modal(window, cx, |window, cx| {
+                                            telemetry::event!(
+                                                "Welcome AI Modal Opened",
+                                                provider = provider.name().0,
+                                            );
+
                                             let modal = AiConfigurationModal::new(
                                                 provider.clone(),
                                                 window,
@@ -245,16 +250,25 @@ pub(crate) fn render_ai_setup_page(
                     ToggleState::Selected
                 },
                 |&toggle_state, _, cx| {
+                    let enabled = match toggle_state {
+                        ToggleState::Indeterminate => {
+                            return;
+                        }
+                        ToggleState::Unselected => true,
+                        ToggleState::Selected => false,
+                    };
+
+                    telemetry::event!(
+                        "Welcome AI Enabled",
+                        toggle = if enabled { "on" } else { "off" },
+                    );
+
                     let fs = <dyn Fs>::global(cx);
                     update_settings_file::<DisableAiSettings>(
                         fs,
                         cx,
                         move |ai_settings: &mut Option<bool>, _| {
-                            *ai_settings = match toggle_state {
-                                ToggleState::Indeterminate => None,
-                                ToggleState::Unselected => Some(true),
-                                ToggleState::Selected => Some(false),
-                            };
+                            *ai_settings = Some(enabled);
                         },
                     );
                 },

crates/onboarding/src/editing_page.rs 🔗

@@ -35,6 +35,11 @@ fn write_show_mini_map(show: ShowMinimap, cx: &mut App) {
     EditorSettings::override_global(curr_settings, cx);
 
     update_settings_file::<EditorSettings>(fs, cx, move |editor_settings, _| {
+        telemetry::event!(
+            "Welcome Minimap Clicked",
+            from = editor_settings.minimap.unwrap_or_default(),
+            to = show
+        );
         editor_settings.minimap.get_or_insert_default().show = Some(show);
     });
 }
@@ -71,7 +76,7 @@ fn read_git_blame(cx: &App) -> bool {
     ProjectSettings::get_global(cx).git.inline_blame_enabled()
 }
 
-fn set_git_blame(enabled: bool, cx: &mut App) {
+fn write_git_blame(enabled: bool, cx: &mut App) {
     let fs = <dyn Fs>::global(cx);
 
     let mut curr_settings = ProjectSettings::get_global(cx).clone();
@@ -95,6 +100,12 @@ fn write_ui_font_family(font: SharedString, cx: &mut App) {
     let fs = <dyn Fs>::global(cx);
 
     update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
+        telemetry::event!(
+            "Welcome Font Changed",
+            type = "ui font",
+            old = theme_settings.ui_font_family,
+            new = font.clone()
+        );
         theme_settings.ui_font_family = Some(FontFamilyName(font.into()));
     });
 }
@@ -119,6 +130,13 @@ fn write_buffer_font_family(font_family: SharedString, cx: &mut App) {
     let fs = <dyn Fs>::global(cx);
 
     update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
+        telemetry::event!(
+            "Welcome Font Changed",
+            type = "editor font",
+            old = theme_settings.buffer_font_family,
+            new = font_family.clone()
+        );
+
         theme_settings.buffer_font_family = Some(FontFamilyName(font_family.into()));
     });
 }
@@ -197,7 +215,7 @@ fn render_setting_import_button(
                                     .color(Color::Muted)
                                     .size(IconSize::XSmall),
                             )
-                            .child(Label::new(label)),
+                            .child(Label::new(label.clone())),
                     )
                     .when(imported, |this| {
                         this.child(
@@ -212,7 +230,10 @@ fn render_setting_import_button(
                         )
                     }),
             )
-            .on_click(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)),
+            .on_click(move |_, window, cx| {
+                telemetry::event!("Welcome Import Settings", import_source = label,);
+                window.dispatch_action(action.boxed_clone(), cx);
+            }),
     )
 }
 
@@ -605,7 +626,13 @@ fn render_popular_settings_section(
                     ui::ToggleState::Unselected
                 },
                 |toggle_state, _, cx| {
-                    write_font_ligatures(toggle_state == &ToggleState::Selected, cx);
+                    let enabled = toggle_state == &ToggleState::Selected;
+                    telemetry::event!(
+                        "Welcome Font Ligature",
+                        options = if enabled { "on" } else { "off" },
+                    );
+
+                    write_font_ligatures(enabled, cx);
                 },
             )
             .tab_index({
@@ -625,7 +652,13 @@ fn render_popular_settings_section(
                     ui::ToggleState::Unselected
                 },
                 |toggle_state, _, cx| {
-                    write_format_on_save(toggle_state == &ToggleState::Selected, cx);
+                    let enabled = toggle_state == &ToggleState::Selected;
+                    telemetry::event!(
+                        "Welcome Format On Save Changed",
+                        options = if enabled { "on" } else { "off" },
+                    );
+
+                    write_format_on_save(enabled, cx);
                 },
             )
             .tab_index({
@@ -644,7 +677,13 @@ fn render_popular_settings_section(
                     ui::ToggleState::Unselected
                 },
                 |toggle_state, _, cx| {
-                    write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
+                    let enabled = toggle_state == &ToggleState::Selected;
+                    telemetry::event!(
+                        "Welcome Inlay Hints Changed",
+                        options = if enabled { "on" } else { "off" },
+                    );
+
+                    write_inlay_hints(enabled, cx);
                 },
             )
             .tab_index({
@@ -663,7 +702,13 @@ fn render_popular_settings_section(
                     ui::ToggleState::Unselected
                 },
                 |toggle_state, _, cx| {
-                    set_git_blame(toggle_state == &ToggleState::Selected, cx);
+                    let enabled = toggle_state == &ToggleState::Selected;
+                    telemetry::event!(
+                        "Welcome Git Blame Changed",
+                        options = if enabled { "on" } else { "off" },
+                    );
+
+                    write_git_blame(enabled, cx);
                 },
             )
             .tab_index({

crates/onboarding/src/onboarding.rs 🔗

@@ -214,6 +214,7 @@ pub fn init(cx: &mut App) {
 }
 
 pub fn show_onboarding_view(app_state: Arc<AppState>, cx: &mut App) -> Task<anyhow::Result<()>> {
+    telemetry::event!("Onboarding Page Opened");
     open_new(
         Default::default(),
         app_state,
@@ -242,6 +243,16 @@ enum SelectedPage {
     AiSetup,
 }
 
+impl SelectedPage {
+    fn name(&self) -> &'static str {
+        match self {
+            SelectedPage::Basics => "Basics",
+            SelectedPage::Editing => "Editing",
+            SelectedPage::AiSetup => "AI Setup",
+        }
+    }
+}
+
 struct Onboarding {
     workspace: WeakEntity<Workspace>,
     focus_handle: FocusHandle,
@@ -261,7 +272,21 @@ impl Onboarding {
         })
     }
 
-    fn set_page(&mut self, page: SelectedPage, cx: &mut Context<Self>) {
+    fn set_page(
+        &mut self,
+        page: SelectedPage,
+        clicked: Option<&'static str>,
+        cx: &mut Context<Self>,
+    ) {
+        if let Some(click) = clicked {
+            telemetry::event!(
+                "Welcome Tab Clicked",
+                from = self.selected_page.name(),
+                to = page.name(),
+                clicked = click,
+            );
+        }
+
         self.selected_page = page;
         cx.notify();
         cx.emit(ItemEvent::UpdateTab);
@@ -325,8 +350,13 @@ impl Onboarding {
                     gpui::Empty.into_any_element(),
                     IntoElement::into_any_element,
                 ))
-                .on_click(cx.listener(move |this, _, _, cx| {
-                    this.set_page(page, cx);
+                .on_click(cx.listener(move |this, click_event, _, cx| {
+                    let click = match click_event {
+                        gpui::ClickEvent::Mouse(_) => "mouse",
+                        gpui::ClickEvent::Keyboard(_) => "keyboard",
+                    };
+
+                    this.set_page(page, Some(click), cx);
                 }))
         })
     }
@@ -475,6 +505,7 @@ impl Onboarding {
     }
 
     fn on_finish(_: &Finish, _: &mut Window, cx: &mut App) {
+        telemetry::event!("Welcome Skip Clicked");
         go_to_welcome_page(cx);
     }
 
@@ -532,13 +563,13 @@ impl Render for Onboarding {
             .on_action(Self::handle_sign_in)
             .on_action(Self::handle_open_account)
             .on_action(cx.listener(|this, _: &ActivateBasicsPage, _, cx| {
-                this.set_page(SelectedPage::Basics, cx);
+                this.set_page(SelectedPage::Basics, Some("action"), cx);
             }))
             .on_action(cx.listener(|this, _: &ActivateEditingPage, _, cx| {
-                this.set_page(SelectedPage::Editing, cx);
+                this.set_page(SelectedPage::Editing, Some("action"), cx);
             }))
             .on_action(cx.listener(|this, _: &ActivateAISetupPage, _, cx| {
-                this.set_page(SelectedPage::AiSetup, cx);
+                this.set_page(SelectedPage::AiSetup, Some("action"), cx);
             }))
             .on_action(cx.listener(|_, _: &menu::SelectNext, window, cx| {
                 window.focus_next();
@@ -806,7 +837,7 @@ impl workspace::SerializableItem for Onboarding {
                     if let Some(page) = page {
                         zlog::info!("Onboarding page {page:?} loaded");
                         onboarding_page.update(cx, |onboarding_page, cx| {
-                            onboarding_page.set_page(page, cx);
+                            onboarding_page.set_page(page, None, cx);
                         })
                     }
                     onboarding_page