From af06063d31f2f2e6c8b3785b5aeac088916ff264 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 22 Feb 2024 12:16:02 -0500 Subject: [PATCH] Add checkbox to only show installed extensions (#8208) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds a checkbox to the extensions view to allow filtering to just extensions that are installed: Screenshot 2024-02-22 at 12 05 40 PM Release Notes: - Added a checkbox to the extensions view to only show installed extensions. --- crates/extension/src/extension_store.rs | 2 +- crates/extensions_ui/src/extensions_ui.rs | 92 ++++++++++++++++++----- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index 5543f04d7d..d7fb92f012 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -34,7 +34,7 @@ pub struct ExtensionsApiResponse { pub data: Vec, } -#[derive(Deserialize)] +#[derive(Clone, Deserialize)] pub struct Extension { pub id: Arc, pub version: Arc, diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 9d9e331c08..d971e200eb 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -11,7 +11,7 @@ use settings::Settings; use std::time::Duration; use std::{ops::Range, sync::Arc}; use theme::ThemeSettings; -use ui::{prelude::*, Tooltip}; +use ui::{prelude::*, CheckboxWithLabel, Tooltip}; use workspace::{ item::{Item, ItemEvent}, @@ -34,7 +34,8 @@ pub struct ExtensionsPage { list: UniformListScrollHandle, telemetry: Arc, is_fetching_extensions: bool, - extensions_entries: Vec, + is_only_showing_installed_extensions: bool, + extension_entries: Vec, query_editor: View, query_contains_error: bool, _subscription: gpui::Subscription, @@ -54,7 +55,8 @@ impl ExtensionsPage { list: UniformListScrollHandle::new(), telemetry: workspace.client().telemetry().clone(), is_fetching_extensions: false, - extensions_entries: Vec::new(), + is_only_showing_installed_extensions: false, + extension_entries: Vec::new(), query_contains_error: false, extension_fetch_task: None, _subscription: subscription, @@ -65,6 +67,24 @@ impl ExtensionsPage { }) } + fn filtered_extension_entries(&self, cx: &mut ViewContext) -> Vec { + let extension_store = ExtensionStore::global(cx).read(cx); + + self.extension_entries + .iter() + .filter(|extension| { + if self.is_only_showing_installed_extensions { + let status = extension_store.extension_status(&extension.id); + + matches!(status, ExtensionStatus::Installed(_)) + } else { + true + } + }) + .cloned() + .collect::>() + } + fn install_extension( &self, extension_id: Arc, @@ -94,7 +114,7 @@ impl ExtensionsPage { let fetch_result = extensions.await; match fetch_result { Ok(extensions) => this.update(&mut cx, |this, cx| { - this.extensions_entries = extensions; + this.extension_entries = extensions; this.is_fetching_extensions = false; cx.notify(); }), @@ -113,7 +133,7 @@ impl ExtensionsPage { } fn render_extensions(&mut self, range: Range, cx: &mut ViewContext) -> Vec
{ - self.extensions_entries[range] + self.filtered_extension_entries(cx)[range] .iter() .map(|extension| self.render_entry(extension, cx)) .collect() @@ -381,10 +401,32 @@ impl ExtensionsPage { Some(search) } } + + fn render_empty_state(&self, cx: &mut ViewContext) -> impl IntoElement { + let has_search = self.search_query(cx).is_some(); + + let message = if self.is_fetching_extensions { + "Loading extensions..." + } else if self.is_only_showing_installed_extensions { + if has_search { + "No installed extensions that match your search." + } else { + "No installed extensions." + } + } else { + if has_search { + "No extensions that match your search." + } else { + "No extensions." + } + }; + + Label::new(message) + } } impl Render for ExtensionsPage { - fn render(&mut self, cx: &mut gpui::ViewContext) -> impl IntoElement { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_flex() .size_full() .p_4() @@ -395,25 +437,39 @@ impl Render for ExtensionsPage { .w_full() .child(Headline::new("Extensions").size(HeadlineSize::XLarge)), ) - .child(h_flex().w_56().child(self.render_search(cx))) + .child( + h_flex() + .w_full() + .gap_2() + .child(h_flex().child(self.render_search(cx))) + .child(CheckboxWithLabel::new( + "installed", + Label::new("Only show installed"), + if self.is_only_showing_installed_extensions { + Selection::Selected + } else { + Selection::Unselected + }, + cx.listener(|this, selection, _cx| { + this.is_only_showing_installed_extensions = match selection { + Selection::Selected => true, + Selection::Unselected => false, + Selection::Indeterminate => return, + } + }), + )), + ) .child(v_flex().size_full().overflow_y_hidden().map(|this| { - if self.extensions_entries.is_empty() { - let message = if self.is_fetching_extensions { - "Loading extensions..." - } else if self.search_query(cx).is_some() { - "No extensions that match your search." - } else { - "No extensions." - }; - - return this.child(Label::new(message)); + let entries = self.filtered_extension_entries(cx); + if entries.is_empty() { + return this.child(self.render_empty_state(cx)); } this.child( canvas({ let view = cx.view().clone(); let scroll_handle = self.list.clone(); - let item_count = self.extensions_entries.len(); + let item_count = entries.len(); move |bounds, cx| { uniform_list::<_, Div, _>( view,