diff --git a/pages/blog.js b/pages/blog.js index 2c6e03d49..ec29847f8 100644 --- a/pages/blog.js +++ b/pages/blog.js @@ -1,6 +1,6 @@ import BlogRes from "src/Blog.mjs"; -export { getStaticProps } from "src/Blog.mjs"; +export { getStaticProps_All as getStaticProps } from "src/Blog.mjs"; export default function Blog(props) { return diff --git a/pages/blog/archived.js b/pages/blog/archived.js new file mode 100644 index 000000000..112948ab4 --- /dev/null +++ b/pages/blog/archived.js @@ -0,0 +1,7 @@ +import BlogRes from "src/Blog.mjs"; + +export { getStaticProps_Archived as getStaticProps } from "src/Blog.mjs"; + +export default function Blog(props) { + return +} diff --git a/src/Blog.res b/src/Blog.res index f2c0c57d2..7ba4ccc51 100644 --- a/src/Blog.res +++ b/src/Blog.res @@ -39,44 +39,34 @@ module Badge = { } } -module CategorySelector = { - type selection = - | All - | Archived - let renderTab = (~text: string, ~isActive: bool, ~onClick) => { - let active = "bg-gray-20 text-gray-80 rounded py-1" -
- {React.string(text)} -
- } +type category = + | /** Actually only unarchived */ All + | Archived +module CategorySelector = { @react.component - let make = (~selected: selection, ~onSelected: selection => unit) => { + let make = (~selected: category) => { let tabs = [All, Archived]
- {Belt.Array.map(tabs, tab => { - let onClick = evt => { - evt->ReactEvent.Mouse.preventDefault - onSelected(tab) - } - + {tabs + ->Belt.Array.map(tab => { // Deep comparison here! let isActive = selected == tab - - let text = switch tab { - | All => "All" - | Archived => "Archived" + let text = (tab :> string) + let href = switch tab { + | All => "/blog" + | Archived => "/blog/archived" } - - renderTab(~isActive, ~text, ~onClick) - })->React.array} + let className = + switch isActive { + | true => "bg-gray-20 text-gray-80 rounded py-1" + | false => "hover:cursor-pointer bg-white hover:text-gray-80" + } ++ " px-4 inline-block" + {React.string(text)} + }) + ->React.array}
} } @@ -209,15 +199,10 @@ module FeatureCard = { type params = {slug: string} -type props = { - posts: array, - archived: array, -} +type props = {posts: array, category: category} let default = (props: props): React.element => { - let {posts, archived} = props - - let (currentSelection, setSelection) = React.useState(() => CategorySelector.All) + let {posts, category} = props let content = if Belt.Array.length(posts) === 0 { /*
{React.string("Currently no posts available")}
; */ @@ -226,16 +211,11 @@ let default = (props: props): React.element => { {React.string("This blog is currently in the works.")} } else { - let filtered = switch currentSelection { - | All => posts - | Archived => archived - } - - let result = switch Belt.Array.length(filtered) { + let result = switch Belt.Array.length(posts) { | 0 =>
{React.string("No posts for this category available...")}
| _ => - let first = Belt.Array.getExn(filtered, 0) - let rest = Js.Array2.sliceFrom(filtered, 1) + let first = Belt.Array.getExn(posts, 0) + let rest = Js.Array2.sliceFrom(posts, 1) let featureBox =
@@ -280,9 +260,7 @@ let default = (props: props): React.element => { <>
- setSelection(_ => selection)} selected=currentSelection - /> +
result @@ -316,12 +294,19 @@ let default = (props: props): React.element => { } -let getStaticProps: Next.GetStaticProps.t = async _ctx => { - let (archived, nonArchived) = BlogApi.getAllPosts()->Belt.Array.partition(data => data.archived) +let getStaticProps_All: Next.GetStaticProps.t = async _ctx => { + let props = { + posts: BlogApi.getLivePosts(), + category: All, + } + + {"props": props} +} +let getStaticProps_Archived: Next.GetStaticProps.t = async _ctx => { let props = { - posts: nonArchived, - archived, + posts: BlogApi.getArchivedPosts(), + category: Archived, } {"props": props} diff --git a/src/Blog.resi b/src/Blog.resi index f49000d26..21b92e49c 100644 --- a/src/Blog.resi +++ b/src/Blog.resi @@ -3,6 +3,9 @@ let defaultPreviewImg: string type params type props +type category = All | Archived + let default: props => React.element -let getStaticProps: Next.GetStaticProps.t +let getStaticProps_All: Next.GetStaticProps.t +let getStaticProps_Archived: Next.GetStaticProps.t diff --git a/src/common/BlogApi.res b/src/common/BlogApi.res index 5a02de7be..a7f512244 100644 --- a/src/common/BlogApi.res +++ b/src/common/BlogApi.res @@ -42,14 +42,14 @@ let blogPathToSlug = path => { path->Js.String2.replaceByRe(%re(`/^(archive\/)?\d\d\d\d-\d\d-\d\d-(.+)\.mdx$/`), "$2") } +let mdxFiles = dir => { + Node.Fs.readdirSync(dir)->Js.Array2.filter(path => Node.Path.extname(path) === ".mdx") +} + let getAllPosts = () => { let postsDirectory = Node.Path.join2(Node.Process.cwd(), "_blogposts") let archivedPostsDirectory = Node.Path.join2(postsDirectory, "archive") - let mdxFiles = dir => { - Node.Fs.readdirSync(dir)->Js.Array2.filter(path => Node.Path.extname(path) === ".mdx") - } - let nonArchivedPosts = mdxFiles(postsDirectory)->Js.Array2.map(path => { let {GrayMatter.data: data} = Node.Path.join2(postsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter @@ -81,6 +81,49 @@ let getAllPosts = () => { }) } +let getLivePosts = () => { + let postsDirectory = Node.Path.join2(Node.Process.cwd(), "_blogposts") + + let livePosts = mdxFiles(postsDirectory)->Js.Array2.map(path => { + let {GrayMatter.data: data} = + Node.Path.join2(postsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter + switch BlogFrontmatter.decode(data) { + | Error(msg) => Js.Exn.raiseError(msg) + | Ok(d) => { + path, + frontmatter: d, + archived: false, + } + } + }) + + livePosts->Js.Array2.sortInPlaceWith((a, b) => { + String.compare(Node.Path.basename(b.path), Node.Path.basename(a.path)) + }) +} + +let getArchivedPosts = () => { + let postsDirectory = Node.Path.join2(Node.Process.cwd(), "_blogposts") + let archivedPostsDirectory = Node.Path.join2(postsDirectory, "archive") + + let archivedPosts = mdxFiles(archivedPostsDirectory)->Js.Array2.map(path => { + let {GrayMatter.data: data} = + Node.Path.join2(archivedPostsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter + switch BlogFrontmatter.decode(data) { + | Error(msg) => Js.Exn.raiseError(msg) + | Ok(d) => { + path: Node.Path.join2("archive", path), + frontmatter: d, + archived: true, + } + } + }) + + archivedPosts->Js.Array2.sortInPlaceWith((a, b) => { + String.compare(Node.Path.basename(b.path), Node.Path.basename(a.path)) + }) +} + module RssFeed = { // Module inspired by // https://gist.github.com/fredrikbergqvist/36704828353ebf5379a5c08c7583fe2d diff --git a/src/common/BlogApi.resi b/src/common/BlogApi.resi index f4542492f..02482047f 100644 --- a/src/common/BlogApi.resi +++ b/src/common/BlogApi.resi @@ -5,6 +5,8 @@ type post = { } let getAllPosts: unit => array +let getLivePosts: unit => array +let getArchivedPosts: unit => array let blogPathToSlug: string => string module RssFeed: {