1
+ import '@/styles/mdx.css'
2
+
1
3
import PageTitle from '@/components/page-title'
2
4
import { sortedBlogPost , coreContent } from '../../../lib/contentlayer'
3
- import '@/styles/mdx.css'
4
5
import { allBlogs , allAuthors } from 'contentlayer/generated'
5
6
import type { Blog } from 'contentlayer/generated'
6
7
import { Mdx } from '@/components/mdx/mdx'
@@ -11,32 +12,63 @@ import Tag from '@/components/tag'
11
12
import { notFound } from 'next/navigation'
12
13
import Comments from '@/components/comments'
13
14
import ScrollTopAndComment from '@/components/floating-buttons'
15
+ import { formatDate } from '@/lib/utils'
14
16
15
- const editUrl = ( path ) => `${ siteMetadata . siteRepo } /blob/master/data/${ path } `
16
- const discussUrl = ( path ) =>
17
+ const editUrl = ( path : string ) => `${ siteMetadata . siteRepo } /blob/master/data/${ path } `
18
+ const discussUrl = ( path : string ) =>
17
19
`https://twitter.com/intent/tweet?text=${ encodeURIComponent ( `${ siteMetadata . siteUrl } /${ path } ` ) } `
18
20
19
- const postDateTemplate : Intl . DateTimeFormatOptions = {
20
- weekday : 'long' ,
21
- year : 'numeric' ,
22
- month : 'long' ,
23
- day : 'numeric' ,
21
+ interface BlogPostPageProps {
22
+ params : {
23
+ slug : string [ ]
24
+ }
24
25
}
25
26
26
- export default function BlogPostPage ( { params } ) {
27
- const slug = ( params . slug as string [ ] ) . join ( '/' )
27
+ interface PostPageDetails {
28
+ post : Blog
29
+ prev : ReturnType < typeof coreContent > | null
30
+ next : ReturnType < typeof coreContent > | null
31
+ authorDetails : ( ReturnType < typeof coreContent > | null ) [ ]
32
+ }
33
+
34
+ async function getPostFromParams (
35
+ params : BlogPostPageProps [ 'params' ]
36
+ ) : Promise < PostPageDetails | null > {
37
+ const slug = params . slug . join ( '/' )
28
38
const sortedPosts = sortedBlogPost ( allBlogs ) as Blog [ ]
29
39
const postIndex = sortedPosts . findIndex ( ( p ) => p . slug === slug )
40
+ if ( postIndex === - 1 ) return null
41
+ const post = sortedPosts [ postIndex ]
30
42
const prevContent = sortedPosts [ postIndex + 1 ] || null
31
43
const prev = ( prevContent && ( prevContent ?. draft ? null : coreContent ( prevContent ) ) ) || null
32
44
const nextContent = sortedPosts [ postIndex - 1 ] || null
33
45
const next = ( nextContent && ( nextContent ?. draft ? null : coreContent ( nextContent ) ) ) || null
34
- const post = sortedPosts . find ( ( p ) => p . slug === slug )
46
+
35
47
const authorList = post . authors || [ 'default' ]
36
48
const authorDetails = authorList . map ( ( author ) => {
37
49
const authorResults = allAuthors . find ( ( p ) => p . slug === author )
50
+ if ( ! authorResults ) return null
38
51
return coreContent ( authorResults )
39
52
} )
53
+
54
+ return {
55
+ post,
56
+ prev,
57
+ next,
58
+ authorDetails,
59
+ }
60
+ }
61
+
62
+ export async function generateStaticParams ( ) : Promise < BlogPostPageProps [ 'params' ] [ ] > {
63
+ return allBlogs . map ( ( post ) => ( {
64
+ slug : post . slugAsParams . split ( '/' ) ,
65
+ } ) )
66
+ }
67
+
68
+ export default async function BlogPostPage ( { params } : BlogPostPageProps ) {
69
+ const pageDetails = await getPostFromParams ( params )
70
+ if ( ! pageDetails ) return notFound ( )
71
+ const { post, prev, next, authorDetails } = pageDetails
40
72
const { filePath, path, date, title, tags } = post
41
73
42
74
if ( ! post ) {
@@ -53,9 +85,7 @@ export default function BlogPostPage({ params }) {
53
85
< div >
54
86
< dt className = "sr-only" > Published on</ dt >
55
87
< dd className = "text-base font-medium leading-6 text-gray-500 dark:text-gray-400" >
56
- < time dateTime = { date } >
57
- { new Date ( date ) . toLocaleDateString ( siteMetadata . locale , postDateTemplate ) }
58
- </ time >
88
+ < time dateTime = { date } > { formatDate ( date ) } </ time >
59
89
</ dd >
60
90
</ div >
61
91
</ dl >
@@ -69,34 +99,36 @@ export default function BlogPostPage({ params }) {
69
99
< dt className = "sr-only" > Authors</ dt >
70
100
< dd >
71
101
< ul className = "flex flex-wrap justify-center gap-4 sm:space-x-12 xl:block xl:space-x-0 xl:space-y-8" >
72
- { authorDetails . map ( ( author ) => (
73
- < li className = "flex items-center space-x-2" key = { author . name } >
74
- { author . avatar && (
75
- < Image
76
- src = { author . avatar }
77
- width = { 38 }
78
- height = { 38 }
79
- alt = "avatar"
80
- className = "h-10 w-10 rounded-full"
81
- />
82
- ) }
83
- < dl className = "whitespace-nowrap text-sm font-medium leading-5" >
84
- < dt className = "sr-only" > Name</ dt >
85
- < dd className = "text-gray-900 dark:text-gray-100" > { author . name } </ dd >
86
- < dt className = "sr-only" > Twitter</ dt >
87
- < dd >
88
- { author . twitter && (
89
- < Link
90
- href = { author . twitter }
91
- className = "text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
92
- >
93
- { author . twitter . replace ( 'https://twitter.com/' , '@' ) }
94
- </ Link >
95
- ) }
96
- </ dd >
97
- </ dl >
98
- </ li >
99
- ) ) }
102
+ { authorDetails . map ( ( author ) =>
103
+ ! author ? null : (
104
+ < li className = "flex items-center space-x-2" key = { author . name } >
105
+ { author . avatar && (
106
+ < Image
107
+ src = { author . avatar }
108
+ width = { 38 }
109
+ height = { 38 }
110
+ alt = "avatar"
111
+ className = "h-10 w-10 rounded-full"
112
+ />
113
+ ) }
114
+ < dl className = "whitespace-nowrap text-sm font-medium leading-5" >
115
+ < dt className = "sr-only" > Name</ dt >
116
+ < dd className = "text-gray-900 dark:text-gray-100" > { author . name } </ dd >
117
+ < dt className = "sr-only" > Twitter</ dt >
118
+ < dd >
119
+ { author . twitter && (
120
+ < Link
121
+ href = { author . twitter }
122
+ className = "text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
123
+ >
124
+ { author . twitter . replace ( 'https://twitter.com/' , '@' ) }
125
+ </ Link >
126
+ ) }
127
+ </ dd >
128
+ </ dl >
129
+ </ li >
130
+ )
131
+ ) }
100
132
</ ul >
101
133
</ dd >
102
134
</ dl >
0 commit comments