Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port /blog to App router; add blog preview cards [#134] #1059

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@keyv/sqlite": "^3.6.6",
"@renovatebot/pep440": "^2.1.20",
"@smithy/node-http-handler": "^2.1.8",
"@types/sanitize-html": "^2.13.0",
"argparse": "^1.0.10",
"aws-sdk": "^2.908.0",
"chalk": "^2.4.1",
Expand Down Expand Up @@ -63,7 +64,7 @@
"luxon": "^3.4.4",
"make-fetch-happen": "^11.1.1",
"mapbox-gl": "^3.2.0",
"marked": "^0.7.0",
"marked": "^14.1.3",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

update marked from 0.7 to 14.1.3

What an upgrade! I'm sure there was a reason for this but it's hard to tell from the commit message. Was there a function that wasn't available in the older version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary reason was I needed to update it off 0.7 to get typing support, and so I figured I might as well update it to "current".

FWIW, 0.7 is "only" five years old — the package versions fairly aggressively, for whatever reason.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I need to rework other stuff in this PR, I have gone back and updated the commit message to include this information.

"mime": "^2.5.2",
"neat-csv": "^7.0.0",
"negotiator": "^0.6.2",
Expand Down
126 changes: 126 additions & 0 deletions static-site/app/blog/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Metadata } from "next";
import { redirect } from "next/navigation";
import React from "react";

import { BigSpacer } from "../../../components/spacers";
import {
siteLogo,
siteTitle,
siteTitleAlt,
siteUrl,
} from "../../../data/BaseConfig";

import { getBlogPosts, markdownToHtml } from "../utils";

import styles from "./styles.module.css";

// just to avoid having to repeat this in a couple method sigs...
interface BlogPostParams {
id: string;
}

// return a list of params that will get handed to this page at build
// time, to statically build out all the blog posts
export function generateStaticParams(): BlogPostParams[] {
return getBlogPosts().map((post) => {
return { id: post.blogUrlName };
});
}

// generate opengraph and other metadata tags
export async function generateMetadata({
params,
}: {
params: BlogPostParams;
}): Promise<Metadata> {
const { id } = params;

// set up some defaults that are independent of the specific blog post
const baseUrl = new URL(siteUrl);
const metadata: Metadata = {
metadataBase: baseUrl,
openGraph: {
description: siteTitleAlt,
images: [
{
url: `${siteUrl}${siteLogo}`,
},
],
siteName: siteTitle,
title: siteTitle,
type: "website",
url: baseUrl,
},
};

// this is the specific post we're rendering
const blogPost = getBlogPosts().find((post) => post.blogUrlName === id);

if (blogPost) {
const description = `Nextstrain blog post from ${blogPost.date}; author(s): ${blogPost.author}`;

metadata.title = blogPost.title;
metadata.description = description;
metadata.openGraph!.description = description;
metadata.openGraph!.title = `${siteTitle}: ${blogPost.title}`;
metadata.openGraph!.url = `/blog/${blogPost.blogUrlName}`;
}

return metadata;
genehack marked this conversation as resolved.
Show resolved Hide resolved
}

export default async function BlogPost({
params,
}: {
params: BlogPostParams;
}): Promise<React.ReactElement> {
const { id } = params;

// we need this list to build the archive list in the sidebar
const allBlogPosts = getBlogPosts();

// and then this is the specific post we're rendering
const blogPost = allBlogPosts.find((post) => post.blogUrlName === id);

// if for some reason we didn't find the post, 404 on out
if (!blogPost) {
redirect("/404");
}

const html = await markdownToHtml(blogPost.mdstring);

return (
<>
<BigSpacer count={2} />

<article className="container">
<div className="row">
<div className="col-lg-8">
<time className={styles.blogPostDate} dateTime={blogPost.date}>
{blogPost.date}
</time>
<h1 className={styles.blogPostTitle}>{blogPost.title}</h1>
<h2 className={styles.blogPostAuthor}>{blogPost.author}</h2>
<div
className={styles.blogPostBody}
dangerouslySetInnerHTML={{
__html: html,
}}
/>
</div>
<div className="col-lg-1" />
<div className={`${styles.blogSidebar} col-lg-3`}>
<h2>Blog Archives</h2>
<ul>
{allBlogPosts.map((p) => (
<li key={p.blogUrlName}>
<a href={p.blogUrlName}>{p.sidebarTitle}</a> ({p.date})
</li>
))}
</ul>
</div>
</div>
</article>
</>
);
}
71 changes: 71 additions & 0 deletions static-site/app/blog/[id]/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.blogPostTitle {
clear: both; /* this is to let the title fall under the floated date, not under it */
color: black;
font-size: 3.5rem;
font-weight: 400;
line-height: 40px;
text-align: left;
width: 100%;
}

.blogPostAuthor {
color: black;
font-size: 2rem;
font-weight: 300;
margin: 1rem 0 2rem;
}

.blogPostDate {
color: black;
float: right;
font-size: 1.4rem;
font-weight: 300;
min-height: 2rem;
}

.blogPostBody {
color: black;
font-size: 1.6rem;
font-weight: 300;
line-height: var(--niceLineHeight);
margin-top: 0px;
padding-bottom: 25px;
width: 100%;
}

.blogPostBody img {
max-width: 100%;
}

.blogPostBody h1 {
color: black;
font-size: 3rem;
margin-top: 20px;
text-align: left;
}
.blogPostBody h2 {
font-size: 2.4rem;
font-weight: 300;
margin-top: 10px;
}
.blogPostBody h3 {
font-size: 1.8rem;
font-weight: 300;
margin-top: 10px;
}
.blogPostBody p {
margin-top: 10px;
}
.blogPostBody li {
margin-left: 3rem;
}

.blogSidebar {
font-size: 14px;
}
.blogSidebar ul {
list-style: none;
}
.blogSidebar ul li {
margin: 1.2rem 0;
}
17 changes: 17 additions & 0 deletions static-site/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { redirect } from "next/navigation";

import { getBlogPosts } from "./utils";

export default function Index(): void {
const mostRecentPost = getBlogPosts()[0];

// _technically_ getBlogPosts() could return an empty array and then
// mostRecentPost would be undefined -- to make the type checker
// happy, if for some reason mostRecentPost is undefined, we will
// detect that and redirect to the 404 page
const redirectTo = mostRecentPost
? `/blog/${mostRecentPost.blogUrlName}`
: `/404`;

redirect(redirectTo);
Comment on lines +12 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The images of the most recent blog fail to load when going to /blog, but load fine at /blog/2024-10-22-oropouche-analysis-and-resources

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i see that.

seems to be because the image URLs are relative to /blog (i.e., img/foo.png, not /blog/img/foo.png), maybe? The images are showing as 404 and the HTTP request was GET /img/oropouche_host_view.png.

oddly, it works fine on localhost... I will dig into the redirect() API and see if I need to do something different, but an initial fix might be tweaking the image URLs in the Oropouche post.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, yeah, this is because of how the redirect() API works, apparently.

I think I'm going to take this as the push from the universe to move ahead with converting the front page to be the five most recent blog posts, which will require some additional refactoring in this PR.

}
Loading