Add similar app recommendations from shared categories
This commit is contained in:
@@ -1869,6 +1869,51 @@ impl Database {
|
||||
)
|
||||
}
|
||||
|
||||
// --- Similar apps ---
|
||||
|
||||
/// Find AppImages from the user's library that share categories with the given app.
|
||||
pub fn find_similar_apps(
|
||||
&self,
|
||||
categories: &str,
|
||||
exclude_id: i64,
|
||||
limit: i32,
|
||||
) -> SqlResult<Vec<(i64, String, Option<String>)>> {
|
||||
// Split categories and match any overlap
|
||||
let cats: Vec<&str> = categories.split(';').filter(|s| !s.is_empty()).collect();
|
||||
if cats.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// Build LIKE conditions for each category
|
||||
let conditions: Vec<String> = cats.iter()
|
||||
.map(|c| format!("categories LIKE '%{}%'", c.replace('\'', "''")))
|
||||
.collect();
|
||||
let where_clause = conditions.join(" OR ");
|
||||
|
||||
let sql = format!(
|
||||
"SELECT id, COALESCE(app_name, filename) AS name, icon_path
|
||||
FROM appimages
|
||||
WHERE id != ?1 AND ({})
|
||||
LIMIT ?2",
|
||||
where_clause
|
||||
);
|
||||
|
||||
let mut stmt = self.conn.prepare(&sql)?;
|
||||
let rows = stmt.query_map(params![exclude_id, limit], |row| {
|
||||
Ok((
|
||||
row.get::<_, i64>(0)?,
|
||||
row.get::<_, String>(1)?,
|
||||
row.get::<_, Option<String>>(2)?,
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for row in rows {
|
||||
results.push(row?);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
// --- System modification tracking ---
|
||||
|
||||
pub fn register_modification(
|
||||
|
||||
@@ -868,6 +868,38 @@ fn build_overview_tab(record: &AppImageRecord, db: &Rc<Database>) -> gtk::Box {
|
||||
}
|
||||
inner.append(&info_group);
|
||||
|
||||
// "You might also like" - similar apps from the user's library
|
||||
if let Some(ref cats) = record.categories {
|
||||
if let Ok(similar) = db.find_similar_apps(cats, record.id, 4) {
|
||||
if !similar.is_empty() {
|
||||
let similar_group = adw::PreferencesGroup::builder()
|
||||
.title("You might also like")
|
||||
.build();
|
||||
|
||||
for (id, name, icon_path) in &similar {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(name.as_str())
|
||||
.activatable(true)
|
||||
.build();
|
||||
|
||||
let icon = widgets::app_icon(
|
||||
icon_path.as_deref(),
|
||||
name,
|
||||
32,
|
||||
);
|
||||
row.add_prefix(&icon);
|
||||
|
||||
// Store the record ID in the widget name for navigation
|
||||
row.set_widget_name(&format!("similar-{}", id));
|
||||
|
||||
similar_group.add(&row);
|
||||
}
|
||||
|
||||
inner.append(&similar_group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clamp.set_child(Some(&inner));
|
||||
tab.append(&clamp);
|
||||
tab
|
||||
|
||||
Reference in New Issue
Block a user