Skip to content

Commit

Permalink
Update to app directory + new design. (leerob#564)
Browse files Browse the repository at this point in the history
  • Loading branch information
leerob authored Jan 29, 2023
1 parent 5274ae0 commit e430782
Show file tree
Hide file tree
Showing 301 changed files with 9,586 additions and 9,536 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IS_TEMPLATE=true
33 changes: 9 additions & 24 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,29 +1,14 @@
// For retrieving Unsplash metrics
UNSPLASH_ACCESS_KEY=
# For blog views and guestbook
DATABASE_URL="mysql://..."

// For retrieving YouTube metrics
GOOGLE_PRIVATE_KEY=
GOOGLE_CLIENT_EMAIL=

// For showing now playing song from Spotify
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
SPOTIFY_REFRESH_TOKEN=

// For NextAuth.js and GitHub OAuth
# For /guestbook
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=
OAUTH_CLIENT_KEY=
OAUTH_CLIENT_SECRET=
NEXTAUTH_URL=http://localhost:3000

// For retrieving Tweets
TWITTER_API_KEY=

// For post views and guestbook
DATABASE_URL=
# For tweets in /blog
TWITTER_API_TOKEN=

// For Sanity CMS
NEXT_PUBLIC_SANITY_PROJECT_ID=
NEXT_PUBLIC_SANITY_DATASET=
SANITY_API_TOKEN=
SANITY_PREVIEW_SECRET=
SANITY_STUDIO_REVALIDATE_SECRET=
# For redirects
EDGE_CONFIG=
9 changes: 0 additions & 9 deletions .eslintrc

This file was deleted.

43 changes: 38 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
*.log
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
.next
node_modules
*.pem
.vscode

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel
.env
.sanity

# typescript
*.tsbuildinfo
next-env.d.ts

# contentlayer
.contentlayer
2 changes: 0 additions & 2 deletions .npmrc

This file was deleted.

2 changes: 0 additions & 2 deletions .prettierignore

This file was deleted.

41 changes: 15 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fleerob%2Fleerob.io&env=NEXT_PUBLIC_SANITY_PROJECT_ID,NEXT_PUBLIC_SANITY_DATASET,SANITY_API_TOKEN,SANITY_PREVIEW_SECRET,SANITY_STUDIO_REVALIDATE_SECRET&envDescription=These%20values%20are%20needed%20to%20connect%20to%20Sanity%20and%20fetch%20content%20for%20blog%20posts.)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fleerob%2Fleerob.io)

# leerob.io

- **Framework**: [Next.js](https://nextjs.org/)
- **Database**: [PlanetScale](https://planetscale.com)
- **ORM**: [Prisma](https://prisma.io/)
- **Authentication**: [NextAuth.js](https://next-auth.js.org/)
- **Authentication**: [NextAuth.js](https://next-auth.js.org)
- **Deployment**: [Vercel](https://vercel.com)
- **CMS**: [Sanity](https://www.sanity.io/)
- **Styling**: [Tailwind CSS](https://tailwindcss.com/)
- **Styling**: [Tailwind CSS](https://tailwindcss.com)
- **Analytics**: [Vercel Analytics](https://vercel.com/analytics)

## Learn More
## TODO

I've recorded two live streams walking through this repository and answering questions.
In early 2023, I refactored my site to use the new `app/` directory in Next.js 13. I went ahead and shipped it, but there are still a few things I want to do:

- [Stream #2 – Nov 10, 2021 (1h 4min)](https://www.youtube.com/watch?v=WZZFW5xDjJ4)
- [Browse repository](https://github.com/leerob/leerob.io/tree/747479118497d31433cb78ced5c1628ed5d1583b) at this point in time.
- [Stream #1 – Jan 27, 2021 (1h 11min)](https://www.youtube.com/watch?v=xXQsF0q8KUg)
- [Browse repository](https://github.com/leerob/leerob.io/tree/568df6d056a4f7ea6f10fab07786c8ec6cbbddde) at this point in time.

## Overview

- `layouts/*` - The different page layouts each MDX category (blog, snippets) uses.
- `lib/*` - Short for "library", a collection of helpful utilities or code for external services.
- `pages/api/*` - [API Routes](https://nextjs.org/docs/api-routes/introduction) powering [`/dashboard`](https://leerob.io/dashboard), newsletter subscription, guestbook, and post views.
- `pages/blog/*` - Static pre-rendered blog pages using MDX.
- `pages/dashboard` - [Personal dashboard](https://leerob.io/dashboard) tracking metrics.
- `pages/sitemap.xml.tsx` - Automatically generated sitemap.
- `pages/feed.xml.tsx` - Automatically generated RSS feed.
- `pages/*` - All other static pages.
- `prisma/*` - My Prisma schema, which uses a PlanetScale MySQL database.
- `public/*` - Static assets including fonts and images.
- `styles/*` - A small amount of global styles. I'm mostly using vanilla Tailwind CSS.
- [ ] Improved sitemap and `robots.txt` support coming soon
- [ ] Global `404` page coming soon
- [ ] Refactor to improved SEO support (waiting on `generateMetadata`)
- [ ] Move redirects to end of routing stack (not in `next.config.js`)
- [ ] Use new support for API routes in `app` (not ready yet)
- [ ] Improved scroll position support in `app/` (not implemented yet)
- [ ] Remove `@vercel/analytics` wrapping component (waiting on new version)

## Running Locally

Expand All @@ -41,11 +29,12 @@ This application requires Node.js v16.13+.
git clone https://github.com/leerob/leerob.io.git
cd leerob.io
pnpm install
pnpm run setup # Remove all of my personal information
pnpm dev
```

Create a `.env` file similar to [`.env.example`](https://github.com/leerob/leerob.io/blob/main/.env.example).

## Cloning / Forking

Please review the [license](https://github.com/leerob/leerob.io/blob/main/LICENSE.txt) and remove all of my personal information (resume, blog posts, images, etc.).
Please review the [license](https://github.com/leerob/leerob.io/blob/main/LICENSE.txt) and remove all of my personal information (resume, blog posts, images, etc.) by running `pnpm run setup`.
87 changes: 87 additions & 0 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
GitHubIcon,
YoutubeIcon,
ArrowIcon,
TwitterIcon,
} from 'components/icons';

export const metadata = {
title: 'About',
description: 'VP of Developer Experience at Vercel.',
};

export default function AboutPage() {
return (
<section>
<h1 className="font-bold text-3xl font-serif">About Me</h1>
<p className="my-5 text-neutral-800 dark:text-neutral-200">
Hey, I'm Lee. Most folks know me as <b>leerob</b> online.
</p>
<div className="prose prose-neutral dark:prose-invert text-neutral-800 dark:text-neutral-200">
<p>
I'm currently the <b>VP of Developer Experience at Vercel</b>, where I
lead our Developer Relations and Documentation teams. I focus on{' '}
<b>educating and growing</b> the Vercel and Next.js communities.
</p>
<hr />
<p>
I'm passionate about many creative pursuits, including music,
photography, videography, and of course, coding. This combination of
interests is what ultimately led me to my current role in building
developer communities.
</p>
<p>
I <b>love</b> building for the web. From something as simple as a
single HTML file – all the way to large Next.js applications. The web
is incredible. Anyone can become a developer, writer, or creator – and
no one has to ask for permission. You can just build.
</p>
<p className="mb-8">
Outside of Vercel, I <b>angel invest</b> in developer tools companies
and <b>advise early-stage startups</b>. I also do Developer Relations
consulting work, helping companies take their DevRel function from 0
to 1, or provide guidance on growing communities, content creation,
and developer marketing.
</p>
<div className="flex flex-col gap-2 md:flex-row md:gap-2">
<a
rel="noopener noreferrer"
target="_blank"
href="https://twitter.com/leeerob"
className="flex w-full border border-neutral-200 dark:border-neutral-800 rounded-lg p-4 no-underline items-center text-neutral-800 dark:text-neutral-200 hover:dark:bg-neutral-900 hover:bg-neutral-100 transition-all justify-between"
>
<div className="flex items-center">
<TwitterIcon />
<div className="ml-3">Twitter</div>
</div>
<ArrowIcon />
</a>
<a
rel="noopener noreferrer"
target="_blank"
href="https://github.com/leerob"
className="flex w-full border border-neutral-200 dark:border-neutral-800 rounded-lg p-4 no-underline items-center text-neutral-800 dark:text-neutral-200 hover:dark:bg-neutral-900 hover:bg-neutral-100 transition-all justify-between"
>
<div className="flex items-center">
<GitHubIcon />
<div className="ml-3">GitHub</div>
</div>
<ArrowIcon />
</a>
<a
rel="noopener noreferrer"
target="_blank"
href="https://www.youtube.com/@leerob"
className="flex w-full border border-neutral-200 dark:border-neutral-800 rounded-lg p-4 no-underline items-center text-neutral-800 dark:text-neutral-200 hover:dark:bg-neutral-900 hover:bg-neutral-100 transition-all justify-between"
>
<div className="flex items-center">
<YoutubeIcon />
<div className="ml-3">YouTube</div>
</div>
<ArrowIcon />
</a>
</div>
</div>
</section>
);
}
Binary file added app/avatar.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions app/blog/[slug]/head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { allBlogs } from 'contentlayer/generated';

export default async function Head({ params }) {
const post = allBlogs.find((post) => post.slug === params.slug) || {
title: 'Not Found',
summary: 'This page could not be found.',
image: 'https://leerob.io/api/og?title=Not%20Found',
publishedAt: new Date().toISOString(),
};

const ogImage = post.image
? post.image
: `https://leerob.io/api/og?title=${post.title}`;

return (
<>
<title>{`${post.title} – Lee Robinson`}</title>
<meta content={post.summary} name="description" />
<meta
property="og:url"
content={`https://leerob.io/blog/${params.slug}`}
/>
<link rel="canonical" href={`https://leerob.io/blog/${params.slug}`} />
<meta property="og:description" content={post.summary} />
<meta property="og:title" content={post.title} />
<meta property="og:image" content={ogImage} />
<meta name="twitter:title" content={post.title} />
<meta name="twitter:description" content={post.summary} />
<meta name="twitter:image" content={ogImage} />
<meta property="article:published_time" content={post.publishedAt} />
</>
);
}
36 changes: 36 additions & 0 deletions app/blog/[slug]/metadata.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { allBlogs } from 'contentlayer/generated';

// Currently, params isn't correctly passted to `generateMetadata`.
// Once that's fixed, I can remove `head.tsx`.
export async function generateMetadata({ params }) {
const post = allBlogs.find((post) => post.slug === params.slug);
const {
title: postTitle,
publishedAt: publishedTime,
summary: description,
image,
slug,
} = post;
const title = `${postTitle} - Lee Robinson`;
const ogImage = image ? image : `https://leerob.io/api/og?title=${title}`;

return {
title,
description,
openGraph: {
title,
description,
type: 'article',
publishedTime,
url: `https://leerob.io/blog/${slug}`,
images: [
{
url: ogImage,
},
],
},
twitter: {
title,
},
};
}
40 changes: 40 additions & 0 deletions app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { notFound } from 'next/navigation';
import { getViews } from 'lib/planetscale';
import { Mdx } from 'components/mdx';
import { allBlogs } from 'contentlayer/generated';
import { getTweets } from 'lib/twitter';
import Balancer from 'react-wrap-balancer';
import ViewCounter from './view-counter';

export async function generateStaticParams() {
return allBlogs.map((post) => ({
slug: post.slug,
}));
}

export default async function Blog({ params }) {
const post = allBlogs.find((post) => post.slug === params.slug);

if (!post) {
notFound();
}

const views = await getViews(post.slug);
const tweets = await getTweets(post.tweetIds);

return (
<section>
<h1 className="font-bold text-3xl font-serif max-w-[650px]">
<Balancer>{post.title}</Balancer>
</h1>
<div className="grid grid-cols-[auto_1fr_auto] items-center mt-4 mb-8 font-mono text-sm max-w-[650px]">
<div className="bg-neutral-100 dark:bg-neutral-800 rounded-md px-2 py-1 tracking-tighter">
{post.publishedAt}
</div>
<div className="h-[0.2em] bg-neutral-50 dark:bg-neutral-800 mx-2" />
<ViewCounter slug={post.slug} views={views} />
</div>
<Mdx code={post.body.code} tweets={tweets} />
</section>
);
}
Loading

0 comments on commit e430782

Please sign in to comment.