CMS service improvements
This commit is contained in:
parent
7b789126d0
commit
f567145e06
|
@ -4,7 +4,7 @@ import React, { useEffect } from 'react';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { Contained } from '../components/Contained';
|
||||
import { RichBody } from '../components/RichBody';
|
||||
import { NAVIGATION } from '../constants';
|
||||
import { CMS, NAVIGATION } from '../constants';
|
||||
import { CmsApi, unslugify } from '../services/cms';
|
||||
import { PageType, setPageType, SideMenuItem } from '../state/navigation';
|
||||
import { ISplitPage } from '../types/cms';
|
||||
|
@ -44,7 +44,7 @@ export async function getStaticProps({ params }) {
|
|||
page,
|
||||
href: `/${href}`,
|
||||
},
|
||||
revalidate: 60,
|
||||
revalidate: CMS.CONTENT_REVALIDATE_RATE,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import Head from 'next/head';
|
|||
import React, { useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Article } from '../../components/article/Article';
|
||||
import { CMS } from '../../constants';
|
||||
import { CmsApi, generateLinkMeta } from '../../services/cms';
|
||||
import { PageType, setPageType, setPostTitle } from '../../state/navigation';
|
||||
import { IPost } from '../../types/cms';
|
||||
|
@ -20,7 +21,7 @@ export async function getStaticPaths() {
|
|||
|
||||
// Contentful only allows 100 at a time
|
||||
while (!foundAllPosts) {
|
||||
const { posts: _posts } = await cms.fetchBlogEntries(100, page);
|
||||
const { entries: _posts } = await cms.fetchBlogEntries(100, page);
|
||||
|
||||
if (_posts.length === 0) {
|
||||
foundAllPosts = true;
|
||||
|
@ -42,7 +43,7 @@ export async function getStaticProps({ params }) {
|
|||
console.log(`Building page: %c${params.slug}`, 'color: purple;');
|
||||
|
||||
const cms = new CmsApi();
|
||||
const post = await cms.fetchBlogBySlug(String(params?.slug) ?? '');
|
||||
const post = await cms.fetchEntryBySlug(String(params?.slug) ?? '', 'post');
|
||||
// embedded links in post body need metadata for preview
|
||||
post.body = await generateLinkMeta(post.body);
|
||||
const url = generateURL(params?.slug ? `/blog/${params?.slug}` : '/blog');
|
||||
|
@ -55,7 +56,7 @@ export async function getStaticProps({ params }) {
|
|||
post,
|
||||
url,
|
||||
},
|
||||
revalidate: 60,
|
||||
revalidate: CMS.CONTENT_REVALIDATE_RATE,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ export const getServerSideProps: GetServerSideProps = async context => {
|
|||
// Fetch posts even when tag, for related etc
|
||||
// Pagination only occurs when tag isnt defined.
|
||||
// If tag is defined, pagination is for tag results
|
||||
const { posts, total: totalPosts } = await cms.fetchBlogEntries(
|
||||
const { entries: posts, total: totalPosts } = await cms.fetchBlogEntries(
|
||||
RESULTS_PER_PAGE,
|
||||
tag ? 1 : page,
|
||||
);
|
||||
|
@ -42,7 +42,7 @@ export const getServerSideProps: GetServerSideProps = async context => {
|
|||
let filteredTotalPosts = totalPosts;
|
||||
if (tag) {
|
||||
const {
|
||||
posts: _tagPosts = [],
|
||||
entries: _tagPosts = [],
|
||||
total: _tagTotalPosts,
|
||||
} = await cms.fetchBlogEntriesByTag(tag ?? '', RESULTS_PER_PAGE, page);
|
||||
tagPosts = _tagPosts;
|
||||
|
@ -50,7 +50,7 @@ export const getServerSideProps: GetServerSideProps = async context => {
|
|||
} else {
|
||||
// Retrieve all blog posts without the `dev-update` tag when not searching by tag
|
||||
const {
|
||||
posts: _tagPosts = [],
|
||||
entries: _tagPosts = [],
|
||||
total: _tagTotalPosts,
|
||||
} = await cms.fetchBlogEntriesWithoutDevUpdates(RESULTS_PER_PAGE, page);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Contained } from '../components/Contained';
|
|||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
const cms = new CmsApi();
|
||||
|
||||
const { faqItems, total } = await cms.fetchFAQItems();
|
||||
const { entries: faqItems, total } = await cms.fetchFAQItems();
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
|
193
services/cms.tsx
193
services/cms.tsx
|
@ -1,5 +1,5 @@
|
|||
import { Document, Block, Inline } from '@contentful/rich-text-types';
|
||||
import { ContentfulClientApi, createClient } from 'contentful';
|
||||
import { ContentfulClientApi, createClient, EntryCollection } from 'contentful';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
import React from 'react';
|
||||
import BittrexSVG from '../assets/svgs/bittrex-logo.svg';
|
||||
|
@ -16,21 +16,14 @@ import {
|
|||
IPost,
|
||||
ISplitPage,
|
||||
IFAQItem,
|
||||
IFetchBlogEntriesReturn,
|
||||
IFetchEntriesReturn,
|
||||
IFetchFAQItemsReturn,
|
||||
} from '../types/cms';
|
||||
import isLive from '../utils/environment';
|
||||
import { generateURL } from '../utils/metadata';
|
||||
import { fetchContent } from './embed';
|
||||
|
||||
interface IFetchBlogEntriesReturn {
|
||||
posts: Array<IPost>;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface IFetchFAQItemsReturn {
|
||||
faqItems: Array<IFAQItem>;
|
||||
total: number;
|
||||
}
|
||||
|
||||
function loadOptions(options: any) {
|
||||
if (isLive()) options['fields.live'] = true;
|
||||
return options;
|
||||
|
@ -55,7 +48,7 @@ export class CmsApi {
|
|||
quantity = CMS.BLOG_RESULTS_PER_PAGE,
|
||||
page = 1,
|
||||
): Promise<IFetchBlogEntriesReturn> {
|
||||
const entries = await this.client.getEntries(
|
||||
const _entries = await this.client.getEntries(
|
||||
loadOptions({
|
||||
content_type: 'post', // only fetch blog post entry
|
||||
order: '-fields.date',
|
||||
|
@ -64,35 +57,11 @@ export class CmsApi {
|
|||
}),
|
||||
);
|
||||
|
||||
if (entries && entries.items && entries.items.length > 0) {
|
||||
const blogPosts = entries.items.map(entry => this.convertPost(entry));
|
||||
return { posts: blogPosts, total: entries.total };
|
||||
}
|
||||
|
||||
return { posts: [], total: 0 } as IFetchBlogEntriesReturn;
|
||||
}
|
||||
|
||||
public async fetchBlogById(id): Promise<IPost> {
|
||||
return this.client.getEntry(id).then(entry => {
|
||||
if (entry) {
|
||||
return this.convertPost(entry);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public async fetchBlogBySlug(slug: string): Promise<IPost> {
|
||||
const entries = await this.client.getEntries({
|
||||
content_type: 'post',
|
||||
'fields.slug': slug,
|
||||
});
|
||||
|
||||
if (entries?.items?.length > 0) {
|
||||
const post = this.convertPost(entries.items[0]);
|
||||
return post;
|
||||
}
|
||||
|
||||
return null;
|
||||
const results = await this.generateEntries(_entries, 'post');
|
||||
return {
|
||||
entries: results.entries as Array<IPost>,
|
||||
total: results.total,
|
||||
};
|
||||
}
|
||||
|
||||
public async fetchBlogEntriesByTag(
|
||||
|
@ -100,7 +69,7 @@ export class CmsApi {
|
|||
quantity = CMS.BLOG_RESULTS_PER_PAGE_TAGGED,
|
||||
page = 1,
|
||||
): Promise<IFetchBlogEntriesReturn> {
|
||||
const entries = await this.client.getEntries(
|
||||
const _entries = await this.client.getEntries(
|
||||
loadOptions({
|
||||
content_type: 'post',
|
||||
order: '-fields.date',
|
||||
|
@ -110,12 +79,11 @@ export class CmsApi {
|
|||
}),
|
||||
);
|
||||
|
||||
if (entries?.items?.length > 0) {
|
||||
const posts = entries.items.map(entry => this.convertPost(entry));
|
||||
return { posts, total: entries.total };
|
||||
}
|
||||
|
||||
return { posts: [], total: 0 } as IFetchBlogEntriesReturn;
|
||||
const results = await this.generateEntries(_entries, 'post');
|
||||
return {
|
||||
entries: results.entries as Array<IPost>,
|
||||
total: results.total,
|
||||
};
|
||||
}
|
||||
|
||||
public async fetchBlogEntriesWithoutDevUpdates(
|
||||
|
@ -123,7 +91,7 @@ export class CmsApi {
|
|||
page = 1,
|
||||
): Promise<IFetchBlogEntriesReturn> {
|
||||
const DEV_UPDATE_TAG = 'dev-update';
|
||||
const entries = await this.client.getEntries(
|
||||
const _entries = await this.client.getEntries(
|
||||
loadOptions({
|
||||
content_type: 'post', // only fetch blog post entry
|
||||
order: '-fields.date',
|
||||
|
@ -133,40 +101,90 @@ export class CmsApi {
|
|||
}),
|
||||
);
|
||||
|
||||
if (entries && entries.items && entries.items.length > 0) {
|
||||
const blogPosts = entries.items.map(entry => this.convertPost(entry));
|
||||
return { posts: blogPosts, total: entries.total };
|
||||
}
|
||||
|
||||
return { posts: [], total: 0 } as IFetchBlogEntriesReturn;
|
||||
const results = await this.generateEntries(_entries, 'post');
|
||||
return {
|
||||
entries: results.entries as Array<IPost>,
|
||||
total: results.total,
|
||||
};
|
||||
}
|
||||
|
||||
public async fetchPageEntries(): Promise<TPages> {
|
||||
try {
|
||||
const entries = await this.client.getEntries({
|
||||
content_type: 'splitPage', // only fetch blog post entry
|
||||
order: 'fields.order',
|
||||
const _entries = await this.client.getEntries(
|
||||
loadOptions({
|
||||
content_type: 'splitPage', // only fetch blog post entry
|
||||
order: 'fields.order',
|
||||
}),
|
||||
);
|
||||
|
||||
const results = await this.generateEntries(_entries, 'splitPage');
|
||||
const pages: TPages = {};
|
||||
|
||||
results.entries.forEach(page => {
|
||||
const pageExists = SideMenuItem[page.id];
|
||||
|
||||
if (pageExists) {
|
||||
pages[page.id] = page;
|
||||
}
|
||||
});
|
||||
|
||||
if (entries && entries.items && entries.items.length > 0) {
|
||||
const pagesArray = entries.items.map(entry => this.convertPage(entry));
|
||||
|
||||
const pages: TPages = {};
|
||||
pagesArray.forEach(page => {
|
||||
const pageExists = SideMenuItem[page.id];
|
||||
|
||||
if (pageExists) {
|
||||
pages[page.id] = page;
|
||||
}
|
||||
});
|
||||
|
||||
return pages;
|
||||
}
|
||||
return pages;
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
public async fetchFAQItems(): Promise<IFetchFAQItemsReturn> {
|
||||
const _entries = await this.client.getEntries({
|
||||
content_type: 'faq_item', // only fetch faq items
|
||||
order: 'fields.id',
|
||||
});
|
||||
|
||||
const results = await this.generateEntries(_entries, 'faq');
|
||||
return {
|
||||
entries: results.entries as Array<IFAQItem>,
|
||||
total: results.total,
|
||||
};
|
||||
}
|
||||
|
||||
public async fetchEntryById(id): Promise<IPost> {
|
||||
return this.client.getEntry(id).then(entry => {
|
||||
if (entry) {
|
||||
return this.convertPost(entry);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public async fetchEntryBySlug(
|
||||
slug: string,
|
||||
entryType: 'post' | 'splitPage',
|
||||
): Promise<any> {
|
||||
const _entries = await this.client.getEntries({
|
||||
content_type: entryType, // only fetch specific type
|
||||
'fields.slug': slug,
|
||||
});
|
||||
|
||||
if (_entries?.items?.length > 0) {
|
||||
let entry;
|
||||
switch (entryType) {
|
||||
case 'post':
|
||||
entry = this.convertPost(_entries.items[0]);
|
||||
break;
|
||||
case 'splitPage':
|
||||
entry = this.convertPage(_entries.items[0]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
return Promise.reject(
|
||||
new Error(`Failed to fetch ${entryType} for ${slug}`),
|
||||
);
|
||||
}
|
||||
|
||||
public async fetchPageById(id: SideMenuItem): Promise<ISplitPage> {
|
||||
return this.client
|
||||
.getEntries({
|
||||
|
@ -181,18 +199,29 @@ export class CmsApi {
|
|||
});
|
||||
}
|
||||
|
||||
public async fetchFAQItems(): Promise<IFetchFAQItemsReturn> {
|
||||
const entries = await this.client.getEntries({
|
||||
content_type: 'faq_item', // only fetch faq items
|
||||
order: 'fields.id',
|
||||
});
|
||||
|
||||
public async generateEntries(
|
||||
entries: EntryCollection<unknown>,
|
||||
entryType: 'post' | 'faq' | 'splitPage',
|
||||
): Promise<IFetchEntriesReturn> {
|
||||
let _entries: any = [];
|
||||
if (entries && entries.items && entries.items.length > 0) {
|
||||
const faqItems = entries.items.map(entry => this.convertFAQ(entry));
|
||||
return { faqItems, total: entries.total };
|
||||
switch (entryType) {
|
||||
case 'post':
|
||||
_entries = entries.items.map(entry => this.convertPost(entry));
|
||||
break;
|
||||
case 'faq':
|
||||
_entries = entries.items.map(entry => this.convertFAQ(entry));
|
||||
break;
|
||||
case 'splitPage':
|
||||
_entries = entries.items.map(entry => this.convertPage(entry));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return { entries: _entries, total: entries.total };
|
||||
}
|
||||
|
||||
return { faqItems: [], total: 0 } as IFetchFAQItemsReturn;
|
||||
return { entries: _entries, total: 0 };
|
||||
}
|
||||
|
||||
public convertImage = (rawImage): IFigureImage =>
|
||||
|
@ -201,6 +230,8 @@ export class CmsApi {
|
|||
imageUrl: rawImage.file.url.replace('//', 'https://'), // may need to put null check as well here
|
||||
description: rawImage.description ?? null,
|
||||
title: rawImage.title ?? null,
|
||||
width: rawImage.file.details.image.width,
|
||||
height: rawImage.file.details.image.height,
|
||||
}
|
||||
: null;
|
||||
|
||||
|
|
15
types/cms.ts
15
types/cms.ts
|
@ -17,6 +17,8 @@ export type IFigureImage = {
|
|||
title: string | null;
|
||||
description: string | null;
|
||||
imageUrl: string;
|
||||
width: string | number;
|
||||
height: string | number;
|
||||
};
|
||||
|
||||
export interface IPost {
|
||||
|
@ -50,3 +52,16 @@ export interface IFAQItem {
|
|||
question: string;
|
||||
answer: Document;
|
||||
}
|
||||
|
||||
export interface IFetchEntriesReturn {
|
||||
entries: Array<any>;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface IFetchBlogEntriesReturn extends IFetchEntriesReturn {
|
||||
entries: Array<IPost>;
|
||||
}
|
||||
|
||||
export interface IFetchFAQItemsReturn extends IFetchEntriesReturn {
|
||||
entries: Array<IFAQItem>;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue