use based::request::{RequestContext, StringResponse}; use based::ui::prelude::*; use maud::{PreEscaped, Render, html}; use rocket::{State, get}; use pacco::pkg::{Repository, arch::Architecture}; use crate::config::Config; use crate::routes::ui::arch_card; use super::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() }; // Repository name and architectures let repo_info = Margin( 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), )) .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), ) .top(ScreenValue::_2)), ) .push( Text(&format!("{} packages", packages.len())) .sm() .color(&Gray::_100), ), ) .gap(ScreenValue::_3) .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(FirstLetterCircle(pkg)) .push(Context(Width( ScreenValue::fit, Text(&pkg).medium().color(&Gray::_100), ))) .push_if( repo.get_pkg_by_name(&pkg) .map(|x| x.sig_content()) .is_some(), || SignedInfo(), ), ) .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 } #[allow(non_snake_case)] pub fn FirstLetterCircle(name: &str) -> PreEscaped { Sized( ScreenValue::_10, ScreenValue::_10, Rounded(Background( Blue::_500, Flex( Text( &name .chars() .next() .unwrap_or_default() .to_uppercase() .to_string(), ) .white() .semibold(), ) .full_center() .group(), )) .size(Size::Full), ) .render() } #[allow(non_snake_case)] pub fn SignedInfo() -> PreEscaped { 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) .render() }