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"));
// 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);
if app_count > 0 {
stack.set_visible_child_name("results");
if let Some(src) = enabled_sources.first() {
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));
}
update_catalog_subtitle(&title, app_count);
} else {
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(
&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");
populate_categories(&db_c, &cat_box_c, &active_cat_c, &results_c, &search_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_header_btn);
// Auto-refresh on first visit when catalog is empty
if app_count == 0 {
refresh_btn.emit_clicked();
}
adw::NavigationPage::builder()
.title(&i18n("Catalog"))
.tag("catalog")
@@ -228,6 +223,18 @@ pub fn build_catalog_page(db: &Rc<Database>) -> adw::NavigationPage {
.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(
db: &Rc<Database>,
query: &str,
@@ -254,6 +261,14 @@ fn populate_results(
let toast_ref = toast_overlay.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 {
let row = adw::ActionRow::builder()
.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
let install_btn = gtk::Button::builder()
.label(&i18n("Install"))