Improve catalog view with relative timestamps, auto-refresh, and installed badges

Show app count with relative last-refreshed time in subtitle. Auto-refresh
catalog on first visit when empty. Show "Installed" badge on catalog entries
that match already-installed AppImages instead of the Install button.
This commit is contained in:
lashman
2026-02-28 01:38:01 +02:00
parent 46f46db98c
commit df186e74db

View File

@@ -95,20 +95,10 @@ pub fn build_catalog_page(db: &Rc<Database>) -> adw::NavigationPage {
stack.add_named(&scrolled, Some("results")); stack.add_named(&scrolled, Some("results"));
// Show empty or results based on catalog data // Show empty or results based on catalog data
let sources = catalog::get_sources(db);
let enabled_sources: Vec<_> = sources.iter().filter(|s| s.enabled).collect();
let app_count = db.catalog_app_count().unwrap_or(0); let app_count = db.catalog_app_count().unwrap_or(0);
if app_count > 0 { if app_count > 0 {
stack.set_visible_child_name("results"); stack.set_visible_child_name("results");
if let Some(src) = enabled_sources.first() { update_catalog_subtitle(&title, app_count);
let synced = src.last_synced.as_deref().unwrap_or("never");
title.set_subtitle(&format!(
"{} apps from {} ({}), synced: {}",
src.app_count, src.name, src.source_type.as_str(), synced,
));
} else {
title.set_subtitle(&format!("{} apps available", app_count));
}
} else { } else {
stack.set_visible_child_name("empty"); stack.set_visible_child_name("empty");
} }
@@ -195,7 +185,7 @@ pub fn build_catalog_page(db: &Rc<Database>) -> adw::NavigationPage {
toast_c.add_toast(adw::Toast::new( toast_c.add_toast(adw::Toast::new(
&format!("Catalog refreshed: {} apps", count), &format!("Catalog refreshed: {} apps", count),
)); ));
title_c.set_subtitle(&format!("{} apps available", count)); update_catalog_subtitle(&title_c, count as i64);
stack_c.set_visible_child_name("results"); stack_c.set_visible_child_name("results");
populate_categories(&db_c, &cat_box_c, &active_cat_c, &results_c, &search_c); populate_categories(&db_c, &cat_box_c, &active_cat_c, &results_c, &search_c);
populate_results(&db_c, "", None, &results_c, &toast_c); populate_results(&db_c, "", None, &results_c, &toast_c);
@@ -221,6 +211,11 @@ pub fn build_catalog_page(db: &Rc<Database>) -> adw::NavigationPage {
wire_refresh(&refresh_btn); wire_refresh(&refresh_btn);
wire_refresh(&refresh_header_btn); wire_refresh(&refresh_header_btn);
// Auto-refresh on first visit when catalog is empty
if app_count == 0 {
refresh_btn.emit_clicked();
}
adw::NavigationPage::builder() adw::NavigationPage::builder()
.title(&i18n("Catalog")) .title(&i18n("Catalog"))
.tag("catalog") .tag("catalog")
@@ -228,6 +223,18 @@ pub fn build_catalog_page(db: &Rc<Database>) -> adw::NavigationPage {
.build() .build()
} }
/// Update the catalog subtitle to show app count and relative last-refreshed time.
fn update_catalog_subtitle(title: &adw::WindowTitle, app_count: i64) {
let settings = gtk::gio::Settings::new(crate::config::APP_ID);
let last_refreshed = settings.string("catalog-last-refreshed");
if last_refreshed.is_empty() {
title.set_subtitle(&format!("{} apps available", app_count));
} else {
let relative = widgets::relative_time(&last_refreshed);
title.set_subtitle(&format!("{} apps - Refreshed {}", app_count, relative));
}
}
fn populate_results( fn populate_results(
db: &Rc<Database>, db: &Rc<Database>,
query: &str, query: &str,
@@ -254,6 +261,14 @@ fn populate_results(
let toast_ref = toast_overlay.clone(); let toast_ref = toast_overlay.clone();
let db_ref = db.clone(); let db_ref = db.clone();
// Get installed app names for matching
let installed_names: std::collections::HashSet<String> = db
.get_all_appimages()
.unwrap_or_default()
.iter()
.filter_map(|r| r.app_name.as_ref().map(|n| n.to_lowercase()))
.collect();
for app in &results { for app in &results {
let row = adw::ActionRow::builder() let row = adw::ActionRow::builder()
.title(&app.name) .title(&app.name)
@@ -280,6 +295,16 @@ fn populate_results(
} }
} }
// Show "Installed" badge if already installed, otherwise show Install button
let is_installed = installed_names.contains(&app.name.to_lowercase());
if is_installed {
let installed_badge = widgets::status_badge(&i18n("Installed"), "success");
installed_badge.set_valign(gtk::Align::Center);
row.add_suffix(&installed_badge);
list_box.append(&row);
continue;
}
// Install button // Install button
let install_btn = gtk::Button::builder() let install_btn = gtk::Button::builder()
.label(&i18n("Install")) .label(&i18n("Install"))