Skip to content

Commit

Permalink
Merge pull request #253 from chrismwilliams/feature/post-year
Browse files Browse the repository at this point in the history
Feature: add sort option
  • Loading branch information
chrismwilliams authored May 7, 2024
2 parents 8abe5a9 + 9cc164e commit 621299a
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 187 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,5 @@
"remark-unwrap-images": "^4.0.0",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.4"
},
"packageManager": "pnpm@8.6.1"
}
}
4 changes: 3 additions & 1 deletion src/components/blog/PostPreview.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import type { HTMLTag, Polymorphic } from "astro/types";
import type { CollectionEntry } from "astro:content";
import { getPostSortDate } from "@/data/post";
import FormattedDate from "../FormattedDate.astro";
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
Expand All @@ -10,7 +12,7 @@ type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
};
const { as: Tag = "div", post, withDesc = false } = Astro.props;
const postDate = post.data.updatedDate ?? post.data.publishDate;
const postDate = getPostSortDate(post);
---

<FormattedDate class="min-w-[120px] text-gray-600 dark:text-gray-400" date={postDate} />
Expand Down
2 changes: 1 addition & 1 deletion src/content/post/draft-post.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "A working draft title"
description: "This post is for testing the draft post functionality"
publishDate: "10 Sept 2023"
publishDate: "10 March 2024"
tags: ["test"]
draft: true
---
Expand Down
58 changes: 42 additions & 16 deletions src/data/post.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,64 @@
import type { CollectionEntry } from "astro:content";
import { getCollection } from "astro:content";
import { siteConfig } from "@/site-config";
import { type CollectionEntry, getCollection } from "astro:content";

/** Note: this function filters out draft posts based on the environment */
/** filter out draft posts based on the environment */
export async function getAllPosts() {
return await getCollection("post", ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
return import.meta.env.PROD ? !data.draft : true;
});
}

export function sortMDByDate(posts: Array<CollectionEntry<"post">>) {
/** returns the date of the post based on option in siteConfig.sortPostsByUpdatedDate */
export function getPostSortDate(post: CollectionEntry<"post">) {
return siteConfig.sortPostsByUpdatedDate && post.data.updatedDate !== undefined
? new Date(post.data.updatedDate)
: new Date(post.data.publishDate);
}

/** sort post by date (by siteConfig.sortPostsByUpdatedDate), desc.*/
export function sortMDByDate(posts: CollectionEntry<"post">[]) {
return posts.sort((a, b) => {
const aDate = new Date(a.data.updatedDate ?? a.data.publishDate).valueOf();
const bDate = new Date(b.data.updatedDate ?? b.data.publishDate).valueOf();
const aDate = getPostSortDate(a).valueOf();
const bDate = getPostSortDate(b).valueOf();
return bDate - aDate;
});
}

/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
export function getAllTags(posts: Array<CollectionEntry<"post">>) {
/** groups posts by year (based on option siteConfig.sortPostsByUpdatedDate), using the year as the key
* Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so.
*/
export function groupPostsByYear(posts: CollectionEntry<"post">[]) {
return posts.reduce<Record<string, CollectionEntry<"post">[]>>((acc, post) => {
const year = getPostSortDate(post).getFullYear();
if (!acc[year]) {
acc[year] = [];
}
acc[year]?.push(post);
return acc;
}, {});
}

/** returns all tags created from posts (inc duplicate tags)
* Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so.
* */
export function getAllTags(posts: CollectionEntry<"post">[]) {
return posts.flatMap((post) => [...post.data.tags]);
}

/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
export function getUniqueTags(posts: Array<CollectionEntry<"post">>) {
/** returns all unique tags created from posts
* Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so.
* */
export function getUniqueTags(posts: CollectionEntry<"post">[]) {
return [...new Set(getAllTags(posts))];
}

/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
export function getUniqueTagsWithCount(
posts: Array<CollectionEntry<"post">>,
): Array<[string, number]> {
/** returns a count of each unique tag - [[tagName, count], ...]
* Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so.
* */
export function getUniqueTagsWithCount(posts: CollectionEntry<"post">[]): [string, number][] {
return [
...getAllTags(posts).reduce(
(acc, t) => acc.set(t, (acc.get(t) || 0) + 1),
(acc, t) => acc.set(t, (acc.get(t) ?? 0) + 1),
new Map<string, number>(),
),
].sort((a, b) => b[1] - a[1]);
Expand Down
4 changes: 2 additions & 2 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
import SocialList from "@/components/SocialList.astro";
import PostPreview from "@/components/blog/PostPreview.astro";
import PageLayout from "@/layouts/Base.astro";
import { getAllPosts, sortMDByDate } from "@/data/post";
import PageLayout from "@/layouts/Base.astro";
const MAX_POSTS = 10;
const allPosts = await getAllPosts();
Expand Down Expand Up @@ -79,7 +79,7 @@ const cactusTech: { desc: string; href: string; title: string }[] = [
<dl class="space-y-4">
{
cactusTech.map(({ desc, href, title }) => (
<div class="flex flex-col gap-2 md:flex-row">
<div class="flex flex-col gap-2 sm:flex-row">
<dt>
<span class="flex">
<a class="cactus-link" href={href} rel="noopener noreferrer" target="_blank">
Expand Down
34 changes: 17 additions & 17 deletions src/pages/og-image/[slug].png.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import type { APIContext, InferGetStaticPropsType } from "astro";
import satori, { type SatoriOptions } from "satori";
import { html } from "satori-html";
import { Resvg } from "@resvg/resvg-js";
import { siteConfig } from "@/site-config";
import { getFormattedDate } from "@/utils";
import { getAllPosts } from "@/data/post";

import RobotoMono from "@/assets/roboto-mono-regular.ttf";
import RobotoMonoBold from "@/assets/roboto-mono-700.ttf";
import RobotoMono from "@/assets/roboto-mono-regular.ttf";
import { getAllPosts } from "@/data/post";
import { siteConfig } from "@/site-config";
import { getFormattedDate } from "@/utils";
import { Resvg } from "@resvg/resvg-js";
import satori, { type SatoriOptions } from "satori";
import { html } from "satori-html";

const ogOptions: SatoriOptions = {
width: 1200,
height: 630,
// debug: true,
fonts: [
{
name: "Roboto Mono",
data: Buffer.from(RobotoMono),
weight: 400,
name: "Roboto Mono",
style: "normal",
weight: 400,
},
{
name: "Roboto Mono",
data: Buffer.from(RobotoMonoBold),
weight: 700,
name: "Roboto Mono",
style: "normal",
weight: 700,
},
],
height: 630,
width: 1200,
};

const markup = (title: string, pubDate: string) =>
Expand Down Expand Up @@ -61,18 +61,18 @@ const markup = (title: string, pubDate: string) =>
type Props = InferGetStaticPropsType<typeof getStaticPaths>;

export async function GET(context: APIContext) {
const { title, pubDate } = context.props as Props;
const { pubDate, title } = context.props as Props;

const postDate = getFormattedDate(pubDate, {
weekday: "long",
month: "long",
weekday: "long",
});
const svg = await satori(markup(title, postDate), ogOptions);
const png = new Resvg(svg).render().asPng();
return new Response(png, {
headers: {
"Content-Type": "image/png",
"Cache-Control": "public, max-age=31536000, immutable",
"Content-Type": "image/png",
},
});
}
Expand All @@ -84,8 +84,8 @@ export async function getStaticPaths() {
.map((post) => ({
params: { slug: post.slug },
props: {
title: post.data.title,
pubDate: post.data.updatedDate ?? post.data.publishDate,
title: post.data.title,
},
}));
}
37 changes: 23 additions & 14 deletions src/pages/posts/[...page].astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import type { CollectionEntry } from "astro:content";
import Pagination from "@/components/Paginator.astro";
import PostPreview from "@/components/blog/PostPreview.astro";
import { getAllPosts, getUniqueTags, groupPostsByYear, sortMDByDate } from "@/data/post";
import PageLayout from "@/layouts/Base.astro";
import { getAllPosts, getUniqueTags, sortMDByDate } from "@/data/post";
export const getStaticPaths = (async ({ paginate }) => {
const MAX_POSTS_PER_PAGE = 10;
const allPosts = await getAllPosts();
const allPostsByDate = sortMDByDate(allPosts);
const uniqueTags = getUniqueTags(allPosts);
return paginate(allPostsByDate, { pageSize: 10, props: { uniqueTags } });
return paginate(allPostsByDate, { pageSize: MAX_POSTS_PER_PAGE, props: { uniqueTags } });
}) satisfies GetStaticPaths;
interface Props {
Expand All @@ -29,38 +30,46 @@ const meta = {
const paginationProps = {
...(page.url.prev && {
prevUrl: {
text: `← Previous Posts`,
text: `← Previous Page`,
url: page.url.prev,
},
}),
...(page.url.next && {
nextUrl: {
text: `Next Posts`,
text: `Next Page`,
url: page.url.next,
},
}),
};
const groupedByYear = groupPostsByYear(page.data);
const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a);
---

<PageLayout meta={meta}>
<h1 class="title mb-6">Posts</h1>
<div class="grid gap-y-16 sm:grid-cols-[3fr_1fr] sm:gap-x-8">
<section aria-label="Blog post list">
<ul class="space-y-8 text-start">
{
page.data.map((p) => (
<li class="flex flex-col flex-wrap gap-2 sm:flex-row [&_q]:basis-full">
<PostPreview as="h2" post={p} withDesc />
</li>
))
}
</ul>
{
descYearKeys.map((yearKey) => (
<>
<h2 class="title text-lg">{yearKey}</h2>
<ul class="mb-8 mt-6 space-y-8 text-start">
{groupedByYear[yearKey]?.map((p) => (
<li class="flex flex-col gap-2 sm:flex-row">
<PostPreview post={p} />
</li>
))}
</ul>
</>
))
}
<Pagination {...paginationProps} />
</section>
{
!!uniqueTags.length && (
<aside>
<h2 class="mb-4 flex items-center text-lg font-semibold">
<h2 class="title mb-4 flex items-center text-lg">
<svg
aria-hidden="true"
class="h-6 w-6"
Expand Down
2 changes: 1 addition & 1 deletion src/pages/tags/[tag]/[...page].astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type { CollectionEntry } from "astro:content";
import Pagination from "@/components/Paginator.astro";
import PostPreview from "@/components/blog/PostPreview.astro";
import PageLayout from "@/layouts/Base.astro";
import { getAllPosts, getUniqueTags, sortMDByDate } from "@/data/post";
import PageLayout from "@/layouts/Base.astro";
export const getStaticPaths: GetStaticPaths = async ({ paginate }) => {
const allPosts = await getAllPosts();
Expand Down
54 changes: 28 additions & 26 deletions src/site.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import type { AstroExpressiveCodeOptions } from "astro-expressive-code";
export const siteConfig: SiteConfig = {
// Used as both a meta property (src/components/BaseHead.astro L:31 + L:49) & the generated satori png (src/pages/og-image/[slug].png.ts)
author: "Chris Williams",
// Meta property used to construct the meta title property, found in src/components/BaseHead.astro L:11
title: "Astro Theme Cactus",
// Meta property used as the default description meta property
description: "An opinionated starter theme for Astro",
// HTML lang property, found in src/layouts/Base.astro L:18
lang: "en-GB",
// Meta property, found in src/components/BaseHead.astro L:42
ogLocale: "en_GB",
// Date.prototype.toLocaleDateString() parameters, found in src/utils/date.ts.
date: {
locale: "en-GB",
Expand All @@ -21,32 +13,52 @@ export const siteConfig: SiteConfig = {
year: "numeric",
},
},
// Meta property used as the default description meta property
description: "An opinionated starter theme for Astro",
// HTML lang property, found in src/layouts/Base.astro L:18
lang: "en-GB",
// Meta property, found in src/components/BaseHead.astro L:42
ogLocale: "en_GB",
// Option to sort posts by updatedDate if set to true (if property exists). Default (false) will sort by publishDate
sortPostsByUpdatedDate: false,
// Meta property used to construct the meta title property, found in src/components/BaseHead.astro L:11
title: "Astro Theme Cactus",
webmentions: {
// Webmention.io API endpoint. Get your own here: https://webmention.io/, and follow this blog post: https://astro-cactus.chriswilliams.dev/posts/webmentions/
link: "https://webmention.io/astro-cactus.chriswilliams.dev/webmention",
},
};

// Used to generate links in both the Header & Footer.
export const menuLinks: Array<{ title: string; path: string }> = [
export const menuLinks: { path: string; title: string }[] = [
{
title: "Home",
path: "/",
title: "Home",
},
{
title: "About",
path: "/about/",
title: "About",
},
{
title: "Blog",
path: "/posts/",
title: "Blog",
},
];

// https://expressive-code.com/reference/configuration/
export const expressiveCodeOptions: AstroExpressiveCodeOptions = {
// One dark, one light theme => https://expressive-code.com/guides/themes/#available-themes
themes: ["dracula", "github-light"],
styleOverrides: {
borderRadius: "4px",
codeFontFamily:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;',
codeFontSize: "0.875rem",
codeLineHeight: "1.7142857rem",
codePaddingInline: "1rem",
frames: {
frameBoxShadowCssValue: "none",
},
uiLineHeight: "inherit",
},
themeCssSelector(theme, { styleVariants }) {
// If one dark and one light theme are available
// generate theme CSS selectors compatible with cactus-theme dark mode switch
Expand All @@ -58,17 +70,7 @@ export const expressiveCodeOptions: AstroExpressiveCodeOptions = {
// return default selector
return `[data-theme="${theme.name}"]`;
},
// One dark, one light theme => https://expressive-code.com/guides/themes/#available-themes
themes: ["dracula", "github-light"],
useThemedScrollbars: false,
styleOverrides: {
frames: {
frameBoxShadowCssValue: "none",
},
uiLineHeight: "inherit",
codeFontSize: "0.875rem",
codeLineHeight: "1.7142857rem",
borderRadius: "4px",
codePaddingInline: "1rem",
codeFontFamily:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;',
},
};
Loading

0 comments on commit 621299a

Please sign in to comment.