parent
6ee1500795
commit
5ae9b80302
4 changed files with 780 additions and 644 deletions
644
src/routes/ui.rs
644
src/routes/ui.rs
|
@ -1,644 +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 rocket::{State, get};
|
|
||||||
|
|
||||||
use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name};
|
|
||||||
|
|
||||||
use crate::config::Config;
|
|
||||||
|
|
||||||
use super::render;
|
|
||||||
|
|
||||||
// TODO : API
|
|
||||||
|
|
||||||
#[get("/<repo>/<pkg_name>?<ver>")]
|
|
||||||
pub async fn pkg_ui(
|
|
||||||
repo: &str,
|
|
||||||
pkg_name: &str,
|
|
||||||
ctx: RequestContext,
|
|
||||||
ver: Option<&str>,
|
|
||||||
) -> Option<StringResponse> {
|
|
||||||
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 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 man_entries = pkg.man_entries();
|
|
||||||
let etc_entries = pkg.etc_entries();
|
|
||||||
let kernel_modules = pkg.kernel_modules();
|
|
||||||
let firmware = pkg.firmware();
|
|
||||||
let keyrings = pkg.keyrings();
|
|
||||||
let shared_objects = pkg.shared_objects();
|
|
||||||
let icons = pkg.icons();
|
|
||||||
let readmes = pkg.readmes();
|
|
||||||
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_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)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
).y(ScreenValue::_1)
|
|
||||||
).list_style(ListStyle::Disc).color(&Gray::_300)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.push_if(!readmes.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish().push(CardTitle("READMEs")).push(
|
|
||||||
Paragraph(
|
|
||||||
SpaceBetween(
|
|
||||||
Div().vanish()
|
|
||||||
.push_for_each(&readmes, |readme: &(String, String)| {
|
|
||||||
Div().vanish()
|
|
||||||
.push(
|
|
||||||
Text(&readme.0).xl().semibold().color(&Gray::_300)
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
Padding(Rounded(Background(Gray::_700, Code(&readme.1).sm().color(&Gray::_100))).size(Size::Large)).all(ScreenValue::_4)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
).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_if(!kernel_modules.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Kernel Modules"))
|
|
||||||
.push(
|
|
||||||
ListElements(&kernel_modules)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).push_if(!shared_objects.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Shared Objects (Libraries)"))
|
|
||||||
.push(
|
|
||||||
ListElements(&shared_objects)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).push_if(!firmware.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Firmware"))
|
|
||||||
.push(
|
|
||||||
ListElements(&firmware)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).push_if(!keyrings.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Pacman Keyrings"))
|
|
||||||
.push(
|
|
||||||
ListElements(&keyrings)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).push_if(!icons.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Icons"))
|
|
||||||
.push(
|
|
||||||
ListElements(&icons)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.push_if(!man_entries.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Man Entries"))
|
|
||||||
.push(
|
|
||||||
ListElements(&man_entries)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.push_if(!etc_entries.is_empty(),
|
|
||||||
|| InfoCard(
|
|
||||||
Div().vanish()
|
|
||||||
.push(CardTitle("Config Files"))
|
|
||||||
.push(
|
|
||||||
ListElements(&etc_entries)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.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<String> {
|
|
||||||
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("/<repo>?<arch>")]
|
|
||||||
pub async fn repo_ui(
|
|
||||||
repo: &str,
|
|
||||||
ctx: RequestContext,
|
|
||||||
arch: Option<&str>,
|
|
||||||
config: &State<Config>,
|
|
||||||
) -> 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),
|
|
||||||
))
|
|
||||||
.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(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<String> {
|
|
||||||
match key.as_str() {
|
|
||||||
"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<String> {
|
|
||||||
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<T>(v: &mut Vec<T>, f: impl Fn(&T) -> bool) -> (&mut Vec<T>, Option<T>) {
|
|
||||||
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<String> {
|
|
||||||
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<String> {
|
|
||||||
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<String> {
|
|
||||||
Context(Text(title).large().medium().color(&Gray::_100).underlined())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub fn InfoCard<T: UIWidget + 'static>(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)
|
|
||||||
}
|
|
50
src/routes/ui/mod.rs
Normal file
50
src/routes/ui/mod.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use based::ui::prelude::*;
|
||||||
|
use maud::{PreEscaped, Render};
|
||||||
|
|
||||||
|
use super::render;
|
||||||
|
|
||||||
|
// TODO : API
|
||||||
|
|
||||||
|
pub mod pkg;
|
||||||
|
pub use pkg::pkg_ui;
|
||||||
|
pub mod repo;
|
||||||
|
pub use repo::repo_ui;
|
||||||
|
|
||||||
|
pub fn arch_card(a: &str, repo_name: &str, current: bool) -> PreEscaped<String> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_out<T>(v: &mut Vec<T>, f: impl Fn(&T) -> bool) -> (&mut Vec<T>, Option<T>) {
|
||||||
|
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)
|
||||||
|
}
|
567
src/routes/ui/pkg.rs
Normal file
567
src/routes/ui/pkg.rs
Normal file
|
@ -0,0 +1,567 @@
|
||||||
|
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 rocket::get;
|
||||||
|
|
||||||
|
use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name};
|
||||||
|
|
||||||
|
use crate::routes::render;
|
||||||
|
|
||||||
|
use super::take_out;
|
||||||
|
|
||||||
|
#[get("/<repo>/<pkg_name>?<ver>")]
|
||||||
|
pub async fn pkg_ui(
|
||||||
|
repo: &str,
|
||||||
|
pkg_name: &str,
|
||||||
|
ctx: RequestContext,
|
||||||
|
ver: Option<&str>,
|
||||||
|
) -> Option<StringResponse> {
|
||||||
|
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 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 man_entries = pkg.man_entries();
|
||||||
|
let etc_entries = pkg.etc_entries();
|
||||||
|
let kernel_modules = pkg.kernel_modules();
|
||||||
|
let firmware = pkg.firmware();
|
||||||
|
let keyrings = pkg.keyrings();
|
||||||
|
let shared_objects = pkg.shared_objects();
|
||||||
|
let icons = pkg.icons();
|
||||||
|
let readmes = pkg.readmes();
|
||||||
|
let mut pkginfo = pkg.pkginfo();
|
||||||
|
|
||||||
|
let pkg_info_header = 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();
|
||||||
|
|
||||||
|
let pkg_info_table = 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_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);
|
||||||
|
|
||||||
|
let pkg_content = 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),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.y(ScreenValue::_1),
|
||||||
|
)
|
||||||
|
.list_style(ListStyle::Disc)
|
||||||
|
.color(&Gray::_300),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!readmes.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div().vanish().push(CardTitle("READMEs")).push(
|
||||||
|
Paragraph(
|
||||||
|
SpaceBetween(Div().vanish().push_for_each(
|
||||||
|
&readmes,
|
||||||
|
|readme: &(String, String)| {
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(
|
||||||
|
Text(&readme.0)
|
||||||
|
.xl()
|
||||||
|
.semibold()
|
||||||
|
.color(&Gray::_300),
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
Padding(
|
||||||
|
Rounded(Background(
|
||||||
|
Gray::_700,
|
||||||
|
Code(&readme.1)
|
||||||
|
.sm()
|
||||||
|
.color(&Gray::_100),
|
||||||
|
))
|
||||||
|
.size(Size::Large),
|
||||||
|
)
|
||||||
|
.all(ScreenValue::_4),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.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_if(!kernel_modules.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Kernel Modules"))
|
||||||
|
.push(ListElements(&kernel_modules)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!shared_objects.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Shared Objects (Libraries)"))
|
||||||
|
.push(ListElements(&shared_objects)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!firmware.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Firmware"))
|
||||||
|
.push(ListElements(&firmware)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!keyrings.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Pacman Keyrings"))
|
||||||
|
.push(ListElements(&keyrings)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!icons.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Icons"))
|
||||||
|
.push(ListElements(&icons)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!man_entries.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Man Entries"))
|
||||||
|
.push(ListElements(&man_entries)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.push_if(!etc_entries.is_empty(), || {
|
||||||
|
InfoCard(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(CardTitle("Config Files"))
|
||||||
|
.push(ListElements(&etc_entries)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.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);
|
||||||
|
|
||||||
|
let content = Div()
|
||||||
|
.vanish()
|
||||||
|
.push(pkg_info_header)
|
||||||
|
.push(pkg_info_table)
|
||||||
|
.push(pkg_content)
|
||||||
|
.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 build_info(key: String, value: String) -> PreEscaped<String> {
|
||||||
|
match key.as_str() {
|
||||||
|
"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<String> {
|
||||||
|
Flex(
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(Width(ScreenValue::_32, Span(&format!("{key}: ")).bold()))
|
||||||
|
.push(Margin(Span(&value)).left(ScreenValue::_6)),
|
||||||
|
)
|
||||||
|
.items_center()
|
||||||
|
.group()
|
||||||
|
.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<String> {
|
||||||
|
Context(Text(title).large().medium().color(&Gray::_100).underlined())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn InfoCard<T: UIWidget + 'static>(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pkg_list_info(key: &str, value: &str) -> PreEscaped<String> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_pkg_url(pkg: &str) -> Option<String> {
|
||||||
|
if let Some(pkg) = find_package_by_name(pkg) {
|
||||||
|
return Some(format!("/{}/{}", pkg.repo, pkg.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
163
src/routes/ui/repo.rs
Normal file
163
src/routes/ui/repo.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
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("/<repo>?<arch>")]
|
||||||
|
pub async fn repo_ui(
|
||||||
|
repo: &str,
|
||||||
|
ctx: RequestContext,
|
||||||
|
arch: Option<&str>,
|
||||||
|
config: &State<Config>,
|
||||||
|
) -> 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<String> {
|
||||||
|
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<String> {
|
||||||
|
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()
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue