From f32800cbb977f02723125d67a3a10e155ded0352 Mon Sep 17 00:00:00 2001 From: hamidra Date: Wed, 17 Jul 2024 17:18:35 -0700 Subject: [PATCH] add hero image and author + enhance style --- next.config.mjs | 2 +- package.json | 1 + pnpm-lock.yaml | 25 +++++++++++++++++++ src/app/blog/[slug]/error.tsx | 18 ++++++++++++++ src/app/blog/[slug]/page.tsx | 45 +++++++++++++++++++++++++++++------ src/app/blog/page.tsx | 9 ++----- src/components/post-card.tsx | 8 +++---- src/config/site.ts | 2 ++ src/lib/posts.ts | 38 +++++++++++++++++++---------- src/lib/utils.ts | 4 ++++ 10 files changed, 120 insertions(+), 32 deletions(-) create mode 100644 src/app/blog/[slug]/error.tsx diff --git a/next.config.mjs b/next.config.mjs index 048ea47..b58c925 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -4,7 +4,7 @@ const nextConfig = { remotePatterns: [ { protocol: 'https', - hostname: 'placehold.co', + hostname: '**', port: '', pathname: '**', }, diff --git a/package.json b/package.json index 91ec458..a2a6e06 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "format": "prettier --write ." }, "dependencies": { + "@pondorasti/remark-img-links": "^1.0.8", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf70dbe..72b7b8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@pondorasti/remark-img-links': + specifier: ^1.0.8 + version: 1.0.8 '@radix-ui/react-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.0.0)(@types/react@18.0.0)(react-dom@18.0.0)(react@18.0.0) @@ -329,6 +332,12 @@ packages: requiresBuild: true optional: true + /@pondorasti/remark-img-links@1.0.8: + resolution: {integrity: sha512-UpW5AfimYi7EdkL9qvQ4trzIzgi5KERdd4KHN8fkVWrh2BqCzKuheJbAzSgIxJr0opQ7NvaIwf+JRjV/fBRdrA==} + dependencies: + unist-util-visit: 1.4.0 + dev: false + /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: @@ -4456,6 +4465,10 @@ packages: unist-util-is: 6.0.0 dev: false + /unist-util-is@3.0.0: + resolution: {integrity: sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==} + dev: false + /unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} dependencies: @@ -4481,6 +4494,12 @@ packages: '@types/unist': 3.0.2 dev: false + /unist-util-visit-parents@2.1.2: + resolution: {integrity: sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==} + dependencies: + unist-util-is: 3.0.0 + dev: false + /unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} dependencies: @@ -4488,6 +4507,12 @@ packages: unist-util-is: 6.0.0 dev: false + /unist-util-visit@1.4.0: + resolution: {integrity: sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==} + dependencies: + unist-util-visit-parents: 2.1.2 + dev: false + /unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} dependencies: diff --git a/src/app/blog/[slug]/error.tsx b/src/app/blog/[slug]/error.tsx new file mode 100644 index 0000000..4aaf8e4 --- /dev/null +++ b/src/app/blog/[slug]/error.tsx @@ -0,0 +1,18 @@ +'use client' +import { Button } from '@/components/ui/button' +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string } + reset: () => void +}) { + return ( +
+
+

Something went wrong!

+ +
+
+ ) +} diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 9e91125..c8a2a1a 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -1,31 +1,62 @@ // pages/blog/[slug].js import Image from 'next/image' +import { siteConfig } from '@/config/site' import ReactMarkdown from 'react-markdown' import remarkGFM from 'remark-gfm' import remarkMath from 'remark-math' import rehypeKatex from 'rehype-katex' -import { getPostBySlug, getPostsMeta } from '@/lib/posts' +import imgLink from '@pondorasti/remark-img-links' +import { getPostBySlug, getPostsMeta, resolveImage } from '@/lib/posts' import 'katex/dist/katex.min.css' +import { formatDate } from '@/lib/utils' export default async function BlogTemplate({ params }) { + let imageBase = siteConfig.links.post_images let { slug } = params - let { frontmatter, markdownBody } = await getPostBySlug(slug) + + let post = await getPostBySlug(slug) + if (!post) throw new Error('No post was found!') + + let { frontmatter, markdownBody } = post + let { hero_image, title, author, date } = frontmatter + let resolved_hero_image = await resolveImage(hero_image) return ( -
-
+
+
-

{frontmatter.title}

+
+ {title} +
+
+
+ By: {author} +
+
+ {formatDate(date)} +
+
+
+
+ {`${title}
{markdownBody}
-

Written By: {frontmatter.author}

) diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index ec0bd9d..ba38bd5 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -1,17 +1,12 @@ import { getPostsMeta } from '@/lib/posts' import PostCard from '@/components/post-card' export default async function PostsList() { - let postsMeta = await getPostsMeta() + let postsMeta = await getPostsMeta().catch((err) => console.error(err)) return (
- {postsMeta.map((meta, id) => ( + {postsMeta?.map((meta, id) => ( <> - - - - - ))}
diff --git a/src/components/post-card.tsx b/src/components/post-card.tsx index 6ae6a40..c3e3161 100644 --- a/src/components/post-card.tsx +++ b/src/components/post-card.tsx @@ -2,9 +2,11 @@ import Link from 'next/link' import { Card, CardHeader, CardContent, CardTitle } from './ui/card' import Image from 'next/image' import { cn } from '@/lib/utils' +import { resolveImage } from '@/lib/posts' -export default function PostCard({ meta }) { +export default async function PostCard({ meta }) { let { author, slug, title, description, hero_image } = meta + hero_image = await resolveImage(hero_image) return ( <> @@ -20,9 +22,7 @@ export default function PostCard({ meta }) {
{title}
- { - 'Warning: Each child in a list should have a unique "key" prop.Warning: Each child in a list should have a unique "key" prop.Warning: Each child in a list should have a unique "key" prop' - } + {description}
{`by ${author}`}
diff --git a/src/config/site.ts b/src/config/site.ts index 6c8a81e..52ff315 100644 --- a/src/config/site.ts +++ b/src/config/site.ts @@ -8,6 +8,8 @@ export const siteConfig = { twitter: 'https://twitter.com/tessernet', github: 'https://github.com/tesser-labs', posts: 'https://api.github.com/repos/tesser-labs/blog/contents/posts/', + post_images: + 'https://raw.githubusercontent.com/tesser-labs/blog/main/posts/', }, } diff --git a/src/lib/posts.ts b/src/lib/posts.ts index d348871..757e64c 100644 --- a/src/lib/posts.ts +++ b/src/lib/posts.ts @@ -2,36 +2,48 @@ import matter from 'gray-matter' import { siteConfig } from '@/config/site' export async function getPost(path: string) { - const res = await fetch(path) - const post = await res.text() + const post = await fetch(path).then((res) => res.text()) const data = matter(post) return { - frontmatter: data.data, - markdownBody: data.content, + frontmatter: data?.data, + markdownBody: data?.content, + excerpt: data?.excerpt, } } export async function getPostBySlug(slug: string) { let base = siteConfig.links.posts let url = base && slug && new URL(`${slug}.md`, base).href - let res = await (await fetch(url)).json() - let downloadUrl = res.download_url - return getPost(downloadUrl) + let res = url && (await fetch(url).then((res) => res.json())) + let downloadUrl = res?.download_url + return downloadUrl && getPost(downloadUrl) +} + +export async function resolveImage(path: string) { + let base = siteConfig.links.post_images + try { + // check of path is absolute or empty + let url = path && new URL(`${path}`).href + return url + } catch { + // if path is relatice resolve it against the base url + let url = path && new URL(`${path}`, base).href + return url + } } export async function getPostsUrls(path: string = siteConfig.links.posts) { - const res = await fetch(path) - const posts: Array<{ download_url: string }> = await res.json() - const postUrls = posts - .map((post) => post.download_url) - .filter((post_url) => post_url) + const posts = await fetch(path).then((res) => res.json()) + const postUrls = + posts?.map((post) => post.download_url)?.filter((post_url) => post_url) || + [] return postUrls } export async function getPostsMeta(path: string = siteConfig.links.posts) { let urls = await getPostsUrls(path) - const pendingMeta = urls.map((url) => getPost(url)) + const pendingMeta = urls?.map((url) => getPost(url)) || [] const meta = (await Promise.all(pendingMeta)).map((data) => data.frontmatter) return meta } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d32b0fe..e1fd50f 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -4,3 +4,7 @@ import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +export function formatDate(date: Date) { + return `${date.toDateString().split(' ').slice(1).join(' ')}` +}