This commit is contained in:
parent
7aed380e21
commit
77f1c27981
4 changed files with 125 additions and 80 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -152,7 +152,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "based"
|
name = "based"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.hydrar.de/jmarya/based?branch=ui#1e0ab7e0dcd42c304146376176d15ff34784bb34"
|
source = "git+https://git.hydrar.de/jmarya/based?branch=ui#cdaabccd9eb701c5f0050def9706fc167137a481"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -257,9 +257,9 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.12"
|
version = "1.2.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2"
|
checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
|
@ -27,3 +27,7 @@ contact.email = "mail@example.com"
|
||||||
[project.sub.name]
|
[project.sub.name]
|
||||||
name = "Sub Project"
|
name = "Sub Project"
|
||||||
description = "Sub Project"
|
description = "Sub Project"
|
||||||
|
|
||||||
|
[project.sub.name.sub.subname]
|
||||||
|
name = "Sub Sub Project"
|
||||||
|
description = "Sub Sub Project"
|
||||||
|
|
|
@ -29,3 +29,15 @@ impl Config {
|
||||||
toml::from_str(&content).unwrap()
|
toml::from_str(&content).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_prj(c: &ProjectConfig, path: &str) -> Option<Project> {
|
||||||
|
let paths: Vec<_> = path.split("/").filter(|x| !x.is_empty()).collect();
|
||||||
|
let name = paths.first()?.to_string();
|
||||||
|
|
||||||
|
if paths.len() == 1 {
|
||||||
|
return c.get(&name).cloned();
|
||||||
|
} else {
|
||||||
|
let sub_prj = c.get(&name)?.sub.as_ref()?;
|
||||||
|
get_prj(sub_prj, &paths[1..].join("/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
157
src/main.rs
157
src/main.rs
|
@ -3,12 +3,12 @@ use std::path::Path;
|
||||||
use based::asset::AssetRoutes;
|
use based::asset::AssetRoutes;
|
||||||
use based::page;
|
use based::page;
|
||||||
use based::request::{RequestContext, StringResponse};
|
use based::request::{RequestContext, StringResponse};
|
||||||
use based::ui::components::{Shell, Tabs};
|
use based::ui::components::{Breadcrumb, Shell, Tabs};
|
||||||
use based::ui::prelude::*;
|
use based::ui::prelude::*;
|
||||||
use based::ui::primitives::flex::Row;
|
use based::ui::primitives::flex::Row;
|
||||||
use based::ui::primitives::Optional;
|
use based::ui::primitives::Optional;
|
||||||
use config::{Project, ProjectConfig};
|
use config::{get_prj, Project, ProjectConfig};
|
||||||
use maud::Render;
|
use maud::{PreEscaped, Render};
|
||||||
use rocket::fs::NamedFile;
|
use rocket::fs::NamedFile;
|
||||||
use rocket::request::FromSegments;
|
use rocket::request::FromSegments;
|
||||||
use rocket::{get, launch, routes, State};
|
use rocket::{get, launch, routes, State};
|
||||||
|
@ -61,11 +61,10 @@ pub async fn project_page(
|
||||||
) -> Option<StringResponse> {
|
) -> Option<StringResponse> {
|
||||||
let mut prj = c.get(path.segments.first()?);
|
let mut prj = c.get(path.segments.first()?);
|
||||||
for p in &path.segments[1..] {
|
for p in &path.segments[1..] {
|
||||||
println!(" --> {p}");
|
|
||||||
prj = prj.as_ref()?.sub.as_ref()?.get(p);
|
prj = prj.as_ref()?.sub.as_ref()?.get(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(render_project(shell, ctx, prj?, path.to_str()).await)
|
Some(render_project(shell, ctx, prj?, c, path.to_str()).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/static/<file>")]
|
#[get("/static/<file>")]
|
||||||
|
@ -85,71 +84,41 @@ pub async fn main_page(
|
||||||
let first = keys.first().unwrap();
|
let first = keys.first().unwrap();
|
||||||
let prj = c.get(*first).unwrap();
|
let prj = c.get(*first).unwrap();
|
||||||
|
|
||||||
render_project(shell, ctx, prj, format!("{first}/")).await
|
render_project(shell, ctx, prj, c, format!("{first}/")).await
|
||||||
} else {
|
} else {
|
||||||
// TODO : root project overview
|
// TODO : root project overview
|
||||||
site::gen_site(c, ctx).await
|
site::gen_site(c, ctx).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_project(
|
pub fn info_tab(prj: &Project, root: &str) -> PreEscaped<String> {
|
||||||
shell: &State<Shell>,
|
|
||||||
ctx: RequestContext,
|
|
||||||
prj: &Project,
|
|
||||||
root: String,
|
|
||||||
) -> StringResponse {
|
|
||||||
let title = format!("{} - Umbrella ☂️", prj.name);
|
|
||||||
|
|
||||||
page!(
|
|
||||||
shell,
|
|
||||||
ctx,
|
|
||||||
title,
|
|
||||||
Div()
|
|
||||||
.vanish()
|
|
||||||
.push(
|
|
||||||
Margin(
|
|
||||||
Row(vec![
|
|
||||||
Optional(prj.icon.as_ref(), |icon| Image(icon).alt("Project Icon")),
|
|
||||||
Text(&prj.name)._2xl().render()
|
|
||||||
])
|
|
||||||
.gap(ScreenValue::_4)
|
|
||||||
.full_center()
|
|
||||||
)
|
|
||||||
.top(ScreenValue::_6)
|
|
||||||
.bottom(ScreenValue::_2)
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
Padding(
|
|
||||||
Tabs()
|
|
||||||
.add_tab(
|
|
||||||
"info",
|
|
||||||
Text("📜 Info").medium(),
|
|
||||||
Flex(
|
Flex(
|
||||||
Div()
|
Div()
|
||||||
.vanish()
|
.vanish()
|
||||||
.push(Text(&prj.description))
|
.push(Rounded(
|
||||||
.push(Optional(prj.website.as_deref(), |website| Link(
|
Background(Padding(Code(&prj.description)).all(ScreenValue::_4)).color(Gray::_900),
|
||||||
website,
|
))
|
||||||
Text(&format!("Website: {website}"))
|
.push(Optional(prj.website.as_deref(), |website| {
|
||||||
)))
|
Link(website, Text(&format!("Website: {website}")))
|
||||||
.push(Optional(prj.documentation.as_deref(), |docs| Link(
|
}))
|
||||||
docs,
|
.push(Optional(prj.documentation.as_deref(), |docs| {
|
||||||
Text(&format!("Documentation: {docs}"))
|
Link(docs, Text(&format!("Documentation: {docs}")))
|
||||||
)))
|
}))
|
||||||
.push(Optional(prj.since.as_deref(), |since| Text(&format!(
|
.push(Optional(prj.since.as_deref(), |since| {
|
||||||
"Since: {since}"
|
Text(&format!("Since: {since}"))
|
||||||
))))
|
}))
|
||||||
.push(Optional(prj.contact.as_ref(), |contact| {
|
.push(Optional(prj.contact.as_ref(), |contact| {
|
||||||
Div().vanish().push(Text("Contact").large()).push(Text(
|
Div()
|
||||||
&format!(
|
.vanish()
|
||||||
|
.push(Text("Contact").large())
|
||||||
|
.push(Text(&format!(
|
||||||
"eMail: {}",
|
"eMail: {}",
|
||||||
contact
|
contact
|
||||||
.email
|
.email
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|x| x.as_str())
|
.map(|x| x.as_str())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
),
|
)))
|
||||||
))
|
|
||||||
}))
|
}))
|
||||||
.push(Optional(prj.sub.as_ref(), |sub| {
|
.push(Optional(prj.sub.as_ref(), |sub| {
|
||||||
let mut prj_links = Vec::new();
|
let mut prj_links = Vec::new();
|
||||||
|
@ -162,23 +131,83 @@ pub async fn render_project(
|
||||||
.vanish()
|
.vanish()
|
||||||
.push(Text("Sub projects").large())
|
.push(Text("Sub projects").large())
|
||||||
.push_for_each(&prj_links, |link: &_| {
|
.push_for_each(&prj_links, |link: &_| {
|
||||||
Link(
|
Width(
|
||||||
|
ScreenValue::fit,
|
||||||
|
Button(Link(
|
||||||
&format!("/prj/{root}{link}"),
|
&format!("/prj/{root}{link}"),
|
||||||
Text(
|
Text(&prj.sub.as_ref().unwrap().get(*link).unwrap().name),
|
||||||
&prj.sub
|
)),
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.get(*link)
|
|
||||||
.unwrap()
|
|
||||||
.name,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}))
|
})),
|
||||||
)
|
)
|
||||||
.gap(ScreenValue::_2)
|
.gap(ScreenValue::_2)
|
||||||
.direction(Direction::Column)
|
.direction(Direction::Column)
|
||||||
|
.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up_to(keyword: &str, v: &[&str], separator: &str) -> String {
|
||||||
|
let index = v.iter().position(|s| *s == keyword).unwrap_or(v.len());
|
||||||
|
v[..index + 1].join(separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn ProjectIcon(prj: &Project) -> PreEscaped<String> {
|
||||||
|
Row(vec![
|
||||||
|
Optional(prj.icon.as_ref(), |icon| Image(icon).alt("Project Icon")),
|
||||||
|
Text(&prj.name)._2xl().render(),
|
||||||
|
])
|
||||||
|
.gap(ScreenValue::_4)
|
||||||
|
.full_center()
|
||||||
|
.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn render_project(
|
||||||
|
shell: &State<Shell>,
|
||||||
|
ctx: RequestContext,
|
||||||
|
prj: &Project,
|
||||||
|
c: &State<ProjectConfig>,
|
||||||
|
mut root: String,
|
||||||
|
) -> StringResponse {
|
||||||
|
let title = format!("{} - Umbrella ☂️", prj.name);
|
||||||
|
|
||||||
|
if !root.is_empty() && !root.ends_with('/') {
|
||||||
|
root.push_str("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
let root_paths: Vec<_> = root.split("/").filter(|x| !x.is_empty()).collect();
|
||||||
|
|
||||||
|
let bc: Vec<_> = root_paths
|
||||||
|
.iter()
|
||||||
|
.map(|x| {
|
||||||
|
let prj_path = up_to(x, &root_paths, "/");
|
||||||
|
|
||||||
|
(
|
||||||
|
ProjectIcon(&get_prj(c, &prj_path).unwrap()).0,
|
||||||
|
format!("/prj/{}", prj_path),
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("{bc:?}");
|
||||||
|
|
||||||
|
page!(
|
||||||
|
shell,
|
||||||
|
ctx,
|
||||||
|
title,
|
||||||
|
Div()
|
||||||
|
.vanish()
|
||||||
|
.push(Width(
|
||||||
|
ScreenValue::fit,
|
||||||
|
Margin(Breadcrumb(bc))
|
||||||
|
.top(ScreenValue::_6)
|
||||||
|
.bottom(ScreenValue::_2)
|
||||||
|
.x(ScreenValue::auto)
|
||||||
|
))
|
||||||
|
.push(
|
||||||
|
Padding(
|
||||||
|
Tabs()
|
||||||
|
.add_tab("info", Text("📜 Info").medium(), info_tab(prj, &root))
|
||||||
.add_tab(
|
.add_tab(
|
||||||
"invest",
|
"invest",
|
||||||
Text("💵 Invest").medium(),
|
Text("💵 Invest").medium(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue