diff --git a/src/routes/ui.rs b/src/routes/ui.rs deleted file mode 100644 index 6599aba..0000000 --- a/src/routes/ui.rs +++ /dev/null @@ -1,571 +0,0 @@ -use based::request::{RequestContext, StringResponse}; -use based::ui::primitives::flex::Strategy; -use based::ui::primitives::space::SpaceBetweenWidget; -use based::ui::primitives::text::{Code, TextWidget}; -use based::ui::{UIWidget, prelude::*}; -use maud::{PreEscaped, Render, html}; -use pacco::pkg::package::PackageMetaInfo; -use rocket::{State, get}; - -use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name}; - -use crate::config::Config; - -use super::render; - -// TODO : API - -#[get("//?")] -pub async fn pkg_ui( - repo: &str, - pkg_name: &str, - ctx: RequestContext, - ver: Option<&str>, -) -> Option { - let repo = Repository::new(repo).unwrap(); - let mut pkg = repo.get_pkg_by_name(pkg_name)?; - - if let Some(ver) = ver { - let (version, rel) = Package::version(ver); - pkg = pkg.get_version(&version); - pkg.rel = rel; - } - - let dl_count = pkg.download_amount().await; - - let versions = pkg.versions(); - let arch = pkg.arch(); - let install_script = pkg.install_script(); - let systemd_units = pkg.systemd_units(); - let pacman_hooks = pkg.pacman_hooks(); - let binaries = pkg.binaries(); - let mut pkginfo = pkg.pkginfo(); - - let content = Div().vanish() - .push( - Flex( - // Package Name - Div().vanish() - .push( - Link(&format!("/{}", repo.name), - Text(&repo.name).bold().color(&Gray::_100)._3xl() - ).use_htmx() - ) - .push(Padding(Text("/").bold().color(&Gray::_400)).all(ScreenValue::_2)) - .push( - Padding(Text(&pkg.name)._3xl().bold().color(&Gray::_100)).right(ScreenValue::_2) - ) - .push_if(pkg.is_signed(), || { - Flex( - Padding( - Div().vanish() - .push(Span("✓")._2xl().bold().color(&Slate::_300)) - .push(Span("Signed").sm().medium().color(&Slate::_300)) - // TODO : Add more info: Who signed? + Public Key recv - ).right(ScreenValue::_4) - ).items_center().gap(ScreenValue::_2) - }) - .push_for_each(&arch, |arch: &Architecture| { - Rounded( - Padding( - Background(Gray::_700, - Span(&arch.to_string()).medium().sm()) - ).x(ScreenValue::_3).y(ScreenValue::_1) - ).size(Size::Full) - }) - .push(Margin( - Flex( - Padding( - Hover(Background(Gray::_300, Nothing())).on(Background(Gray::_200, - Rounded(Shadow::medium( - Link(&format!("/pkg/{}/{}/{}", pkg.repo, pkg.arch.to_string(), pkg.file_name()), - Paragraph( - html! { - svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" { - path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m7-7l-7 7-7-7" {}; - }; - p class="ml-2 text-black" { "Download" }; - } - ).black().xs()))).size(Size::Large) - )) - ).x(ScreenValue::_2).y(ScreenValue::_2) - ).items_center() - ).left(ScreenValue::_4) - ) - ).wrap(Wrap::Wrap).gap(ScreenValue::_2).full_center().group() - ) - .push( - Padding( - Flex( - Div().vanish().push( - Context( - FlexGrow(Strategy::Grow, - Animated(Background(Gray::_800, Shadow::large(Rounded(Margin(Padding(SpaceBetween( - Div() - .push( - Context(Text("Info").xl().semibold().color(&Gray::_300).underlined()) - ) - .push_some( - take_out(&mut pkginfo, |x| { x.0 == "pkgdesc" }).1, - |x: (String, String)| build_info(x.0, x.1) - ) - .push_some( - take_out(&mut pkginfo, |x| { x.0 == "packager" }).1, - |x: (String, String)| build_info(x.0, x.1) - ) - .push_some( - take_out(&mut pkginfo, |x| { x.0 == "url" }).1, - |x: (String, String)| build_info(x.0, x.1) - ) - .push_some( - take_out(&mut pkginfo, |x| { x.0 == "size" }).1, - |x: (String, String)| build_info(x.0, x.1) - ) - .push( - build_info("download_amount".to_string(), dl_count.to_string()) - ) - .push_for_each(&pkginfo, |(key, val)| build_info(key.clone(), val.clone())) - ).y(ScreenValue::_2)).all(ScreenValue::_4)).all(ScreenValue::_2)).size(Size::Large))))) - )).push( - SpaceBetween( - Padding( - Background(Gray::_800, - Shadow::large( - Rounded( - Margin( - Screen::medium(FlexGrow(Strategy::NoGrow, Nothing())).on( - FlexGrow(Strategy::Grow, - Div().vanish() - .push( - Text("Versions").xl().semibold().color(&Gray::_300) - ) - .push( - SpaceBetween( - Div().vanish() - .push_for_each(&versions, |version: &String| { - Animated(Link(&format!("/{}/{}?ver={version}", repo.name, &pkg.name), - if pkg.version.as_ref() - .map(|x| *x == Package::version(&version).0).unwrap_or_default() - { Text(&version).color(&Blue::_500).render() } else { - Hover(Text("").color(&Blue::_400)).on( - Text(&version).color(&Gray::_100)).render() - } - ).use_htmx()) - }) - ).y(ScreenValue::_1) - ) - )) - ).all(ScreenValue::_2) - ).size(Size::Large) - ) - ) - ).all(ScreenValue::_4) - ).y(ScreenValue::_2) - ) - ).wrap(Wrap::Wrap).group() - ).top(ScreenValue::_6) - ).push( - Padding(Flex( - SpaceBetween( - Div().vanish() - .push( - Text("Content").xl().bold().color(&Gray::_300) - ) - .push( - Flex( - Div().vanish() - .push_if(!systemd_units.is_empty(), || { - InfoCard( - Div().vanish() - .push( - CardTitle("Systemd Units") - ) - .push( - ListElements(&systemd_units) - ) - ) - }) - .push_if(!pacman_hooks.is_empty(), || { - InfoCard( - Div().vanish() - .push(CardTitle("Pacman Hooks")) - .push( - Paragraph( - SpaceBetween( - Div().vanish() - .push_for_each(&pacman_hooks, |hook: &(String, String)| { - Div().vanish() - .push( - Text(&hook.0).xl().semibold().color(&Gray::_300) - ) - .push( - Padding(Rounded(Background(Gray::_700, Code(&hook.1).sm().color(&Gray::_100))).size(Size::Large)).all(ScreenValue::_4) - ) - }) - .push( - html! { - @for unit in &systemd_units { - li { (unit) } - } - }) - ).y(ScreenValue::_1) - ).list_style(ListStyle::Disc).color(&Gray::_300) - ) - ) - }) - .push_if(!binaries.is_empty(), - || InfoCard( - Div().vanish() - .push( - CardTitle("Binaries") - ).push( - ListElements(&binaries) - ) - ) - ) - .push( - InfoCard( - Div().vanish().push( - CardTitle("Package Files") - ) - .push( - ListElements(&pkg.file_list()) - ) - ) - ) - ).group().wrap(Wrap::Wrap) - ) - ).y(ScreenValue::_2) - ).wrap(Wrap::Wrap).group()).top(ScreenValue::_6) - ).push_some(install_script.as_ref(), |install_script: &String| { - SpaceBetween( - Margin( - Div() - .push(Text("Install Script")._3xl().semibold().color(&Gray::_300).indentation(ScreenValue::_2)) - .push(Rounded(Padding(Background(Gray::_700, - Code(install_script.trim()) - .color(&Gray::_100).sm() - .indentation(ScreenValue::_0) - )).all(ScreenValue::_2)).size(Size::Large)) - ).top(ScreenValue::_6) - ).y(ScreenValue::_4) - }).render(); - - Some(render(content, pkg_name, ctx).await) -} - -pub fn arch_card(a: &str, repo_name: &str, current: bool) -> PreEscaped { - let url = if current { - &format!("/{repo_name}") - } else { - &format!("/{repo_name}?arch={a}") - }; - - let link = Link(&url, Text(&a).sm().medium().color(&Gray::_300)).use_htmx(); - - Rounded( - Padding(if current { - Background(Blue::_500, link) - } else { - Background(Gray::_700, link) - }) - .x(ScreenValue::_3) - .y(ScreenValue::_1), - ) - .size(Size::Full) - .render() -} - -#[get("/?")] -pub async fn repo_ui( - repo: &str, - ctx: RequestContext, - arch: Option<&str>, - config: &State, -) -> StringResponse { - let arch = arch.map(|x| Architecture::parse(x).unwrap_or(Architecture::any)); - - let repo = Repository::new(repo).unwrap(); - let architectures: Vec<_> = repo.arch().into_iter().map(|x| x.to_string()).collect(); - let packages = if let Some(arch) = arch.clone() { - repo.list_pkg_arch(arch) - } else { - repo.list_pkg() - }; - - let repo_info = Margin( - // Repository name and architectures - Flex( - Div() - .vanish() - .push(Text(&repo.name)._3xl().bold().color(&Gray::_100)) - .push_if(config.is_mirrored_repo(&repo.name), || { - Background( - Blue::_500, - Rounded( - Margin( - Padding(Text("Mirrored").sm().medium().white()) - .x(ScreenValue::_3) - .y(ScreenValue::_1), - ) - .left(ScreenValue::_4), - ) - .size(Size::Full), - ) - }) - .push( - Screen::medium(Margin(Nothing()).top(ScreenValue::_0)).on(Margin( - Flex(Div().vanish().push_for_each(&architectures, |a: &String| { - html! { - @if let Some(arch) = arch.as_ref() { - @if arch.to_string() == *a { - (arch_card(&a, &repo.name, true)) - } @else { - (arch_card(&a, &repo.name, false)) - } - } @else { - (arch_card(&a, &repo.name, false)) - } - } - })) - .group() - .gap(ScreenValue::_2), - ) - .left(ScreenValue::_4) - .top(ScreenValue::_2)), - ), - ) - .wrap(Wrap::Wrap) - .full_center() - .group(), - ) - .bottom(ScreenValue::_6); - - let package_list = SpaceBetween(Div().vanish().push_for_each(&packages, |pkg| { - Animated( - Flex( - Padding( - Hover(Background(Gray::_600, Nothing())).on(Background( - Gray::_700, - Rounded(Shadow::medium( - Link( - &format!("/{}/{pkg}", repo.name), - Div() - .vanish() - .push(Context(Sized( - ScreenValue::_10, - ScreenValue::_10, - Rounded(Background( - Blue::_500, - Flex( - Text( - &pkg.chars() - .next() - .unwrap_or_default() - .to_uppercase() - .to_string(), - ) - .white() - .semibold(), - ) - .full_center() - .group(), - )) - .size(Size::Full), - ))) - .push(Context(Width( - ScreenValue::fit, - Text(&pkg).medium().color(&Gray::_100), - ))) - .push(Context( - Padding( - Flex( - Div() - .vanish() - .push( - Span("✓")._2xl().bold().color(&Slate::_300), - ) - .push( - Span("Signed") - .sm() - .medium() - .color(&Slate::_300), - ), - ) - .items_center() - .gap(ScreenValue::_2) - .group(), - ) - .right(ScreenValue::_4), - )), - ) - .use_htmx(), - )) - .size(Size::Large), - )), - ) - .all(ScreenValue::_4), - ) - .items_center() - .gap(ScreenValue::_4), - ) - })) - .y(ScreenValue::_4); - - let content = Div().vanish().push(repo_info).push(package_list).render(); - - render(content, &repo.name, ctx).await -} - -pub fn build_info(key: String, value: String) -> PreEscaped { - match key.as_str() { - "download_amount" => { - return key_value("Downloads".to_string(), value); - } - "pkgname" => {} - "xdata" => {} - "arch" => {} - "pkbase" => {} - "pkgver" => {} - "pkgdesc" => { - return key_value("Description".to_string(), value); - } - "packager" => { - return key_value("Packager".to_string(), value); - } - "url" => { - return Flex( - Div() - .vanish() - .push(Width(ScreenValue::_32, Span("Website: ").bold())) - .push( - Margin(Link(&value, Span(&value).color(&Blue::_400))).left(ScreenValue::_6), - ), - ) - .group() - .render(); - } - "builddate" => { - let date = chrono::DateTime::from_timestamp(value.parse().unwrap(), 0).unwrap(); - return key_value( - "Build at".to_string(), - date.format("%d.%m.%Y %H:%M").to_string(), - ); - } - "depend" => { - return pkg_list_info("Depends on", &value); - } - "makedepend" => { - return pkg_list_info("Build Dependencies", &value); - } - "license" => { - return key_value("License".to_string(), value); - } - "size" => { - return key_value( - "Size".to_string(), - bytesize::to_string(value.parse().unwrap(), false), - ); - } - _ => { - log::warn!("Unhandled PKGINFO {key} = {value}"); - } - } - - html! {} -} - -pub fn key_value(key: String, value: String) -> PreEscaped { - Flex( - Div() - .vanish() - .push(Width(ScreenValue::_32, Span(&format!("{key}: ")).bold())) - .push(Margin(Span(&value)).left(ScreenValue::_6)), - ) - .items_center() - .group() - .render() -} - -pub fn take_out(v: &mut Vec, f: impl Fn(&T) -> bool) -> (&mut Vec, Option) { - let mut index = -1; - - for (i, e) in v.iter().enumerate() { - if f(e) { - index = i as i64; - } - } - - if index != -1 { - let e = v.remove(index as usize); - return (v, Some(e)); - } - - (v, None) -} - -pub fn find_pkg_url(pkg: &str) -> Option { - if let Some(pkg) = find_package_by_name(pkg) { - return Some(format!("/{}/{}", pkg.repo, pkg.name)); - } - - None -} - -pub fn pkg_list_info(key: &str, value: &str) -> PreEscaped { - let pkgs = value.split_whitespace().map(|pkg| { - if let Some(pkg_url) = find_pkg_url(pkg) { - Margin(Link(&pkg_url, Text(&pkg).color(&Blue::_400)).use_htmx()).left(ScreenValue::_6) - } else { - Margin(Span(&pkg)).left(ScreenValue::_6) - } - }); - - html! { - (Flex( - Div().vanish() - .push(Width(ScreenValue::_32, Span(&format!("{key}: ")).bold())) - .push(Flex( - html! { - @for pkg in pkgs { - (pkg) - } - } - ).group().wrap(Wrap::Wrap)) - ).items_center().group()) - } -} - -#[allow(non_snake_case)] -pub fn ListElements(el: &[String]) -> TextWidget { - Paragraph( - SpaceBetween(html! { - @for unit in el { - li { (unit) } - } - }) - .y(ScreenValue::_1), - ) - .list_style(ListStyle::Disc) - .color(&Gray::_300) -} - -#[allow(non_snake_case)] -pub fn CardTitle(title: &str) -> PreEscaped { - Context(Text(title).large().medium().color(&Gray::_100).underlined()) -} - -#[allow(non_snake_case)] -pub fn InfoCard(inner: T) -> SpaceBetweenWidget { - SpaceBetween( - Padding(Background( - Gray::_800, - Shadow::large( - Rounded(Margin(FlexGrow(Strategy::Grow, inner)).all(ScreenValue::_2)) - .size(Size::Large), - ), - )) - .all(ScreenValue::_4), - ) - .y(ScreenValue::_2) -} diff --git a/src/routes/ui/pkg.rs b/src/routes/ui/pkg.rs index 749f981..160bb42 100644 --- a/src/routes/ui/pkg.rs +++ b/src/routes/ui/pkg.rs @@ -135,9 +135,10 @@ pub async fn pkg_ui( take_out(&mut pkginfo, |x| x.0 == "size").1, |x: (String, String)| build_info(x.0, x.1), ) - .push( - build_info("download_amount".to_string(), dl_count.to_string()) - ) + .push(build_info( + "download_amount".to_string(), + dl_count.to_string(), + )) .push_for_each(&pkginfo, |(key, val)| { build_info(key.clone(), val.clone()) }),