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

Feature/post series #31

Merged
merged 8 commits into from
Jul 5, 2023
Merged
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- `aria-label` to `Skip to content` link
- not found page
- post series type

### Changed

- Added contributors to docs
- Redesigned work availability badge
- Fixed table of contents crash

## [0.4.1] - 2023-07-03

### Changed
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -153,14 +153,18 @@ Create a PR and add your blog to this list if you're using the template!
- [x] about section on homepage
- [x] search & command bar
- [x] Analytics: Vercel, Umami
- [x] Post series
- [x] Not found page
- [ ] Docs rework
- [ ] hero title and subtitle text HTML support(?)
- [ ] Social sharing buttons
- [ ] Other analytics providers (fathom, simplelytics, plausible, etc)
- [ ] Design improvements (whitespace, layout, etc.)
- [ ] 404, error, and loading pages
- [ ] Code preview component
- [ ] Code highlight improvements (copy code, theme)
- [ ] `manifest.json`
- [ ] newsletter integration (form, api route, keys, welcome page, previous issues)
- [ ] Post series
- [ ] Hidden content (behind email subscription)
- [ ] 100 lighthouse score
- [ ] Command bar fuzzy search in content
@@ -180,7 +184,6 @@ Create a PR and add your blog to this list if you're using the template!
- [ ] code playground instead of code highlighting (?)
- [ ] Categories and/or tags (?)
- [ ] Commenting system (?)
- [ ] Social sharing buttons (?)
- [ ] keyboard-based navigation with hotkeys (?)
- [ ] multiple layouts (sidebar, full-width, etc.) (?)
- [ ] multilang support (?)
1 change: 1 addition & 0 deletions app/(site)/layout.tsx
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
<a
className="absolute left-0 top-0 z-[9999] m-3 block -translate-y-96 overflow-hidden bg-white p-3 text-2xl text-black transition focus:translate-y-0"
href="#main-content"
aria-label="Skip to Content"
>
Skip to Content
</a>
4 changes: 3 additions & 1 deletion app/(site)/page.tsx
Original file line number Diff line number Diff line change
@@ -28,7 +28,9 @@ export default async function Home() {
const aboutPage = await getAboutPage();
const posts = allPosts
.filter((post) => post.status === "published")
.sort((a, b) => compareDesc(new Date(a.publishedDate), new Date(b.publishedDate)));
.sort((a, b) =>
compareDesc(new Date(a.lastUpdatedDate || a.publishedDate), new Date(b.lastUpdatedDate || b.publishedDate))
);

return (
<div className="pb-10">
28 changes: 26 additions & 2 deletions app/(site)/posts/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import { allPosts } from "contentlayer/generated";
import { PostSeries, PostWithSeries, SeriesItem } from "@/types";
import { allPosts, Post } from "contentlayer/generated";
import { format, parseISO } from "date-fns";
import { Home } from "lucide-react";

@@ -10,6 +11,7 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { Mdx } from "@/components/mdx-components";
import { PostSeriesBox } from "@/components/post-series-box";
import { TableOfContents } from "@/components/table-of-contents";

interface PostProps {
@@ -18,13 +20,30 @@ interface PostProps {
};
}

async function getPostFromParams(params: PostProps["params"]) {
async function getPostFromParams(params: PostProps["params"]): Promise<any> {
const post = allPosts.find((post) => post.slug === params.slug);

if (!post) {
null;
}

if (post?.series) {
const seriesPosts: SeriesItem[] = allPosts
.filter((p) => p.series?.title === post.series?.title)
.sort((a, b) => Number(a.series!.order) - Number(b.series!.order))
.map((p) => {
return {
title: p.title,
slug: p.slug,
status: p.status,
isCurrent: p.slug === post.slug,
};
});
if (seriesPosts.length > 0) {
return { ...post, series: { ...post.series, posts: seriesPosts } as PostSeries };
}
}

return post;
}

@@ -130,6 +149,11 @@ export default async function PostPage({ params }: PostProps) {
<p className="mb-2 mt-0 text-xl text-slate-700 dark:text-slate-200">{post.description}</p>
)}
<hr className="my-4" />
{post?.series && (
<div className="not-prose">
<PostSeriesBox data={post.series} />
</div>
)}
<Mdx code={post.body.code} />
<hr className="my-4" />
{post.tags && (
4 changes: 3 additions & 1 deletion app/(site)/posts/page.tsx
Original file line number Diff line number Diff line change
@@ -16,7 +16,9 @@ export async function generateMetadata(): Promise<Metadata> {
export default function Blog() {
const posts = allPosts
.filter((post) => post.status === "published")
.sort((a, b) => compareDesc(new Date(a.publishedDate), new Date(b.publishedDate)));
.sort((a, b) =>
compareDesc(new Date(a.lastUpdatedDate || a.publishedDate), new Date(b.lastUpdatedDate || b.publishedDate))
);
return (
<div className="container">
<div className="prose mx-auto max-w-5xl dark:prose-invert prose-headings:mb-3 prose-headings:mt-8 prose-headings:font-heading prose-headings:font-bold prose-headings:leading-tight hover:prose-a:text-accent-foreground prose-a:prose-headings:no-underline">
3 changes: 1 addition & 2 deletions components/footer.tsx
Original file line number Diff line number Diff line change
@@ -5,11 +5,10 @@ import Link from "next/link";
import { Github, Instagram, Linkedin, Mail, Twitter, Youtube } from "lucide-react";

import siteMetadata, { defaultAuthor } from "@/lib/metadata";
import { Button } from "@/components/ui/button";
import { CopyButton } from "@/components/copy-button";
import { Signature } from "@/components/signature";

import { Button } from "./ui/button";

const Footer = () => {
return (
<footer className="mx-auto flex max-w-6xl flex-col items-center gap-6 border-t py-6">
7 changes: 6 additions & 1 deletion components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -41,7 +41,12 @@ export function Navbar() {
</>
) : (
<Link href={item.href as string} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>{item.title}</NavigationMenuLink>
<NavigationMenuLink
target={item?.href?.startsWith("http") ? "_blank" : "_self"}
className={navigationMenuTriggerStyle()}
>
{item.title}
</NavigationMenuLink>
</Link>
)}
</NavigationMenuItem>
53 changes: 53 additions & 0 deletions components/post-series-box.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Link from "next/link";
import { PostSeries } from "@/types";

import { cn } from "@/lib/utils";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";

export type Props = {
data: PostSeries;
};

export const PostSeriesBox = ({ data }: Props) => {
const currentIndex = data.posts.findIndex((post) => post.isCurrent) + 1;

return (
<Card className="mb-4">
<CardHeader>
<CardTitle>Series: {data.title}</CardTitle>
</CardHeader>
<CardContent>
Episodes: ({currentIndex}/{data.posts.length})
<ul>
{data.posts.map((p) => (
<li
key={p.slug}
className={cn(
"relative my-3 list-none pl-7 text-sm before:absolute before:left-1 before:top-[9px] before:h-1.5 before:w-1.5 before:rounded-full",
p.isCurrent &&
"before:bg-accent-foreground/90 before:ring-[3px] before:ring-purple-400/20 before:ring-offset-1 before:ring-offset-black/10",
!p.isCurrent &&
"font-bold before:bg-primary/30 hover:before:bg-accent-foreground/90 hover:before:ring-[3px] hover:before:ring-purple-400/20 hover:before:ring-offset-1 hover:before:ring-offset-black/10"
)}
>
{p.status === "published" ? (
p.isCurrent ? (
<span>{p.title}</span>
) : (
<Link
className="transition-colors duration-200 ease-in-out hover:text-accent-foreground"
href={`/posts/${p.slug}`}
>
{p.title}
</Link>
)
) : (
<span>{p.title}</span>
)}
</li>
))}
</ul>
</CardContent>
</Card>
);
};
2 changes: 1 addition & 1 deletion components/table-of-contents.tsx
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ export function TableOfContents({ chapters }: TocProps) {
);

chapters.forEach((chapter) => {
const element = document.querySelector(`#${chapter.slug}`);
const element = document.getElementById(chapter.slug);
if (element) {
observer.observe(element);
}
16 changes: 16 additions & 0 deletions content/posts/choosing-providers.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: "Choosing Providers"
publishedDate: "2023-07-05"
lastUpdatedDate: "2023-07-05"
tags:
- "starter"
- "development"
- "docs"
description: "How to choose and set up Analytics, newsletter, and other providers for your blog."
status: published
series:
order: 2
title: "Using this template"
---

This post will be the third part of the series on using this template. In this post, we'll look at how to choose and set up Analytics, newsletter, and other providers for your blog.
11 changes: 9 additions & 2 deletions content/posts/test.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Modern Developer Blog Template (Digital Garden Starter)
publishedDate: "2023-05-31"
lastUpdatedDate: "2023-06-25"
lastUpdatedDate: "2023-07-05"
tags:
- "jamstack"
- "technology"
@@ -11,6 +11,9 @@ tags:
- "opinion"
description: As a developer who creates content, I want to have a blog & digital garden where I can share my thoughts and ideas with the world. Now, there's not really a "perfect solution" for this currently. With included analytics, SEO, email subscriptions, modern tooling, simple design, etc. We either have to build one from scratch, use a design template and code the features, or use a CMS/no-code tool.
status: published
series:
order: 1
title: "Using this template"
---

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/thedevdavid/digital-garden)
@@ -148,14 +151,18 @@ Create a PR and add your blog to this list if you're using the template!
- [x] about section on homepage
- [x] search & command bar
- [x] Analytics: Vercel, Umami
- [x] Post series
- [x] Not found page
- [ ] Docs rework
- [ ] hero title and subtitle text HTML support(?)
- [ ] Social sharing buttons
- [ ] Other analytics providers (fathom, simplelytics, plausible, etc)
- [ ] Design improvements (whitespace, layout, etc.)
- [ ] 404, error, and loading pages
- [ ] Code preview component
- [ ] Code highlight improvements (copy code, theme)
- [ ] `manifest.json`
- [ ] newsletter integration (form, api route, keys, welcome page, previous issues)
- [ ] Post series
- [ ] Hidden content (behind email subscription)
- [ ] 100 lighthouse score
- [ ] Command bar fuzzy search in content
16 changes: 16 additions & 0 deletions content/posts/what-is-this.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: "What is this starter?"
publishedDate: "2023-07-05"
lastUpdatedDate: "2023-07-05"
tags:
- "starter"
- "development"
- "docs"
description: "The motivation and goals of this starter template. What it is and what it is not."
status: published
series:
order: 0
title: "Using this template"
---

This post will be the first part of a series of posts that will explain how to use this starter template. This post will explain what this starter template is and what it is not.
5 changes: 5 additions & 0 deletions lib/content-definitions/post.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import { defineDocumentType } from "contentlayer/source-files";
import GithubSlugger from "github-slugger";

import { calculateReadingTime } from "../utils";
import { Series } from "./series";

export const Post = defineDocumentType(() => ({
name: "Post",
@@ -27,6 +28,10 @@ export const Post = defineDocumentType(() => ({
type: "list",
of: { type: "string" },
},
series: {
type: "nested",
of: Series,
},
status: {
type: "enum",
options: ["draft", "published"],
15 changes: 15 additions & 0 deletions lib/content-definitions/series.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineNestedType } from "contentlayer/source-files";

export const Series = defineNestedType(() => ({
name: "Series",
fields: {
title: {
type: "string",
required: true,
},
order: {
type: "number",
required: true,
},
},
}));
12 changes: 12 additions & 0 deletions types/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LinkProps } from "next/link";
import { Post, Series } from "@/.contentlayer/generated";

export interface PostHeading {
heading: number;
@@ -36,3 +37,14 @@ export type SiteMetaData = {
newsletterUrl: string;
analyticsProvider: AnalyticsProvider;
};

export type SeriesItem = {
title: string;
slug: Post["slug"];
status: Post["status"];
isCurrent: boolean;
};

export type PostSeries = Series & { posts: SeriesItem[] };

export type PostWithSeries = Omit<Post, "series"> & { series: PostSeries };