update ui
Some checks failed
ci/woodpecker/push/build Pipeline failed

This commit is contained in:
JMARyA 2024-12-29 22:57:39 +01:00
parent 847008825f
commit 634706beec
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
3 changed files with 104 additions and 40 deletions

View file

@ -60,7 +60,7 @@ impl Domain {
Document::new(&self.name, path, self.dir.parent().unwrap().to_path_buf()) Document::new(&self.name, path, self.dir.parent().unwrap().to_path_buf())
} }
pub fn paths(&self, path: &str) -> Vec<PathEntry> { pub fn paths(&self, path: &str) -> (Vec<PathEntry>, bool) {
let mut base_path = self.dir.clone(); let mut base_path = self.dir.clone();
for p in path.split('/') { for p in path.split('/') {
@ -71,44 +71,31 @@ impl Domain {
let mut ret = Vec::new(); let mut ret = Vec::new();
let mut is_doc = false;
for entry in dir_content { for entry in dir_content {
let url_path = format!("{path}/{entry}"); let url_path = format!("{path}/{entry}");
let is_doc = read_dir(&base_path.join(entry)) if entry.starts_with("index_") && entry.ends_with(".html") {
.into_iter() is_doc = true;
.any(|x| x.starts_with("index_") && x.ends_with(".html")); continue;
if is_doc {
ret.push(PathEntry::Document(Document::new(
&self.name,
&url_path,
self.dir.parent().unwrap().to_path_buf(),
)));
} else {
ret.push(PathEntry::Path(self.name.clone(), url_path));
} }
ret.push(PathEntry(self.name.clone(), url_path));
} }
ret (ret, is_doc)
} }
} }
pub enum PathEntry { pub struct PathEntry(String, String);
Path(String, String),
Document(Document),
}
impl PathEntry { impl PathEntry {
pub fn url(&self) -> String { pub fn url(&self) -> String {
match self { format!("/d/{}/{}", self.0, self.1)
PathEntry::Path(domain, path) => format!("/d/{domain}/{path}"),
PathEntry::Document(document) => document.url(),
}
} }
pub fn path(&self) -> String { pub fn path(&self) -> &String {
match self { &self.1
PathEntry::Path(_, path) => path.to_string(),
PathEntry::Document(document) => document.path.clone(),
}
} }
} }
@ -170,7 +157,13 @@ impl Document {
} }
pub fn versions(&self) -> Vec<String> { pub fn versions(&self) -> Vec<String> {
read_dir(&self.doc_dir()) let mut res: Vec<String> = read_dir(&self.doc_dir())
.into_iter()
.filter(|x| x.starts_with("index_") && x.ends_with(".html"))
.collect();
res.sort();
res.reverse();
res
} }
} }

View file

@ -17,8 +17,11 @@ pub async fn download_favicon(domain: &str) -> Option<Vec<u8>> {
pub async fn download_fav_for(site: &str) { pub async fn download_fav_for(site: &str) {
if let Some(fav) = download_favicon(site).await { if let Some(fav) = download_favicon(site).await {
std::fs::write(std::path::Path::new("./favicon").join(site), fav).unwrap(); let fav_path = std::path::Path::new("./favicon").join(site);
log::info!("Writting favicon for {site}"); if !fav_path.exists() {
std::fs::write(fav_path, fav).unwrap();
log::info!("Writting favicon for {site}");
}
} }
} }

View file

@ -7,7 +7,7 @@ use based::{
use maud::{html, PreEscaped}; use maud::{html, PreEscaped};
use rocket::{get, State}; use rocket::{get, State};
use crate::archive::{PathEntry, WebsiteArchive}; use crate::archive::WebsiteArchive;
pub async fn render_page(content: PreEscaped<String>, ctx: RequestContext) -> StringResponse { pub async fn render_page(content: PreEscaped<String>, ctx: RequestContext) -> StringResponse {
based::page::render_page( based::page::render_page(
@ -17,6 +17,7 @@ pub async fn render_page(content: PreEscaped<String>, ctx: RequestContext) -> St
&Shell::new( &Shell::new(
html! { html! {
script src="https://cdn.tailwindcss.com" {}; script src="https://cdn.tailwindcss.com" {};
meta name="viewport" content="width=device-width, initial-scale=1.0" {};
}, },
html! {}, html! {},
Some("bg-zinc-950 text-white min-h-screen flex pt-8 justify-center".to_string()), Some("bg-zinc-950 text-white min-h-screen flex pt-8 justify-center".to_string()),
@ -72,6 +73,35 @@ pub fn arrow_icon(color: &str) -> PreEscaped<String> {
} }
} }
pub fn slash_seperator() -> PreEscaped<String> {
html! {
p class="font-bold p-2 text-gray-400" { " / " };
}
}
pub fn gen_path_link(
path: &str,
index: usize,
path_seperations: &[&str],
domain: &str,
) -> PreEscaped<String> {
let upto: Vec<&str> = path_seperations.iter().take(index + 1).cloned().collect();
html! {
a href=(format!("/d/{}/{}", domain, upto.join("/"))) { (path)}
}
}
pub fn gen_path_header(path_seperations: Vec<&str>, domain: &str) -> PreEscaped<String> {
html! {
@for (index, path) in path_seperations.iter().enumerate() {
(gen_path_link(path, index, &path_seperations, domain))
@if index < path_seperations.len()-1 {
(slash_seperator())
};
};
}
}
#[get("/d/<domain>/<paths..>")] #[get("/d/<domain>/<paths..>")]
pub async fn domain_info_route( pub async fn domain_info_route(
ctx: RequestContext, ctx: RequestContext,
@ -80,19 +110,56 @@ pub async fn domain_info_route(
arc: &State<WebsiteArchive>, arc: &State<WebsiteArchive>,
) -> StringResponse { ) -> StringResponse {
let domain = arc.get_domain(domain); let domain = arc.get_domain(domain);
let paths = domain.paths(paths.to_str().unwrap()); let document = domain.path(paths.to_str().unwrap());
let versions: Vec<String> = document
.versions()
.into_iter()
.map(|x| {
x.trim_start_matches("index_")
.trim_end_matches(".html")
.to_string()
})
.collect();
let (path_entries, is_doc) = domain.paths(paths.to_str().unwrap());
let path_seperations: Vec<&str> = paths.to_str().unwrap().split('/').collect();
let content = html! { let content = html! {
h2 class="text-xl font-bold mb-4" { (domain.name) }; h2 class="text-xl font-bold mb-4 flex items-center" {
div class="max-w-md mx-auto p-4 bg-neutral-900 rounded-lg shadow-md" { img class="p-2" src=(format!("/favicon/{}", &domain.name)) {};
ul class="space-y-2" { a href=(format!("/d/{}", &domain.name)) { (domain.name) };
@for path in paths { (slash_seperator())
a href=(path.url()) class="flex items-center gap-2 p-3 border bg-neutral-800 rounded hover:shadow-lg transition" { (gen_path_header(path_seperations, &domain.name))
@if matches!(path, PathEntry::Document(_)) { };
(arrow_icon("red"))
@if !versions.is_empty() {
div class="max-w-md mx-auto p-4 bg-neutral-900 rounded-lg shadow-md mb-8" {
h3 class="font-bold mb-2" { "Saved Versions:" };
@for (index, ver) in versions.into_iter().enumerate() {
a href=(format!("/s/{}/{}?time={}", &domain.name, paths.to_str().unwrap(), ver)) class="text-xs text-blue-400" {
@if index == 0 {
(format!("Latest ({ver})"))
} @else { } @else {
(arrow_icon("blue")) (ver)
}; }
};
}
};
}
@if path_entries.is_empty() && !is_doc {
div class="max-w-md mx-auto p-4 bg-neutral-900 rounded-lg shadow-md" {
p class="p-4 text-xl" { "Nothing found" };
}
}
@if !path_entries.is_empty() {
div class="max-w-md mx-auto p-4 bg-neutral-900 rounded-lg shadow-md" {
h3 class="font-bold mb-2" { "Paths:" };
ul class="space-y-2" {
@for path in path_entries {
a href=(path.url()) class="flex items-center gap-2 p-3 border bg-neutral-800 rounded hover:shadow-lg transition" {
(arrow_icon("blue"))
span class="font-medium" { (path.path()) }; span class="font-medium" { (path.path()) };
}; };
@ -100,6 +167,7 @@ pub async fn domain_info_route(
}; };
}; };
}; };
};
render_page(content, ctx).await render_page(content, ctx).await
} }