import type { GetStaticProps } from 'next'; import Head from 'next/head'; import { useCallback } from 'react'; import { useIntl } from 'react-intl'; import { getLayout, Heading, LinksWidget, Notice, PostsList, Pagination, type RenderPaginationLink, type RenderPaginationItemAriaLabel, Page, PageHeader, PageBody, PageSidebar, Spinner, } from '../../components'; import { convertWPThematicPreviewToPageLink, convertWPTopicPreviewToPageLink, fetchPostsList, fetchThematicsCount, fetchThematicsList, fetchTopicsCount, fetchTopicsList, } from '../../services/graphql'; import styles from '../../styles/pages/blog.module.scss'; import type { GraphQLConnection, NextPageWithLayout, WPPostPreview, WPThematicPreview, WPTopicPreview, } from '../../types'; import { CONFIG } from '../../utils/config'; import { ARTICLE_ID, PAGINATED_ROUTE_PREFIX, ROUTES, } from '../../utils/constants'; import { getBlogGraph, getLinksItemData, getPostsWithUrl, getSchemaFrom, getWebPageGraph, } from '../../utils/helpers'; import { loadTranslation, type Messages } from '../../utils/helpers/server'; import { useArticlesList, useBreadcrumbs, useThematicsList, useTopicsList, } from '../../utils/hooks'; const renderPaginationLink: RenderPaginationLink = (pageNum) => { if (pageNum === 1) return ROUTES.BLOG; return `${ROUTES.BLOG}${PAGINATED_ROUTE_PREFIX}/${pageNum}`; }; type BlogPageProps = { data: { posts: GraphQLConnection; thematics: GraphQLConnection; topics: GraphQLConnection; }; translation: Messages; }; /** * Blog index page. */ const BlogPage: NextPageWithLayout = ({ data }) => { const intl = useIntl(); const { articles, error, firstNewResultIndex, isLoading, isLoadingMore, isRefreshing, hasNextPage, loadMore, } = useArticlesList({ fallback: [data.posts], perPage: CONFIG.postsPerPage, }); const { isLoading: areThematicsLoading, thematics } = useThematicsList({ fallback: data.thematics, input: { first: data.thematics.pageInfo.total }, }); const { isLoading: areTopicsLoading, topics } = useTopicsList({ fallback: data.topics, input: { first: data.topics.pageInfo.total }, }); const messages = { loading: { thematicsList: intl.formatMessage({ defaultMessage: 'Thematics are loading...', description: 'BlogPage: loading thematics message', id: 'y37FuH', }), topicsList: intl.formatMessage({ defaultMessage: 'Topics are loading...', description: 'BlogPage: loading topics message', id: 'OsclKU', }), }, pageTitle: intl.formatMessage({ defaultMessage: 'Blog', description: 'BlogPage: page title', id: '7TbbIk', }), pagination: { title: intl.formatMessage({ defaultMessage: 'Pagination', description: 'BlogPage: pagination accessible name', id: 'AXe1Iz', }), }, seo: { metaDesc: intl.formatMessage( { defaultMessage: "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", description: 'BlogPage: SEO - Meta description', id: '18h/t0', }, { websiteName: CONFIG.name } ), title: intl.formatMessage( { defaultMessage: 'Blog: development, open source - {websiteName}', description: 'BlogPage: SEO - Page title', id: '+Y+tLK', }, { websiteName: CONFIG.name } ), }, widgets: { thematicsListTitle: intl.formatMessage({ defaultMessage: 'Thematics', description: 'BlogPage: thematics list widget title', id: 'HriY57', }), topicsListTitle: intl.formatMessage({ defaultMessage: 'Topics', description: 'BlogPage: topics list widget title', id: '2D9tB5', }), }, }; const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumbs( messages.pageTitle ); const jsonLd = getSchemaFrom([ getWebPageGraph({ breadcrumb: breadcrumbSchema, description: messages.seo.metaDesc, slug: ROUTES.BLOG, title: messages.pageTitle, }), getBlogGraph({ description: '', posts: articles?.flatMap((page) => page.edges.map(({ node }) => { return { '@id': `${node.slug}#${ARTICLE_ID}` }; }) ), slug: ROUTES.BLOG, title: messages.pageTitle, }), ]); const renderPaginationLabel: RenderPaginationItemAriaLabel = useCallback( ({ kind, pageNumber: number, isCurrentPage }) => { switch (kind) { case 'backward': return intl.formatMessage( { defaultMessage: 'Go to previous page, page {number}', description: 'BlogPage: previous page label', id: 'faO6BQ', }, { number } ); case 'forward': return intl.formatMessage( { defaultMessage: 'Go to next page, page {number}', description: 'BlogPage: next page label', id: 'oq3BzP', }, { number } ); case 'number': default: return isCurrentPage ? intl.formatMessage( { defaultMessage: 'Current page, page {number}', description: 'BlogPage: current page label', id: 'JL6G22', }, { number } ) : intl.formatMessage( { defaultMessage: 'Go to page {number}', description: 'BlogPage: page number label', id: 'IVczxR', }, { number } ); } }, [intl] ); const pageUrl = `${CONFIG.url}${ROUTES.BLOG}`; return ( {messages.seo.title} {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}