Skip to content
This repository was archived by the owner on Feb 27, 2024. It is now read-only.

Commit 7f3eed1

Browse files
authored
Merge pull request #44 from WebDevStudios/feature/33-archives-post-page
Feature/33 archives post page
2 parents 7673059 + 21e692f commit 7f3eed1

File tree

10 files changed

+235
-38
lines changed

10 files changed

+235
-38
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {initializeApollo} from '../connector'
2+
import queryPostsArchive from '../posts/queryPostsArchive'
3+
import {postTypes} from './postTypes'
4+
5+
/**
6+
* Retrieve post archive.
7+
*
8+
* @author WebDevStudios
9+
* @param {string} postType WP post type.
10+
* @param {string} orderBy Order by: field.
11+
* @param {string} order Order by: direction.
12+
* @param {string} cursor Start/end cursor for pagination.
13+
* @param {bool} getNext Whether to retrieve next set of posts (true) or previous set (false).
14+
* @param {number} perPage Number of posts per page.
15+
* @return {Object} Object containing Apollo client instance and post archive data or error object.
16+
*/
17+
export default async function getPostTypeArchive(
18+
postType,
19+
orderBy = 'DATE',
20+
order = 'DESC',
21+
cursor = null,
22+
getNext = true,
23+
perPage = 10
24+
) {
25+
// Define single post query based on post type.
26+
const postTypeQuery = {
27+
post: queryPostsArchive
28+
}
29+
30+
// Retrieve post type query.
31+
const query = postTypeQuery?.[postType] ?? null
32+
33+
// Get/create Apollo instance.
34+
const apolloClient = initializeApollo()
35+
36+
// Set up return object.
37+
const response = {
38+
apolloClient,
39+
error: false,
40+
errorMessage: null
41+
}
42+
43+
// If no query is set for given post type, return error message.
44+
if (!query) {
45+
return {
46+
apolloClient,
47+
error: true,
48+
errorMessage: `Post type \`${postType}\` archives are not supported.`
49+
}
50+
}
51+
52+
// Determine query variables.
53+
const variables = {
54+
first: getNext ? perPage : null, // Only used for retrieving next set.
55+
last: getNext ? null : perPage, // Only used for retrieving previous set.
56+
after: getNext ? cursor : null, // Only used for retrieving next set.
57+
before: getNext ? null : cursor, // Only used for retrieving previous set.
58+
orderBy,
59+
order
60+
}
61+
62+
// Execute query.
63+
response.posts = await apolloClient
64+
.query({query, variables})
65+
.then((posts) => {
66+
const pluralType = postTypes[postType] ?? postType
67+
68+
// Set error props if data not found.
69+
if (!posts?.data?.[pluralType]?.edges) {
70+
response.error = true
71+
response.errorMessage = `An error occurred while trying to retrieve data for ${pluralType} archive.`
72+
73+
return null
74+
}
75+
76+
// Flatten posts array to include inner node post data.
77+
return posts.data[pluralType].edges.map((post) => post.node)
78+
})
79+
.catch((error) => {
80+
response.error = true
81+
response.errorMessage = error.message
82+
83+
return null
84+
})
85+
86+
return response
87+
}

api/wordpress/_global/getPostTypeById.js

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,39 @@ export default async function getPostTypeById(postType, id, idType = 'SLUG') {
2929
// Retrieve post type query.
3030
const query = postTypeQuery?.[postType] ?? null
3131

32+
// Get/create Apollo instance.
33+
const apolloClient = initializeApollo()
34+
35+
// Set up return object.
36+
const response = {
37+
apolloClient,
38+
error: false,
39+
errorMessage: null
40+
}
41+
3242
// If no query is set for given post type, return error message.
3343
if (!query) {
3444
return {
35-
isError: true,
36-
message: `Post type \`${postType}\` is not supported.`
45+
apolloClient,
46+
error: true,
47+
errorMessage: `Post type \`${postType}\` is not supported.`
3748
}
3849
}
3950

40-
// Get/create Apollo instance.
41-
const apolloClient = initializeApollo()
42-
4351
// Execute query.
44-
const post = await apolloClient
52+
response.post = await apolloClient
4553
.query({query, variables: {id, idType}})
46-
.then((post) => post?.data?.[postType] ?? null)
54+
.then((post) => {
55+
// Set error props if data not found.
56+
if (!post?.data?.[postType]) {
57+
response.error = true
58+
response.errorMessage = `An error occurred while trying to retrieve data for ${postType} "${id}."`
59+
60+
return null
61+
}
62+
63+
return post.data[postType]
64+
})
4765
.then(async (post) => {
4866
// Handle blocks.
4967
if (!post || !post?.blocksJSON) {
@@ -59,21 +77,11 @@ export default async function getPostTypeById(postType, id, idType = 'SLUG') {
5977
return newPost
6078
})
6179
.catch((error) => {
62-
return {
63-
isError: true,
64-
message: error.message
65-
}
66-
})
80+
response.error = true
81+
response.errorMessage = error.message
6782

68-
if (!post) {
69-
return {
70-
isError: true,
71-
message: `An error occurred while trying to retrieve data for ${postType} "${id}."`
72-
}
73-
}
83+
return null
84+
})
7485

75-
return {
76-
apolloClient,
77-
post
78-
}
86+
return response
7987
}

api/wordpress/_global/getPostTypeStaticPaths.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ export default async function getPostTypeStaticPaths(postType) {
2727
const query = gql`
2828
query GET_SLUGS {
2929
${pluralName}(first: 10000) {
30-
nodes {
31-
${pathField}
30+
edges {
31+
node {
32+
${pathField}
33+
}
3234
}
3335
}
3436
}
@@ -41,11 +43,11 @@ export default async function getPostTypeStaticPaths(postType) {
4143
const posts = await apolloClient.query({query})
4244

4345
// Process paths.
44-
const paths = !posts?.data?.[pluralName]?.nodes
46+
const paths = !posts?.data?.[pluralName]?.edges
4547
? []
46-
: posts.data[pluralName].nodes.map((post) => {
48+
: posts.data[pluralName].edges.map((post) => {
4749
// Trim leading and trailing slashes then split into array on inner slashes.
48-
const slug = post[pathField].replace(/^\/|\/$/g, '').split('/')
50+
const slug = post.node[pathField].replace(/^\/|\/$/g, '').split('/')
4951

5052
return {
5153
params: {
@@ -56,6 +58,6 @@ export default async function getPostTypeStaticPaths(postType) {
5658

5759
return {
5860
paths,
59-
fallback: false
61+
fallback: 'blocking'
6062
}
6163
}

api/wordpress/_global/getPostTypeStaticProps.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import getPostTypeById from './getPostTypeById'
22
import {addApolloState} from '../connector'
3+
import getPostTypeArchive from './getPostTypeArchive'
34

45
/**
56
* Retrieve static props by post type.
@@ -16,16 +17,39 @@ export default async function getPostTypeStaticProps(
1617
// preview = false, // TODO - add preview handling.
1718
// previewData = null
1819
) {
20+
// Check for dynamic archive display.
21+
if (!Object.keys(params).length) {
22+
const {apolloClient, posts, error, errorMessage} = await getPostTypeArchive(
23+
postType
24+
)
25+
26+
// Merge in query results as Apollo state.
27+
return addApolloState(apolloClient, {
28+
props: {
29+
posts,
30+
error,
31+
errorMessage,
32+
archive: true
33+
},
34+
revalidate: 60 * 5
35+
})
36+
}
37+
1938
// Handle catch-all routes.
2039
const slug = Array.isArray(params.slug) ? params.slug.join('/') : params.slug
2140

2241
// Retrieve post data.
23-
const {apolloClient, post} = await getPostTypeById(postType, slug)
42+
const {apolloClient, post, error, errorMessage} = await getPostTypeById(
43+
postType,
44+
slug
45+
)
2446

2547
// Merge in query results as Apollo state.
2648
return addApolloState(apolloClient, {
2749
props: {
28-
post
50+
post,
51+
error,
52+
errorMessage
2953
},
3054
revalidate: 60 * 5
3155
})

api/wordpress/_partials/globalPostFields.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const globalPostFields = `
33
databaseId
44
date
55
slug
6+
uri
67
title
78
`
89
export default globalPostFields

api/wordpress/pages/queryPageById.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const singlePageFragment = gql`
1919

2020
// Query: retrieve page by specified identifier.
2121
const queryPageById = gql`
22-
query GET_PAGE_BY_SLUG(
22+
query GET_PAGE_BY_ID(
2323
$id: ID!
2424
$idType: PageIdType = URI
2525
$imageSize: MediaItemSizeEnum = LARGE

api/wordpress/posts/queryPostById.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import categoriesPostFields from '../_partials/categoriesPostFields'
77

88
const {gql} = require('@apollo/client')
99

10-
// Fragment: retrieve single page fields.
10+
// Fragment: retrieve single post fields.
1111
const singlePostFragment = gql`
1212
fragment SinglePostFields on Post {
1313
${globalPostFields}
@@ -22,7 +22,7 @@ const singlePostFragment = gql`
2222
`
2323
// Query: retrieve post by specified identifier.
2424
const queryPostById = gql`
25-
query GET_POST_BY_SLUG(
25+
query GET_POST_BY_ID(
2626
$id: ID!
2727
$idType: PostIdType = SLUG
2828
$imageSize: MediaItemSizeEnum = LARGE
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {gql} from '@apollo/client'
2+
import globalPostFields from '../_partials/globalPostFields'
3+
import featuredImagePostFields from '../_partials/featuredImagePostFields'
4+
5+
// Fragment: retrieve archive post fields.
6+
const archivePostFragment = gql`
7+
fragment ArchivePostFields on Post {
8+
${globalPostFields}
9+
excerpt
10+
${featuredImagePostFields}
11+
}
12+
`
13+
14+
// Query: retrieve posts archive.
15+
const queryPostsArchive = gql`
16+
query GET_POSTS_ARCHIVE(
17+
$first: Int
18+
$last: Int
19+
$after: String
20+
$before: String
21+
$orderBy: PostObjectsConnectionOrderbyEnum = DATE
22+
$order: OrderEnum = DESC
23+
$imageSize: MediaItemSizeEnum = THUMBNAIL
24+
) {
25+
posts(
26+
first: $first
27+
last: $last
28+
after: $after
29+
before: $before
30+
where: {orderby: {field: $orderBy, order: $order}}
31+
) {
32+
edges {
33+
node {
34+
...ArchivePostFields
35+
}
36+
}
37+
}
38+
}
39+
${archivePostFragment}
40+
`
41+
42+
export default queryPostsArchive

pages/_app.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ export default function App({Component, pageProps}) {
1313
const apolloClient = useApollo(pageProps)
1414

1515
// Check for errors.
16-
const isError = pageProps?.post?.isError
17-
let errorMessage = pageProps?.post?.message ?? 'An unknown error occurred.'
16+
const error = pageProps?.error
17+
let errorMessage = pageProps?.errorMessage ?? 'An unknown error occurred.'
1818
// Trim trailing period - added via Error component.
1919
errorMessage = errorMessage.replace(/\.$/g, '')
2020

2121
return (
2222
<ApolloProvider client={apolloClient}>
23-
{isError ? (
23+
{error ? (
2424
<Error statusCode={500} title={errorMessage} />
2525
) : (
2626
<Component {...pageProps} />

pages/posts/[[...slug]].js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Info} from '@/components/molecules/Alert'
33
import PropTypes from 'prop-types'
44
import getPostTypeStaticPaths from '@/api/wordpress/_global/getPostTypeStaticPaths'
55
import getPostTypeStaticProps from '@/api/wordpress/_global/getPostTypeStaticProps'
6+
import Link from 'next/link'
67

78
// Define route post type.
89
const postType = 'post'
@@ -14,7 +15,37 @@ const postType = 'post'
1415
* @param {Object} [props] Properties passed to the component.
1516
* @return {Element} Element to render.
1617
*/
17-
export default function BlogPost({post}) {
18+
export default function BlogPost({post, posts, archive}) {
19+
// Check for post archive.
20+
// TODO create generic archive component and move this check to `_app.js`.
21+
if (archive) {
22+
return (
23+
<Layout title="Blog">
24+
<div className="container">
25+
<section>
26+
{!posts || !posts.length ? (
27+
<p>No posts found.</p>
28+
) : (
29+
posts.map((post, index) => (
30+
<>
31+
<article key={index}>
32+
<Link href={post.uri}>
33+
<a>
34+
<h1 dangerouslySetInnerHTML={{__html: post?.title}} />
35+
</a>
36+
</Link>
37+
<div dangerouslySetInnerHTML={{__html: post?.excerpt}} />
38+
</article>
39+
<hr />
40+
</>
41+
))
42+
)}
43+
</section>
44+
</div>
45+
</Layout>
46+
)
47+
}
48+
1849
return (
1950
<Layout title={post?.title} description={post?.excerpt}>
2051
<div className="container">
@@ -64,5 +95,7 @@ export async function getStaticProps({params}) {
6495
}
6596

6697
BlogPost.propTypes = {
67-
post: PropTypes.object
98+
post: PropTypes.object,
99+
posts: PropTypes.array,
100+
archive: PropTypes.bool
68101
}

0 commit comments

Comments
 (0)