Created blog post page and updated a few things here and there to work with the blog post page. Has comments and a sidebar.

Signed-off-by: rodude123 <rodude123@gmail.com>
This commit is contained in:
2023-10-18 00:28:34 +01:00
parent 74d1ea35c1
commit 57aa831cdf
159 changed files with 3476 additions and 11912 deletions
+176
View File
@@ -0,0 +1,176 @@
.profile {
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%;
width: 70%;
}
svg {
width: 2em;
fill: var(--primaryDefault);
font-size: 2em;
}
footer {
margin-top: 0;
}
div.byLine {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 1em;
}
div.byLine h3:last-child {
border-left: 2px solid var(--mutedBlack);
padding-left: 1em;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 1em;
}
div.byLine h3:last-child a {
padding: 0 1em;
}
div.cover {
width: 100%;
height: 20rem;
background-position: center;
background-size: cover;
border-radius: 10px;
box-shadow: 0 4px 2px 0 var(--mutedBlack);
}
section#individualPost {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
}
div.mainContent {
border-right: 5px solid var(--mutedGrey);
min-height: 100%;
width: 85%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
}
article {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 0 2em;
}
article a::before,
article a::after {
visibility: hidden;
position: absolute;
margin-top: 1px;
}
article a::before {
content: '<';
margin-left: -0.5em;
}
article a::after {
content: '>';
}
article a:hover::before,
article a:hover::after {
visibility: visible;
}
article h1 {
margin-bottom: 0.5em;
}
article h3 {
margin-top: 0;
}
aside.sideContent {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
width: 15%;
align-self: flex-start;
}
div.authorInfo {
display: grid;
grid-template-columns: 2fr 1fr;
grid-template-rows: repeat(4, auto);
padding-left: 1em;
padding-top: 0.5em;
border-bottom: 5px solid var(--mutedGrey);
}
div.authorInfo .picture {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
grid-row: span 3;
}
div.authorInfo h3 {
grid-column: span 2;
}
div.otherPosts {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 0 1em 1em;
border-bottom: 5px solid var(--mutedGrey);
width: 100%;
}
div.otherPosts a {
padding: 0.5em 1em;
}
div.categories {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 0 1em 1em;
width: 100%;
}
.image img, .image_resized img {
max-width: 100%;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
box-shadow: 0 4px 2px 0 var(--mutedBlack);
}
.image {
justify-self: center;
align-self: center;
}
.image-style-side {
justify-self: flex-end;
align-self: flex-end;
}
section.comments {
padding: 0 2em 2em;
}
+80
View File
@@ -0,0 +1,80 @@
.banner {
max-width: 30%;
box-shadow: 0 6px 4px 0 var(--mutedBlack);
-webkit-border-radius: 0.625rem;
-moz-border-radius: 0.625rem;
border-radius: 0.625rem;
border: 2px solid var(--mutedGrey);
}
h2 {
font-family: Share Tech Mono, monospace;
font-style: normal;
font-weight: normal;
font-size: var(--headingFS);
line-height: 2.5625rem;
text-transform: lowercase;
}
h3 {
font-family: Noto Sans KR, sans-serif;
font-style: normal;
font-weight: 500;
font-size: var(--generalFS);
line-height: 2.1875rem;
}
section.largePost {
/*margin: 0 5em;*/
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: flex-start;
gap: 2em;
width: 100%;
padding: 0 5em 1em;
}
section.largePost:first-child {
border-bottom: 5px solid var(--mutedGrey);
}
section.largePost .outerContent {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
gap: 1em;
}
section.largePost .outerContent > img, section.largePost .outerContent > .content {
width: 50%;
}
section.largePost .outerContent .postContent {
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: flex-start;
}
section.largePost .outerContent .postContent h2 {
align-self: center;
}
section.largePost .outerContent .postContent a {
align-self: flex-end;
}
#main .error {
display: table;
width: 100%;
height: 100vh;
text-align: center;
}
.fof {
display: table-cell;
vertical-align: middle;
}
+1 -1
View File
@@ -6,6 +6,6 @@
@import "../../css/nav.css";
@import "../../css/footer.css";
@import "blogPosts.css";
@import "home.css";
@import "prism.css";
+22 -18
View File
@@ -5,16 +5,25 @@
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Rohit Pai - All Projects</title>
<title>Rohit Pai - Blog</title>
<meta name="title" content="Rohit Pai - Blog">
<meta name="description"
content="This is all the blog posts that Rohit Pai has posted. You'll find posts on various topics, mostly on tech but some on various other random topics.">
<meta name="keywords"
content="Blog, all posts, rohit, pai, rohit pai, tech, web development, self-hosting, hosting">
<meta name="robots" content="index, follow">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="language" content="English">
<meta name="author" content="Rohit Pai">
<link rel="stylesheet" href="/blog/css/main.css">
<script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script>
</head>
<body>
<nav>
<input type="checkbox" id="nav-check">
<a href="/">
<h1>rohit pai</h1>
</a>
<h1>
<a href="/" class="link">rohit pai</a>
</h1>
<div class="nav-btn">
<label for="nav-check">
@@ -25,29 +34,23 @@
</div>
<ul>
<li><a href="/#about" class="textShadow"><span>&lt;</span>about<span>&gt;</span></a></li>
<li><a href="/#curriculumVitae" class="textShadow"><span>&lt;</span>cv<span>&gt;</span></a></li>
<li><a href="/#projects" class="textShadow active"><span>&lt;</span>projects<span>&gt;</span></a></li>
<li><a href="/#contact" class="textShadow"><span>&lt;</span>contact<span>&gt;</span></a></li>
<li><a href="/blog" class="textShadow"><span>&lt;</span>blog<span>&gt;</span></a></li>
<li><a href="/#about" class="textShadow link">about</a></li>
<li><a href="/#curriculumVitae" class="textShadow link">cv</a></li>
<li><a href="/#projects" class="textShadow link">projects</a></li>
<li><a href="/#contact" class="textShadow link">contact</a></li>
<li><a href="/blog" class="textShadow link active">blog</a></li>
</ul>
</nav>
<header>
<div>
<h1>full stack developer</h1>
<a href="/#sayHello" class="btn btnPrimary boxShadowIn boxShadowOut">Contact Me</a>
<a href="#featured"><i class="fa-solid fa-chevron-down"></i></a>
<a href="" id="arrow"><i class="fa-solid fa-chevron-down"></i></a>
</div>
</header>
<main>
<section id="featured">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus accusantium amet autem, commodi corporis
illo ipsam nemo nihil nostrum numquam perspiciatis quo tenetur voluptatum. Architecto atque aut doloremque
incidunt iusto labore obcaecati pariatur, porro qui rem saepe, suscipit tempore voluptatem? Aliquam asperiores
dignissimos error labore odio, praesentium quod reiciendis totam.
</section>
<main id="main">
</main>
<footer class="flexRow">
@@ -60,5 +63,6 @@
<script src="/js/typewriter.js"></script>
<script src="/blog/js/index.js"></script>
<script id="dsq-count-scr" src="https://rohitpaiportfolio.disqus.com/count.js" async></script>
</body>
</html>
+347 -11
View File
@@ -1,3 +1,5 @@
// nav bar scroll effect
const scrollLimit = 150;
document.addEventListener('DOMContentLoaded', () =>
{
@@ -9,6 +11,14 @@ window.addEventListener('popstate', _ =>
goToURL(window.history.state);
});
window.onscroll = () => {
// check if scrolled past limit if so add scrolled class to change background of nav
if (document.body.scrollTop >= scrollLimit || document.documentElement.scrollTop >= scrollLimit) {
document.querySelector("nav").classList.add("scrolled");
} else {
document.querySelector("nav").classList.remove("scrolled");
}
};
/**
* goToURL tries to go to the specified URL if not shows the error page (not yet implemented)
@@ -19,23 +29,349 @@ function goToURL(url)
// Get the current URL and split it into an array
let urlArray = url.split('/');
let newUrl = "";
if (urlArray.includes('blog'))
if (url === "/blog/" || url === "/blog")
{
newUrl = url;
loadHomeContent();
// window.history.pushState(null, null, url);
return;
}
// Check if the URL is a post page
if (urlArray[0] === 'post')
if (urlArray[2] === 'post')
{
// Create a new URL with the dynamic part
newUrl = "/blog/" + url;
// window.history.pushState(null, null, url);
loadIndividualPost(urlArray[urlArray.length - 1]).catch(err => console.log(err));
return;
}
// Update the URL in the browser without reloading the page
window.history.pushState(null, null, newUrl);
if (urlArray[2] === 'category') {
// Create a new URL with the dynamic part
// window.history.pushState(null, null, url);
if (urlArray[3]) {
loadPostsByCategory(urlArray[urlArray.length - 1]);
return;
}
// Get the dynamic part of the URL
document.querySelector("#url").innerHTML = decodeURI(urlArray[urlArray.length - 1]);
}
loadAllCategories();
return;
}
show404();
}
/**
* Creates a large post element
* @param post the post object
* @returns {HTMLDivElement} the outer content of the post
*/
function createLargePost(post) {
let outerContent = document.createElement("div");
outerContent.classList.add("outerContent");
let img = document.createElement("img");
img.className = "banner";
img.src = post.headerImg;
img.alt = post.title;
outerContent.appendChild(img);
let content = document.createElement("div");
content.classList.add("content");
let postContent = document.createElement("div");
postContent.classList.add("postContent");
let categories = "";
post.categories.split(", ").forEach(category => {
categories += `<a href="/blog/category/${category}" class="link">${category}</a>`
if (post.categories.split(", ").length > 1) {
categories += ", ";
}
});
window.categories = categories;
postContent.innerHTML = `
<h2>${post.title}</h2>
<h3>Last updated: ${post.dateModified} | ${categories}</h3>
<p>${post.abstract}</p>
<a href="/blog/post/${post.title}#disqus_thread" class="btn btnPrimary">See Post</a>
`;
content.appendChild(postContent);
outerContent.appendChild(content);
return outerContent;
}
/**
* Loads the home content
*/
function loadHomeContent() {
fetch("/api/blog/post").then(res => res.json().then(json => {
for (let i = 0; i < json.length; i++) {
if (json[i].featured === 1) {
let featuredPost = document.createElement("section");
featuredPost.classList.add("largePost");
featuredPost.id = "featuredPost";
let h1 = document.createElement("h1");
h1.innerHTML = "featured post";
featuredPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
featuredPost.appendChild(outerContent);
document.querySelector("#main").prepend(featuredPost);
}
if (i === 0) {
let latestPost = document.createElement("section");
latestPost.classList.add("largePost");
latestPost.id = "latestPost";
let h1 = document.createElement("h1");
h1.innerHTML = "latest post";
latestPost.appendChild(h1);
let outerContent = createLargePost(json[i]);
latestPost.appendChild(outerContent);
document.querySelector("#main").prepend(latestPost);
}
}
}))
}
/**
* Gets the latest and featured posts
* @returns {Promise<any[]>} the latest and featured posts
*/
async function getLatestAndFeaturedPosts() {
let latestPost = await fetch("/api/blog/post/latest").then(res => res.json());
let featuredPost = await fetch("/api/blog/post/featured").then(res => res.json());
return [latestPost, featuredPost];
}
/**
* Converts a csv to an array
* @param text the csv text
* @returns {string[]} the array
*/
function csvToArray(text) {
let p = '';
let arr = [''];
let i = 0;
let s = true;
let l = null;
for (l of text) {
if ('"' === l) {
if (s && l === p) {
arr[i] += l;
}
s = !s;
} else if (',' === l && s) {
l = arr[++i] = '';
} else if ('\n' === l && s) {
if ('\r' === p) row[i] = row[i].slice(0, -1);
arr = arr[++r] = [l = ''];
i = 0;
} else {
arr[i] += l;
}
p = l;
}
return arr;
}
/**
* Gets the categories
* @returns {Promise<*[]>} the categories
*/
async function getCategories() {
let categories = await fetch("/api/blog/categories").then(res => res.json());
let modifiedCategories = [];
categories.forEach(category => modifiedCategories.push(csvToArray(category.categories)));
return modifiedCategories;
}
/**
* Creates the categories
* @param {*[]} categoriesList the categories
*/
function createCategories(categoriesList) {
let categories = "";
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="link">${category}</a>`));
return categories;
}
/**
* Creates the button categories
* @param {string[][]} categoriesList - the categories
*/
function createButtonCategories(categoriesList) {
let categories = "";
categoriesList.forEach(lst => lst.forEach(category => categories += `<a href="/blog/category/${category}" class="btn btnOutline">${category}</a>`));
return categories;
}
/**
* Creates the side content
* @returns {HTMLElement} the aside element
*/
async function createSideContent() {
let posts = await getLatestAndFeaturedPosts();
let latestPost = posts[0];
let featuredPost = posts[1];
let categoriesList = await getCategories();
let categories = createCategories(categoriesList);
let sideContent = document.createElement("aside");
sideContent.classList.add("sideContent");
sideContent.innerHTML = `
<div class="authorInfo">
<div class="picture">
<img src="/imgs/profile.jpg"
alt="My professional picture taken in brighton near
north street at night wearing a beige jacket and checkered shirt"
class="profile">
<p>Rohit Pai</p>
</div>
<a href="https://linkedin.com/in/rohitpai98">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-2 16h-2v-6h2v6zm-1-6.891c-.607 0-1.1-.496-1.1-1.109 0-.612.492-1.109 1.1-1.109s1.1.497 1.1 1.109c0 .613-.493 1.109-1.1 1.109zm8 6.891h-1.998v-2.861c0-1.881-2.002-1.722-2.002 0v2.861h-2v-6h2v1.093c.872-1.616 4-1.736 4 1.548v3.359z"/>
</svg>
</a>
<a href="mailto:rohit@rohitpai.co.uk">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13.718 10.528c0 .792-.268 1.829-.684 2.642-1.009 1.98-3.063 1.967-3.063-.14 0-.786.27-1.799.687-2.58 1.021-1.925 3.06-1.624 3.06.078zm10.282 1.472c0 6.627-5.373 12-12 12s-12-5.373-12-12 5.373-12 12-12 12 5.373 12 12zm-5-1.194c0-3.246-2.631-5.601-6.256-5.601-4.967 0-7.744 3.149-7.744 7.073 0 3.672 2.467 6.517 7.024 6.517 2.52 0 4.124-.726 5.122-1.288l-.687-.991c-1.022.593-2.251 1.136-4.256 1.136-3.429 0-5.733-2.199-5.733-5.473 0-5.714 6.401-6.758 9.214-5.071 2.624 1.642 2.524 5.578.582 7.083-1.034.826-2.199.799-1.821-.756 0 0 1.212-4.489 1.354-4.975h-1.364l-.271.952c-.278-.785-.943-1.295-1.911-1.295-2.018 0-3.722 2.19-3.722 4.783 0 1.73.913 2.804 2.38 2.804 1.283 0 1.95-.726 2.364-1.373-.3 2.898 5.725 1.557 5.725-3.525z"/>
</svg>
</a>
<a href="https://gitea.rohitpai.co.uk/rodude123">
<svg xmlns="http://www.w3.org/2000/svg"
x="0px" y="0px" viewBox="0 0 1024 1024"
style="enable-background:new -1 0 1024 1024;" xml:space="preserve">
<style type="text/css">.st1 {fill: #FFFFFF;}</style>
<g id="Guides"></g>
<g id="Icon"><circle class="st0" cx="512" cy="512" r="512"/>
<g><path class="st1" d="M762.2,350.3c-100.9,5.3-160.7,8-212,8.5v114.1l-16-7.9l-0.1-106.1c-58.9,0-110.7-3.1-209.1-8.6 c-12.3-0.1-29.5-2.4-47.9-2.5c-47.1-0.1-110.2,33.5-106.7,118C175.8,597.6,296,609.9,344,610.9c5.3,24.7,61.8,110.1,103.6,114.6 H631C740.9,717.3,823.3,351.7,762.2,350.3z M216.2,467.6c-4.7-36.6,11.8-74.8,73.2-73.2C296.1,462,307,501.5,329,561.9 C272.8,554.5,225,536.2,216.2,467.6z M631.8,551.1l-51.3,105.6c-6.5,13.4-22.7,19-36.2,12.5l-105.6-51.3 c-13.4-6.5-19-22.7-12.5-36.2l51.3-105.6c6.5-13.4,22.7-19,36.2-12.5l105.6,51.3C632.7,521.5,638.3,537.7,631.8,551.1z"/>
<path class="st1"
d="M555,609.9c0.1-0.2,0.2-0.3,0.2-0.5c17.2-35.2,24.3-49.8,19.8-62.4c-3.9-11.1-15.5-16.6-36.7-26.6 c-0.8-0.4-1.7-0.8-2.5-1.2c0.2-2.3-0.1-4.7-1-7c-0.8-2.3-2.1-4.3-3.7-6l13.6-27.8l-11.9-5.8L519.1,501c-2,0-4.1,0.3-6.2,1 c-8.9,3.2-13.5,13-10.3,21.9c0.7,1.9,1.7,3.5,2.8,5l-23.6,48.4c-1.9,0-3.8,0.3-5.7,1c-8.9,3.2-13.5,13-10.3,21.9 c3.2,8.9,13,13.5,21.9,10.3c8.9-3.2,13.5-13,10.3-21.9c-0.9-2.5-2.3-4.6-4-6.3l23-47.2c2.5,0.2,5,0,7.5-0.9 c2.1-0.8,3.9-1.9,5.5-3.3c0.9,0.4,1.9,0.9,2.7,1.3c17.4,8.2,27.9,13.2,30,19.1c2.6,7.5-5.1,23.4-19.3,52.3 c-0.1,0.2-0.2,0.5-0.4,0.7c-2.2-0.1-4.4,0.2-6.5,1c-8.9,3.2-13.5,13-10.3,21.9c3.2,8.9,13,13.5,21.9,10.3 c8.9-3.2,13.5-13,10.3-21.9C557.8,613.6,556.5,611.6,555,609.9z"/>
</g>
</g>
</svg>
</a>
<h3>Avid Full Stack Dev | Uni of Notts Grad | Amateur Blogger</h3>
</div>
<div class="otherPosts">
<h2>latest post</h2>
<h3>${latestPost.title}</h3>
<p>${latestPost.abstract}</p>
<a href="/blog/post/${latestPost.title}" class="btn btnPrimary boxShadowIn boxShadowOut">See Post</a>
</div>
<div class="otherPosts">
<h2>featured post</h2>
<h3>${featuredPost.title}</h3>
<p>${featuredPost.abstract}</p>
<a href="/blog/post/${featuredPost.title}" class="btn btnPrimary boxShadowIn boxShadowOut">See Post</a>
</div>
<div class="categories">
<h2>categories</h2>
${categories}
</div>
`;
return sideContent;
}
/**
* Trys to load the individual post if not runs the 404 function
* @param title
*/
async function loadIndividualPost(title) {
document.title = "Rohit Pai - " + decodeURI(title);
await fetch(`/api/blog/post/${title}`).then(async res => {
if (!res.ok) {
show404();
return;
}
await res.json().then(async json => {
// create the post
let post = document.createElement("section");
post.classList.add("post");
post.id = "individualPost";
let mainContent = document.createElement("div");
mainContent.classList.add("mainContent");
let article = document.createElement("article");
article.innerHTML = `
<h1>${json.title}</h1>
<div class="byLine">
<h3>Last updated: ${json.dateModified}</h3>
<h3>${createButtonCategories([csvToArray(json.categories)])}</h3>
</div>
<div class="cover" style="background-image: url('${json.headerImg}')"></div>
${json.body}
`;
let comments = document.createElement("section");
comments.classList.add("comments");
comments.innerHTML = `<h2>Comments</h2>
<div id="disqus_thread"></div>
`;
mainContent.appendChild(article);
mainContent.appendChild(comments);
let sideContent = await createSideContent();
post.appendChild(mainContent);
post.appendChild(sideContent);
document.querySelector("#main").appendChild(post);
let disqus_config = _ => {
this.page.url = window.location.href; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = window.location.href.substring(window.location.href.lastIndexOf("/") + 1); // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
(function () { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://rohitpaiportfolio.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
d.body.appendChild(s);
})();
});
});
}
function loadAllCategories() {
}
/**
* Loads the posts by category
* @param category the category
*/
function loadPostsByCategory(category) {
document.title = "Rohit Pai - " + decodeURI(category);
fetch(`/api/blog/post/category/${category}`).then(res => res.json().then(json => {
let posts = document.createElement("section");
posts.classList.add("posts");
posts.id = "postsByCategory";
let h1 = document.createElement("h1");
h1.innerHTML = category;
posts.appendChild(h1);
for (let i = 0; i < json.length; i++) {
let outerContent = createLargePost(json[i]);
posts.appendChild(outerContent);
}
document.querySelector("#main").appendChild(posts);
}));
}
/**
* Shows the 404 page
*/
function show404() {
document.querySelector("#main").innerHTML = `
<div class="error">
<div class="fof">
<h1>Blog post, Category or page not found</h1>
<a href="/blog/" class="btn btnPrimary">See all blog posts</a>
</div>
</div>
`;
}