diff --git a/i18n/en/cosmic_store.ftl b/i18n/en/cosmic_store.ftl index e5bcb20..cc00eb1 100644 --- a/i18n/en/cosmic_store.ftl +++ b/i18n/en/cosmic_store.ftl @@ -52,6 +52,15 @@ app-developers = {$app} Developers monthly-downloads = Flathub Monthly Downloads licenses = Licenses +## App URLs +bug-tracker = Bug tracker +contact = Contact +donation = Donation +faq = FAQ +help = Help +homepage = Homepage +translate = Translate + # Context Pages ## Settings diff --git a/src/app_info.rs b/src/app_info.rs index 60a657f..3ce5876 100644 --- a/src/app_info.rs +++ b/src/app_info.rs @@ -1,5 +1,5 @@ use appstream::{ - enums::{Bundle, Icon, ImageKind, Launchable}, + enums::{Bundle, Icon, ImageKind, Launchable, ProjectUrl}, xmltree, Component, }; use std::{error::Error, fmt::Write}; @@ -120,6 +120,17 @@ pub struct AppScreenshot { } #[derive(Clone, Debug, Hash, Eq, PartialEq, bitcode::Decode, bitcode::Encode)] +pub enum AppUrl { + BugTracker(String), + Contact(String), + Donation(String), + Faq(String), + Help(String), + Homepage(String), + Translate(String), +} + +#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, bitcode::Decode, bitcode::Encode)] pub struct AppInfo { pub source_id: String, pub source_name: String, @@ -136,6 +147,7 @@ pub struct AppInfo { pub icons: Vec, pub releases: Vec, pub screenshots: Vec, + pub urls: Vec, pub monthly_downloads: u64, } @@ -274,6 +286,22 @@ impl AppInfo { } } } + let urls = component + .urls + .into_iter() + .filter_map(|project_url| { + Some(match project_url { + ProjectUrl::BugTracker(url) => AppUrl::BugTracker(url.into()), + ProjectUrl::Contact(url) => AppUrl::Contact(url.into()), + ProjectUrl::Donation(url) => AppUrl::Donation(url.into()), + ProjectUrl::Faq(url) => AppUrl::Faq(url.into()), + ProjectUrl::Help(url) => AppUrl::Help(url.into()), + ProjectUrl::Homepage(url) => AppUrl::Homepage(url.into()), + ProjectUrl::Translate(url) => AppUrl::Translate(url.into()), + _ => return None, + }) + }) + .collect(); Self { source_id: source_id.to_string(), @@ -291,6 +319,7 @@ impl AppInfo { icons, releases, screenshots, + urls, monthly_downloads, } } diff --git a/src/appstream_cache.rs b/src/appstream_cache.rs index bfacb91..c76b2c8 100644 --- a/src/appstream_cache.rs +++ b/src/appstream_cache.rs @@ -1,5 +1,5 @@ use appstream::{ - enums::{ComponentKind, Icon, ImageKind, Launchable, ReleaseKind, ReleaseUrgency}, + enums::{ComponentKind, Icon, ImageKind, Launchable, ProjectUrl, ReleaseKind, ReleaseUrgency}, url::Url, xmltree, Component, Image, MarkupTranslatableString, ParseError, Release, Screenshot, }; @@ -879,6 +879,56 @@ impl AppstreamCache { } } + if let Some(urls) = value["Url"].as_mapping() { + for (key, url_value) in urls.iter() { + let url = match url_value.as_str() { + Some(url_str) => match Url::parse(url_str) { + Ok(ok) => ok, + Err(err) => { + log::warn!( + "failed to parse url {:?} for {:?} in {:?}: {}", + url_str, + component.id, + path, + err + ); + continue; + } + }, + None => { + log::warn!( + "unsupported url kind {:?} for {:?} in {:?}", + url_value, + component.id, + path + ); + continue; + } + }; + let project_url = match key.as_str() { + Some("bugtracker") => ProjectUrl::BugTracker(url), + Some("contact") => ProjectUrl::Contact(url), + //TODO: add to appstream crate: Some("contribute") => ProjectUrl::Contribute(url), + Some("donation") => ProjectUrl::Donation(url), + Some("faq") => ProjectUrl::Faq(url), + Some("help") => ProjectUrl::Help(url), + Some("homepage") => ProjectUrl::Homepage(url), + Some("translate") => ProjectUrl::Translate(url), + //TODO: add to appstream crate: Some("vcs-browser") => ProjectUrl::VcsBrowser(url), + _ => { + log::warn!( + "unsupported url kind {:?} for {:?} in {:?}", + key, + component.id, + path + ); + continue; + } + }; + component.urls.push(project_url); + } + } + let id = AppId::new(&component.id.0); let monthly_downloads = stats::monthly_downloads(&id).unwrap_or(0); infos.push(( diff --git a/src/backend/flatpak.rs b/src/backend/flatpak.rs index 49c2055..e2713f7 100644 --- a/src/backend/flatpak.rs +++ b/src/backend/flatpak.rs @@ -175,20 +175,11 @@ impl Flatpak { info: Arc::new(AppInfo { source_id: appstream_cache.source_id.clone(), source_name: appstream_cache.source_name.clone(), - origin_opt: None, name, summary, - developer_name: String::new(), description, - license_opt: None, - pkgnames: Vec::new(), - categories: Vec::new(), - desktop_ids: Vec::new(), flatpak_refs, - icons: Vec::new(), - releases: Vec::new(), - screenshots: Vec::new(), - monthly_downloads: 0, + ..Default::default() }), version: String::new(), extra: HashMap::new(), diff --git a/src/backend/packagekit.rs b/src/backend/packagekit.rs index d0815c8..2c6617f 100644 --- a/src/backend/packagekit.rs +++ b/src/backend/packagekit.rs @@ -195,20 +195,11 @@ impl Packagekit { info: Arc::new(AppInfo { source_id: appstream_cache.source_id.clone(), source_name: appstream_cache.source_name.clone(), - origin_opt: None, name: package_name.to_string(), summary: tx_detail.summary.clone(), - developer_name: String::new(), description: tx_detail.description.clone(), - license_opt: None, pkgnames: vec![package_name.to_string()], - categories: Vec::new(), - desktop_ids: Vec::new(), - flatpak_refs: Vec::new(), - icons: Vec::new(), - releases: Vec::new(), - screenshots: Vec::new(), - monthly_downloads: 0, + ..Default::default() }), version: version_opt.unwrap_or("").to_string(), extra: HashMap::new(), @@ -280,20 +271,11 @@ impl Packagekit { info: Arc::new(AppInfo { source_id: appstream_cache.source_id.clone(), source_name: appstream_cache.source_name.clone(), - origin_opt: None, name, summary, - developer_name: String::new(), description, - license_opt: None, pkgnames, - categories: Vec::new(), - desktop_ids: Vec::new(), - flatpak_refs: Vec::new(), - icons: Vec::new(), - releases: Vec::new(), - screenshots: Vec::new(), - monthly_downloads: 0, + ..Default::default() }), version: String::new(), extra: HashMap::new(), diff --git a/src/main.rs b/src/main.rs index 855b451..bb274ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ use std::{ use app_id::AppId; mod app_id; -use app_info::{AppIcon, AppInfo}; +use app_info::{AppIcon, AppInfo, AppUrl}; mod app_info; use appstream_cache::AppstreamCache; @@ -1873,9 +1873,11 @@ impl App { .push(widget::text::body(format!("Custom: {id}"))); if let Some(url) = parts.next() { license_row = license_row.push( - widget::button::link(url.to_string()).on_press( - Message::LaunchUrl(url.to_string()), - ), + widget::button::link(url.to_string()) + .on_press(Message::LaunchUrl( + url.to_string(), + )) + .padding(0), ) } } else { @@ -1895,6 +1897,29 @@ impl App { column = column.push(license_col); } + if !selected.info.urls.is_empty() { + let mut url_row = widget::row::with_capacity(selected.info.urls.len()) + .spacing(space_s) + .align_items(Alignment::Center); + for app_url in &selected.info.urls { + let (name, url) = match app_url { + AppUrl::BugTracker(url) => (fl!("bug-tracker"), url), + AppUrl::Contact(url) => (fl!("contact"), url), + AppUrl::Donation(url) => (fl!("donation"), url), + AppUrl::Faq(url) => (fl!("faq"), url), + AppUrl::Help(url) => (fl!("help"), url), + AppUrl::Homepage(url) => (fl!("homepage"), url), + AppUrl::Translate(url) => (fl!("translate"), url), + }; + url_row = url_row.push( + widget::button::link(name) + .on_press(Message::LaunchUrl(url.to_string())) + .padding(0), + ); + } + column = column.push(url_row); + } + column.into() } None => match &self.search_results {