Blog page (#15)

This commit is contained in:
Manuel Garcia Genta
2022-04-11 12:18:52 -03:00
committed by GitHub
parent 4e8b9d5a64
commit a5e0829ed4
44 changed files with 26544 additions and 51 deletions
+98
View File
@@ -0,0 +1,98 @@
import { flatten } from 'lodash'
import cms from './cms'
import { BlogPostRecord } from './cms/generated'
import { getPagination, PaginationData } from './utils'
const DEFAULT_PAGE_STEP = 9
export type GetBlogPostsParams = {
page?: number
step?: number
filterIds?: string[]
filterSlugs?: string[]
category?: string
search?: string
}
export const getBlogPosts = async ({
page = 1,
step = DEFAULT_PAGE_STEP,
filterIds,
filterSlugs,
category,
search = ''
}: GetBlogPostsParams) => {
const { allBlogPosts, _allBlogPostsMeta } = await cms().GetBlogPosts({
skip: (page - 1) * step,
step,
filterIds,
filterSlugs,
category,
search
})
const pagination = getPagination(page, _allBlogPostsMeta.count, step)
return {
pagination,
data: allBlogPosts
}
}
export const getBlogPostsCategories = async () => {
const { allCategories } = await cms().GetBlogPostsCategories()
// Push dummy category
allCategories.push({
id: '0',
slug: 'none',
title: 'No Category'
})
return allCategories
}
export type GetBlogPostsSlugsParams = {
page?: number
step?: number
}
export const getBlogPostsSlugs = async ({
page = 1,
step = DEFAULT_PAGE_STEP
}: GetBlogPostsSlugsParams) => {
const { allBlogPosts, _allBlogPostsMeta } = await cms().BlogPostsSlug({
skip: (page - 1) * step,
step
})
const pagination = getPagination(page, _allBlogPostsMeta.count, step)
return {
pagination,
data: allBlogPosts
}
}
export const getAllBlogPostsSlugs = async () => {
const postsChunks: Pick<BlogPostRecord, 'slug'>[][] = []
let nextPage: number | null = 1
while (nextPage) {
const {
pagination,
data
}: { pagination: PaginationData; data: Pick<BlogPostRecord, 'slug'>[] } =
await getBlogPostsSlugs({
page: nextPage,
step: 100
})
postsChunks.push(data)
nextPage = pagination.nextPage
}
return flatten(postsChunks)
}
+3
View File
@@ -0,0 +1,3 @@
fragment Author on AuthorRecord {
name
}
+21
View File
@@ -0,0 +1,21 @@
fragment BlogPost on BlogPostRecord {
_seoMetaTags {
...SEOTags
}
title
date
category {
title
slug
}
author {
...Author
}
slug
content {
value
}
image {
...Image
}
}
+5
View File
@@ -0,0 +1,5 @@
fragment Category on CategoryRecord {
id
title
slug
}
+7
View File
@@ -0,0 +1,7 @@
fragment Image on FileField {
url
alt
height
width
title
}
+5
View File
@@ -0,0 +1,5 @@
fragment SEOTags on Tag {
content
tag
attributes
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
import { GraphQLClient } from 'graphql-request'
import { getSdk } from './generated'
const cms = (preview?: boolean) => {
const endpoint = 'https://graphql.datocms.com/'
const previewEndpoint = 'https://graphql.datocms.com/preview'
return getSdk(
new GraphQLClient(preview ? previewEndpoint : endpoint, {
headers: {
Authorization: `Bearer ${process.env.CMS_ACCESS_TOKEN}`
}
})
)
}
export default cms
+40
View File
@@ -0,0 +1,40 @@
query GetBlogPostsCategories {
allCategories {
...Category
}
}
query GetBlogPosts(
$skip: IntType!
$step: IntType!
$filterIds: [ItemId] = []
$filterSlugs: [String] = []
$category: [ItemId]
$search: String!
) {
_allBlogPostsMeta(
filter: {
title: { isBlank: false }
id: { notIn: $filterIds }
slug: { notIn: $filterSlugs }
category: { anyIn: $category }
OR: [{ title: { matches: { pattern: $search } } }]
}
) {
count
}
allBlogPosts(
filter: {
title: { isBlank: false }
id: { notIn: $filterIds }
slug: { notIn: $filterSlugs }
category: { anyIn: $category }
OR: [{ title: { matches: { pattern: $search } } }]
}
first: $step
orderBy: date_DESC
skip: $skip
) {
...BlogPost
}
}
+14
View File
@@ -0,0 +1,14 @@
query BlogPostsSlug($skip: IntType!, $step: IntType!) {
_allBlogPostsMeta {
count
}
allBlogPosts(first: $step, skip: $skip, orderBy: date_DESC) {
slug
}
}
query SingleBlogPost($slug: String!) {
blogPost(filter: { slug: { eq: $slug }, title: { isBlank: false } }) {
...BlogPost
}
}
+13
View File
@@ -0,0 +1,13 @@
@import '~/css/helpers';
.structured {
max-width: tovw(856px, 'default', 856px);
p {
font-family: var(--font-tt-hoves);
font-size: tovw(22px, 'default', 15px);
line-height: 160%;
letter-spacing: -0.01em;
margin: tovw(44px, 'default', 40px) 0;
}
}
+72
View File
@@ -0,0 +1,72 @@
import { StructuredText } from 'react-datocms'
import Marked from '~/components/common/marked'
import { Container } from '~/components/layout/container'
import s from './blog.module.scss'
export const renderBlock = ({ record }) => {
switch (record.__typename) {
case 'ImageRecord':
return (
<div className="my-12">
<img
src={record.image?.url ?? '/4040404'}
width={record.image?.width ?? 0}
height={record.image?.height ?? 0}
alt={record.image?.alt ?? record.image?.title ?? 'blog image'}
/>
</div>
)
case 'CalloutRecord':
return <Marked>{record.content ?? ''}</Marked>
// case 'ShareLinkRecord':
// return (
// <div className="flex justify-center mt-20 space-x-4">
// <SocialLink variant="logo-only" tag="/" type="twitter" />
// <SocialLink variant="logo-only" tag="/" type="telegram" />
// <SocialLink variant="logo-only" tag="/" type="facebook" />
// <SocialLink variant="logo-only" type="copy-link" />
// </div>
// )
// case 'CtaRecord':
// return (
// <SectionCTAs
// className="flex flex-col items-center justify-center mt-20 space-y-2 sm:space-y-0 sm:space-x-2 sm:flex-row"
// ctas={
// record?.links?.map((link, i) => {
// return {
// href: link?.href ?? '',
// as: 'a',
// children: link?.label ?? '',
// variant: i === 0 ? 'primary' : 'tertiary',
// size: 'lg'
// }
// }) ?? []
// }
// />
// )
// case 'TableRecord':
// return (
// <ViewportWidthBox>
// <Container size="sm">
// <div className={s.table}>
// <Marked>{record.markdownTable ?? ''}</Marked>
// </div>
// </Container>
// </ViewportWidthBox>
// )
default:
return null
}
}
const CustomStructuredText = ({ data }) => {
return (
<Container className={s.structured}>
<StructuredText data={data} renderBlock={renderBlock} />
</Container>
)
}
export default CustomStructuredText
+74
View File
@@ -0,0 +1,74 @@
import { render } from 'datocms-structured-text-to-plain-text'
import { BlogPostFragment } from 'lib/cms/generated'
export const getPlainTextContent = (post: BlogPostFragment) => {
const text = render(post.content)
return text ?? ''
}
export const getDescription = (post: BlogPostFragment) => {
let text = getPlainTextContent(post)
const charLimit = 200
text = text.substring(0, charLimit)
let words = text.trim().split(/\s+/)
words = words.splice(0, words.length - 1)
const description = words.join(' ') + '...'
return description
}
export const getSeoTags = (post: BlogPostFragment) => {
const description = getDescription(post)
const filteredMetaTags = post._seoMetaTags.filter((t) => {
if (
t.attributes?.name === 'description' ||
t.attributes?.name === 'twitter:description' ||
t.attributes?.property === 'og:description' ||
t.tag === 'title'
) {
return false
}
return true
})
return [
...filteredMetaTags,
{
content: post.title + ' | Akash Network',
tag: 'title'
},
{
attributes: {
content: description,
name: 'description'
},
content: null,
tag: 'meta'
},
{
attributes: {
content: description,
property: 'og:description'
},
content: null,
tag: 'meta'
},
{
attributes: {
content: description,
name: 'twitter:description'
},
content: null,
tag: 'meta'
}
]
}
export type BlogPostWithMeta = Omit<BlogPostFragment, 'content'> & {
plainTextContent: string
numId: number
}
export type SingleBlogPost = BlogPostFragment & {
description: string
}
+57
View File
@@ -39,3 +39,60 @@ export const formatStatCount = (count = 0, withAbbr = false, decimals = 2) => {
}
return result
}
export const range = (start: number, stop?: number, step?: number) => {
if (typeof stop == 'undefined') {
// one param defined
stop = start
start = 0
}
if (typeof step == 'undefined') {
step = 1
}
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
return []
}
const result = []
for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
result.push(i)
}
return result
}
export function getHash(input: string) {
let hash = 0
for (let i = 0; i < input.length; i++) {
hash = (hash << 5) - hash + input.charCodeAt(i)
hash |= 0 // to 32bit integer
}
return hash
}
export const formatDate = (date: string) => {
const [year, month, day] = date.split('-')
return `${month}.${day}.${year}`
}
export type PaginationData = {
page: number
nextPage: number | null
totalPages: number
step: number
total: number
}
export const getPagination = (
page: number,
total: number,
step: number
): PaginationData => ({
page: page,
nextPage: page < Math.ceil(total / step) ? page + 1 : null,
totalPages: Math.ceil(total / step),
step: step,
total
})
+9
View File
@@ -60,3 +60,12 @@ export const makeQuery = (
if (replace) return router.replace(url, url, { scroll: false, ...opts })
else return router.push(url, url, { scroll: false, ...opts })
}
export const getQueryParams = (params: QueryParams) => {
const searchParams = new URLSearchParams()
Object.keys(params).forEach((key) => {
const value = params[key]
if (value !== null) searchParams.append(key, value)
})
return searchParams
}