Skip to content

Commit

Permalink
Improve display (#26)
Browse files Browse the repository at this point in the history
* Remove css import

* Add loading

* Side by side

* Hide loading

* Restructure url
  • Loading branch information
j178 authored Feb 11, 2025
1 parent e330e94 commit 337d841
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 67 deletions.
2 changes: 1 addition & 1 deletion src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ impl Render for SvgRenderer {
let mut document = Document::new()
.set("width", total_width)
.set("height", total_height)
.set("style", "background-color: white; @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css');");
.set("style", "background-color: white");

// Title and date on the same line
document =
Expand Down
197 changes: 131 additions & 66 deletions vercel/api/vercel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,26 @@ async fn main() -> Result<(), Error> {
let res = match (req.uri().path(), req.method()) {
("/", method) if method == "GET" => render_form(),
("/", method) if method == "POST" => handle_form_submit(req).await,
("/created", _) => render_created_repos(req).await,
("/contributed", _) => render_contributed_repos(req).await,
(path, method) if method == "GET" && path.ends_with("/created.svg") => {
let username = path
.trim_end_matches("/created.svg")
.trim_start_matches("/");
render_created_svg(username.to_string(), &req).await
}
(path, method) if method == "GET" && path.ends_with("/contributed.svg") => {
let username = path
.trim_end_matches("/contributed.svg")
.trim_start_matches("/");
render_contributed_svg(username.to_string(), &req).await
}
(path, method) if method == "GET" && path.starts_with("/") => {
let username = path.trim_start_matches("/");
if !username.is_empty() {
render_stats_page(username.to_string(), &req).await
} else {
render_form()
}
}
_ => Ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from("Not found"))?),
Expand All @@ -34,49 +52,6 @@ async fn main() -> Result<(), Error> {
run(h).await
}

pub async fn render_created_repos(req: Request) -> Result<Response<Body>, Error> {
let url = Url::parse(&req.uri().to_string()).unwrap();
let query: HashMap<_, _> = url.query_pairs().collect();
let username = query
.get("username")
.ok_or_else(|| anyhow!("name not found"))?;
let max_repos = query
.get("max_repos")
.map(|x| x.parse::<usize>())
.transpose()?;

let repos = github::get_created_repos(username, max_repos).await?;

let mut buf = String::new();
SvgRenderer::new().render_created_repos(&mut buf, &repos, username);

Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "image/svg+xml")
.body(buf.into())?)
}

pub async fn render_contributed_repos(req: Request) -> Result<Response<Body>, Error> {
let url = Url::parse(&req.uri().to_string()).unwrap();
let query: HashMap<_, _> = url.query_pairs().collect();
let username = query
.get("username")
.ok_or_else(|| anyhow!("name not found"))?;
let max_repos = query
.get("max_repos")
.map(|x| x.parse::<usize>())
.transpose()?;
let repos = github::get_contributed_repos(username, max_repos).await?;

let mut buf = String::new();
SvgRenderer::new().render_contributed_repos(&mut buf, &repos, username);

Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "image/svg+xml")
.body(buf.into())?)
}

fn render_form() -> Result<Response<Body>, Error> {
let html = r#"<!DOCTYPE html>
<html>
Expand Down Expand Up @@ -162,20 +137,28 @@ async fn handle_form_submit(req: Request) -> Result<Response<Body>, Error> {
let max_repos = params
.get("max_repos")
.filter(|v| !v.is_empty())
.map(|v| v.to_string());
.map(|v| format!("?max_repos={}", v))
.unwrap_or_default();

let base_url = "https://github-contrib-stats.vercel.app";
let max_repos_param = max_repos
.as_ref()
.map(|m| format!("&max_repos={}", m))
// Redirect to /<username>?max_repos=X
Ok(Response::builder()
.status(StatusCode::FOUND)
.header("Location", format!("/{}{}", username, max_repos))
.body(Body::Empty)?)
}

async fn render_stats_page(username: String, req: &Request) -> Result<Response<Body>, Error> {
let url = Url::parse(&req.uri().to_string()).unwrap();
let query: HashMap<_, _> = url.query_pairs().collect();
let max_repos_param = query
.get("max_repos")
.map(|v| format!("?max_repos={}", v))
.unwrap_or_default();

let created_url = format!(
"{}/created?username={}{}",
base_url, username, max_repos_param
);
let base_url = "https://github-contrib-stats.vercel.app";
let created_url = format!("{}/{}/created.svg{}", base_url, username, max_repos_param);
let contributed_url = format!(
"{}/contributed?username={}{}",
"{}/{}/contributed.svg{}",
base_url, username, max_repos_param
);

Expand All @@ -187,7 +170,7 @@ async fn handle_form_submit(req: Request) -> Result<Response<Body>, Error> {
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
max-width: 800px;
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
line-height: 1.5;
Expand All @@ -204,6 +187,26 @@ async fn handle_form_submit(req: Request) -> Result<Response<Body>, Error> {
max-width: 100%;
height: auto;
margin: 1rem 0;
min-height: 200px;
background: #f6f8fa;
border-radius: 6px;
display: block;
}}
.loading {{
position: relative;
}}
.loading::after {{
content: 'Loading...';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: #6a737d;
opacity: 1;
transition: opacity 0.3s;
}}
.loading img.loaded + .loading::after {{
opacity: 0;
}}
a {{
color: #0366d6;
Expand All @@ -212,21 +215,45 @@ async fn handle_form_submit(req: Request) -> Result<Response<Body>, Error> {
a:hover {{
text-decoration: underline;
}}
.stats-grid {{
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin: 2rem 0;
}}
.stats-column {{
min-width: 0;
}}
@media (max-width: 768px) {{
.stats-grid {{
grid-template-columns: 1fr;
}}
}}
</style>
</head>
<body>
<h1>GitHub Stats for {}</h1>
<h2>Created Repositories</h2>
<div class="markdown-snippet">
![Repos I created]({})
</div>
<img src="{}" alt="Created repositories stats">
<h2>Contributed Repositories</h2>
<div class="markdown-snippet">
![Repos I contributed to]({})
<div class="stats-grid">
<div class="stats-column">
<h2>Created Repositories</h2>
<div class="markdown-snippet">
![Repos I created]({})
</div>
<div class="loading">
<img src="{}" alt="Created repositories stats" onload="this.classList.add('loaded')">
</div>
</div>
<div class="stats-column">
<h2>Contributed Repositories</h2>
<div class="markdown-snippet">
![Repos I contributed to]({})
</div>
<div class="loading">
<img src="{}" alt="Contributed repositories stats" onload="this.classList.add('loaded')">
</div>
</div>
</div>
<img src="{}" alt="Contributed repositories stats">
<p><a href="/">← Generate for another user</a></p>
</body>
Expand All @@ -239,3 +266,41 @@ async fn handle_form_submit(req: Request) -> Result<Response<Body>, Error> {
.header("Content-Type", "text/html")
.body(Body::from(result_html))?)
}

async fn render_created_svg(username: String, req: &Request) -> Result<Response<Body>, Error> {
let url = Url::parse(&req.uri().to_string()).unwrap();
let query: HashMap<_, _> = url.query_pairs().collect();
let max_repos = query
.get("max_repos")
.map(|x| x.parse::<usize>())
.transpose()?;

let repos = github::get_created_repos(&username, max_repos).await?;

let mut buf = String::new();
SvgRenderer::new().render_created_repos(&mut buf, &repos, &username);

Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "image/svg+xml")
.body(buf.into())?)
}

async fn render_contributed_svg(username: String, req: &Request) -> Result<Response<Body>, Error> {
let url = Url::parse(&req.uri().to_string()).unwrap();
let query: HashMap<_, _> = url.query_pairs().collect();
let max_repos = query
.get("max_repos")
.map(|x| x.parse::<usize>())
.transpose()?;

let repos = github::get_contributed_repos(&username, max_repos).await?;

let mut buf = String::new();
SvgRenderer::new().render_contributed_repos(&mut buf, &repos, &username);

Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "image/svg+xml")
.body(buf.into())?)
}

0 comments on commit 337d841

Please sign in to comment.