add projects page that uses gitea api

This commit is contained in:
hippoz 2021-10-18 21:54:41 +03:00
parent 24d5798d8a
commit 9690372b01
No known key found for this signature in database
GPG key ID: 7C52899193467641
12 changed files with 252 additions and 103 deletions

View file

@ -46,10 +46,11 @@ const specialCharacter = (c) => {
return ""; return "";
} }
const processPageOutput = (out) => { const processPageOutput = async (out) => {
if (typeof out !== "string") { if (typeof out === "function")
out = await out();
if (typeof out !== "string")
throw new Error("got non-string page output (maybe one of the pages isn't exporting a string?)"); throw new Error("got non-string page output (maybe one of the pages isn't exporting a string?)");
}
if (buildConfig.postProcessing.trimOutput) { if (buildConfig.postProcessing.trimOutput) {
out = out.trim(); out = out.trim();
@ -69,13 +70,13 @@ const findAllPages = async (directory) =>
) )
.map(f => directory + "/" + f); // TODO: hack .map(f => directory + "/" + f); // TODO: hack
const buildPage = (pagePath) => processPageOutput(require(pagePath)); const buildPage = async (pagePath) => await processPageOutput(require(pagePath));
const buildAllPages = async (directory) => const buildAllPages = async (directory) =>
(await findAllPages(directory)) await Promise.all((await findAllPages(directory))
.map( .map(
p => [p, buildPage(p)] async p => [p, (await buildPage(p))]
); ));
const exportAllPages = async (sourcePath, outputPath) => { const exportAllPages = async (sourcePath, outputPath) => {
const pages = await buildAllPages(sourcePath); const pages = await buildAllPages(sourcePath);

View file

@ -1,13 +1,15 @@
:root { :root {
--body-bg-color: #2E2E2E; --body-bg-color: #2e2e2e;
--accent-bg-color: #d4d3d3; --accent-bg-color: #d4d3d3;
--accent-color: #949494;
--grayed-text-color: #949494;
--button-accent-color: #3d3d3d; --button-accent-color: #3d3d3d;
--selected-bg-color: #2E2E2E; --selected-bg-color: #2e2e2e;
--hover-bg-color: #4d4d4d; --hover-bg-color: #4d4d4d;
--card-border-radius: 8px; --card-border-radius: 8px;
--button-border-radius: 6px; --button-border-radius: 8px;
--main-font-weight: 500; --main-font-weight: 500;
--cards-length: 600px; --cards-length: 675px;
--fonts-regular: "Noto Sans", "Liberation Sans", sans-serif; --fonts-regular: "Noto Sans", "Liberation Sans", sans-serif;
} }
@ -19,6 +21,8 @@ body {
background-repeat: no-repeat; background-repeat: no-repeat;
font-family: var(--fonts-regular); font-family: var(--fonts-regular);
font-weight: var(--main-font-weight); font-weight: var(--main-font-weight);
color: #000000;
} }
br { br {
@ -28,25 +32,40 @@ br {
} }
.card { .card {
margin: 36px auto; margin: 6px;
padding: 26px; padding: 8px;
background: var(--accent-bg-color); background: var(--accent-bg-color);
border-radius: var(--card-border-radius); border-radius: var(--card-border-radius);
}
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.45); .card.inner-card {
margin: 4px;
margin-bottom: 28px;
padding: 18px;
border: solid var(--accent-color) 1px;
} }
.card.layout-card { .card.layout-card {
margin: 36px auto;
padding: 26px;
width: var(--cards-length); width: var(--cards-length);
box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40);
} }
.navigation-buttons { .grayed-out {
color: var(--grayed-text-color);
}
.align-right {
text-align: right; text-align: right;
} }
.float-right {
.navigation-branding { float: right;
}
.float-left {
float: left; float: left;
} }
.navigation-branding-text { .navigation-branding-text {
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
@ -64,7 +83,6 @@ br {
.button-default { .button-default {
display: inline-block; display: inline-block;
text-decoration: none; text-decoration: none;
font-weight: 500;
border: none; border: none;
background-color: var(--accent-bg-color); background-color: var(--accent-bg-color);
border-radius: var(--button-border-radius); border-radius: var(--button-border-radius);
@ -87,7 +105,6 @@ br {
.button-selected { .button-selected {
color: var(--accent-bg-color); color: var(--accent-bg-color);
background-color: var(--selected-bg-color); background-color: var(--selected-bg-color);
} }
@ -99,8 +116,18 @@ br {
margin: 8px; margin: 8px;
} }
.row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.column {
flex-basis: 100%;
}
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.card.layout-card { .card.layout-card {
width: 85%; width: 80%;
} }
} }

83
package-lock.json generated
View file

@ -9,7 +9,8 @@
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"js-beautify": "^1.14.0" "js-beautify": "^1.14.0",
"node-fetch": "^3.0.0"
} }
}, },
"node_modules/abbrev": { "node_modules/abbrev": {
@ -50,6 +51,14 @@
"proto-list": "~1.2.1" "proto-list": "~1.2.1"
} }
}, },
"node_modules/data-uri-to-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
"engines": {
"node": ">= 6"
}
},
"node_modules/editorconfig": { "node_modules/editorconfig": {
"version": "0.15.3", "version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
@ -64,6 +73,27 @@
"editorconfig": "bin/editorconfig" "editorconfig": "bin/editorconfig"
} }
}, },
"node_modules/fetch-blob": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.2.tgz",
"integrity": "sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -146,6 +176,22 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/node-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz",
"integrity": "sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==",
"dependencies": {
"data-uri-to-buffer": "^3.0.1",
"fetch-blob": "^3.1.2"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/nopt": { "node_modules/nopt": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
@ -199,6 +245,14 @@
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
}, },
"node_modules/web-streams-polyfill": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@ -249,6 +303,11 @@
"proto-list": "~1.2.1" "proto-list": "~1.2.1"
} }
}, },
"data-uri-to-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="
},
"editorconfig": { "editorconfig": {
"version": "0.15.3", "version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
@ -260,6 +319,14 @@
"sigmund": "^1.0.1" "sigmund": "^1.0.1"
} }
}, },
"fetch-blob": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.2.tgz",
"integrity": "sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==",
"requires": {
"web-streams-polyfill": "^3.0.3"
}
},
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -325,6 +392,15 @@
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"node-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz",
"integrity": "sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==",
"requires": {
"data-uri-to-buffer": "^3.0.1",
"fetch-blob": "^3.1.2"
}
},
"nopt": { "nopt": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
@ -366,6 +442,11 @@
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
}, },
"web-streams-polyfill": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q=="
},
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

View file

@ -13,6 +13,7 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"js-beautify": "^1.14.0" "js-beautify": "^1.14.0",
"node-fetch": "^3.0.0"
} }
} }

View file

@ -2,7 +2,8 @@ const Page = require("./components/Page.component");
module.exports = Page({ module.exports = Page({
title: "hippoz blog", title: "hippoz blog",
description: "hippoz blog" description: "hippoz blog",
name: "blog",
})(` })(`
<h2>hippoz's blog</h2> <h2>hippoz's blog</h2>
<p>i think</p> <p>i think</p>

View file

@ -1 +1,2 @@
module.exports = ({ link, text }) => `<a href="${link}" class="button-default">${text}</a>`; module.exports = ({ link, text, selected=false }) =>
`<a href="${link}" class="button-default${selected ? " button-selected" : ""}">${text}</a>`;

View file

@ -1,15 +1,20 @@
const { navigationLinks, navigationBrandingText } = require("../env"); const { navigationLinks, navigationBrandingText } = require("../env");
const LinkButton = require("./LinkButton.component"); const LinkButtonComponent = require("./LinkButton.component");
module.exports = () => ` module.exports = ({ pageName }) => `
<div class="card layout-card noselect"> <div class="card layout-card noselect">
<div class="navigation-branding"> <div class="float-left">
<b class="navigation-branding-text">${navigationBrandingText}</b> <b class="navigation-branding-text">${navigationBrandingText}</b>
</div> </div>
<div class="navigation-buttons"> <div class="align-right">
${ ${
navigationLinks.map( navigationLinks.map(
([link, text]) => LinkButton({ link, text }) ({link, text, name}) =>
LinkButtonComponent({
link,
text,
selected: name === pageName
})
).join("") ).join("")
} }
</div> </div>

View file

@ -1,6 +1,6 @@
const Navigation = require("./Navigation.component"); const NavigationComponent = require("./Navigation.component");
module.exports = ({ title="page", description="a page" }) => (content) => module.exports = ({ title="page", description="a page", name="page" }) => (content) =>
`<!DOCTYPE html> `<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -11,7 +11,9 @@ module.exports = ({ title="page", description="a page" }) => (content) =>
<link rel="stylesheet" href="res/style.css"> <link rel="stylesheet" href="res/style.css">
</head> </head>
<body> <body>
${Navigation()} ${NavigationComponent({
pageName: name
})}
<div class="card layout-card"> <div class="card layout-card">
${content} ${content}
</div> </div>

24
src/env/index.js vendored
View file

@ -1,9 +1,25 @@
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
module.exports = { module.exports = {
navigationLinks: [ navigationLinks: [
["index.html", "home"], {link: "index.html", text: "home", name: "home"},
["blog.html", "blog"], {link: "blog.html", text: "blog", name: "blog"},
["me.html", "me"], {link: "projects.html", text: "projects", name: "projects"},
["https://git.hippoz.xyz", "git"], {link: "https://git.hippoz.xyz", text: "git", name: "__ext_git"},
], ],
repositoryList: {
gitTargetUsername: "hippoz",
repositoryFetchUrl: `https://git.hippoz.xyz/api/v1/users/hippoz/repos?page=1&limit=100`,
_cache: null,
fetchProjects: async function() {
if (this._cache) return this._cache;
const response = await fetch(this.repositoryFetchUrl);
const json = await response.json();
this._cache = json;
return json;
}
},
navigationBrandingText: "hippoz." navigationBrandingText: "hippoz."
} }

View file

@ -2,7 +2,8 @@ const Page = require("./components/Page.component");
module.exports = Page({ module.exports = Page({
title: "hippoz", title: "hippoz",
description: "hippoz website homepage" description: "hippoz website homepage",
name: "home",
})(` })(`
<h2>hippoz's website</h2> <h2>hippoz's website</h2>
<p>i think</p> <p>i think</p>

View file

@ -1,12 +0,0 @@
const Page = require("./components/Page.component");
module.exports = Page({
title: "about",
description: "hippoz about page"
})(`
<h2>hello</h2>
<p>i think</p>
<br>
<br>
<p>i'm hippoz. that's about it.</p>
`);

25
src/projects.page.js Normal file
View file

@ -0,0 +1,25 @@
const { repositoryList } = require("./env");
const Page = require("./components/Page.component");
module.exports = async () => Page({
title: "hippoz",
description: "hippoz website homepage",
name: "projects",
})(`
${
(await repositoryList.fetchProjects())
.sort((a, b) => b.size - a.size) // biggest projects first
.filter(a => !a.archived)
.map(
repo =>`
<div class="card inner-card">
<div>
<b>${repo.fork ? "🍴" : "📘"} ${repo.name}</b>
<a class="button-default float-right" href=${repo.html_url}>view >></a>
</div>
${repo.description ? `<p>${repo.description}</p>` : `<p class="grayed-out">Mysterious! This project has no description.</p>`}
</div>`
)
.join("\n")
}
`);