1
0
Fork 0

fix(org): Port blog posts to new MDX loader

This commit is contained in:
Joost De Cock 2023-10-10 07:35:11 +02:00
parent d84ae7af3b
commit ec0b8b0322
13 changed files with 115 additions and 195 deletions

View file

@ -1,10 +1,17 @@
import { cloudflareImageUrl, nsMerge } from 'shared/utils.mjs'
// Components // Components
import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs'
import { Lightbox } from 'shared/components/lightbox.mjs'
import { ImageWrapper } from 'shared/components/wrappers/img.mjs'
import { TimeAgo, ns as timeagoNs } from 'shared/components/timeago/index.mjs'
import { useTranslation } from 'next-i18next'
import { FrontmatterHead } from './docs.mjs' import { FrontmatterHead } from './docs.mjs'
import { import {
BaseLayout, BaseLayout,
BaseLayoutLeft, BaseLayoutLeft,
BaseLayoutProse, BaseLayoutProse,
BaseLayoutRight, BaseLayoutRight,
BaseLayoutWide,
} from 'shared/components/base-layout.mjs' } from 'shared/components/base-layout.mjs'
import { import {
NavLinks, NavLinks,
@ -12,41 +19,99 @@ import {
MainSections, MainSections,
ns as navNs, ns as navNs,
} from 'shared/components/navigation/sitenav.mjs' } from 'shared/components/navigation/sitenav.mjs'
import { Toc } from 'shared/components/mdx/toc.mjs' import { Toc, ns as tocNs } from 'shared/components/mdx/toc.mjs'
import { PrevNext } from 'shared/components/prev-next.mjs' import { PrevNext } from 'shared/components/prev-next.mjs'
export const ns = [navNs, 'docs'] export const ns = nsMerge(navNs, tocNs, 'docs')
/** checks for a slug that isn't a post, to prevent a prev or next button to it */ /** checks for a slug that isn't a post, to prevent a prev or next button to it */
const isEndSlug = (slug) => slug.split('/').length === 1 const isEndSlug = (slug) => slug.split('/').length === 1
const PostMeta = ({ frontmatter, t }) => (
<div className="flex flex-row justify-between text-sm mb-1 mt-2">
<div>
<TimeAgo date={frontmatter.date} t={t} />
</div>
<div>
{frontmatter.designs?.map((design) => (
<Tag
href={`/showcase#filter="${design}"`}
color="primary"
hoverColor="secondary"
key={design}
>
{design}
</Tag>
))}
</div>
<div>
By{' '}
<a href="#maker" className="text-secondary hover:text-secondary-focus">
{frontmatter.author || frontmatter.maker || 'FIXME: No displayname'}
</a>
</div>
</div>
)
const PostImage = ({ imgId, frontmatter }) => (
<figure>
<Lightbox>
<ImageWrapper>
<img
src={cloudflareImageUrl({ id: imgId })}
alt={frontmatter.caption}
className="shadow m-auto"
/>
</ImageWrapper>
<figcaption
className="text-center mb-8 prose m-auto text-sm italic"
dangerouslySetInnerHTML={{ __html: frontmatter.caption }}
/>
</Lightbox>
</figure>
)
const PostContent = ({ mdx, dir }) => (
<div className="strapi prose lg:prose-lg mb-12 m-auto">
<MdxWrapper mdx={mdx} slug={`blog/${dir}`} />
</div>
)
/** layout for a page that displays a blog, showcase or newsletter */ /** layout for a page that displays a blog, showcase or newsletter */
export const PostLayout = ({ children = [], slug, frontmatter, locale }) => ( export const PostLayout = ({ mdx, slug, frontmatter, imgId, locale, type, dir }) => {
<> const { t } = useTranslation(ns)
<FrontmatterHead {...{ frontmatter, slug, locale }} />
return (
<BaseLayout> <BaseLayout>
<FrontmatterHead {...{ frontmatter, slug, locale }} />
<BaseLayoutLeft> <BaseLayoutLeft>
<MainSections /> <MainSections />
<NavLinks /> <NavLinks />
</BaseLayoutLeft> </BaseLayoutLeft>
<BaseLayoutProse> <BaseLayoutWide>
<div className="w-full"> <div className="w-full max-w-4xl">
<Breadcrumbs /> <Breadcrumbs />
<h1 className="break-words searchme">{frontmatter.title}</h1> <h1 className="break-words searchme">{frontmatter.title}</h1>
<PostMeta frontmatter={frontmatter} t={t} />
<PostImage imgId={`${type}-${dir}`} frontmatter={frontmatter} />
<div className="block xl:hidden"> <div className="block xl:hidden">
<Toc toc={frontmatter.toc} wrap /> <Toc toc={frontmatter.toc} wrap />
</div> </div>
</div> </div>
{children} <div className="flex flex-row">
<PrevNext noPrev={isEndSlug} noNext={isEndSlug} /> <BaseLayoutProse>
</BaseLayoutProse> <article className="mb-12 max-w-7xl">
<PostContent {...{ mdx }} />
<BaseLayoutRight> </article>
<div className="hidden xl:block"> </BaseLayoutProse>
<Toc toc={frontmatter.toc} wrap /> <BaseLayoutRight>
<div className="hidden xl:block xl:sticky xl:top-4">
<Toc toc={frontmatter.toc} wrap />
</div>
</BaseLayoutRight>
</div> </div>
</BaseLayoutRight> </BaseLayoutWide>
</BaseLayout> </BaseLayout>
</> )
) }

View file

@ -1,100 +0,0 @@
import { Lightbox } from 'shared/components/lightbox.mjs'
import { ImageWrapper } from 'shared/components/wrappers/img.mjs'
//import { Author } from './author.mjs'
import { TimeAgo, ns as timeagoNs } from 'shared/components/timeago/index.mjs'
import { useTranslation } from 'next-i18next'
import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs'
import { cloudflareImageUrl } from 'shared/utils.mjs'
import Markdown from 'react-markdown'
import { nsMerge } from 'shared/utils.mjs'
import { Tag } from 'shared/components/tag.mjs'
import { Popout } from 'shared/components/popout/index.mjs'
export const ns = nsMerge('common', 'posts', timeagoNs)
export const PostArticle = (props) => {
const { t } = useTranslation('common')
return (
<article className="mb-12 max-w-7xl">
<PostMeta frontmatter={props.frontmatter} t={t} />
<PostImage imgId={props.imgId} frontmatter={props.frontmatter} />
<PostContent {...props} />
<Popout comment by="joost">
<h5 id="maker">We cannot link authors/makers to their FreeSewing accounts (yet)</h5>
<p>
This is a known issue that I decided not to block the v3 release for. I will take care of
this later.
</p>
<p>
If you are the author of this post, you can reach out so I can correctly attribute it.
</p>
</Popout>
</article>
)
}
//<PostAuthor frontmatter={props.frontmatter} />
const PostMeta = ({ frontmatter, t }) => (
<div className="flex flex-row justify-between text-sm mb-1 mt-2">
<div>
<TimeAgo date={frontmatter.date} t={t} />
</div>
<div>
{frontmatter.designs?.map((design) => (
<Tag
href={`/showcase#filter="${design}"`}
color="primary"
hoverColor="secondary"
key={design}
>
{design}
</Tag>
))}
</div>
<div>
By{' '}
<a href="#maker" className="text-secondary hover:text-secondary-focus">
{frontmatter.author || frontmatter.maker || 'FIXME: No displayname'}
</a>
</div>
</div>
)
const PostImage = ({ imgId, frontmatter }) => (
<figure>
<Lightbox>
<ImageWrapper>
<img
src={cloudflareImageUrl({ id: imgId })}
alt={frontmatter.caption}
className="shadow m-auto"
/>
</ImageWrapper>
<figcaption
className="text-center mb-8 prose m-auto"
dangerouslySetInnerHTML={{ __html: frontmatter.caption }}
/>
</Lightbox>
</figure>
)
//const PostAuthor = ({ frontmatter }) => (
// <div className="max-w-prose text-lg lg:text-xl">
// <Author author={frontmatter.author || frontmatter.maker} />
// </div>
//)
const PostContent = (props) =>
props.MDX ? <PostMDXContent {...props} /> : <PostPreviewContent {...props} />
const PostMDXContent = ({ MDX }) => (
<div className="strapi prose lg:prose-lg mb-12 m-auto">
<MdxWrapper>{MDX}</MdxWrapper>
</div>
)
const PostPreviewContent = ({ body }) => (
<div className="strapi prose lg:prose-lg mb-12 m-auto">
<Markdown>{body}</Markdown>
</div>
)

View file

@ -1,4 +0,0 @@
xMadeThis: "<strong>{x}</strong> hat dies erstellt"
xWroteThis: "<strong>{x}</strong> hat dies geschrieben"
by: By

View file

@ -1,4 +0,0 @@
xMadeThis: "<strong>{x}</strong> made this"
xWroteThis: "<strong>{x}</strong> wrote this"
by: By

View file

@ -1,4 +0,0 @@
xMadeThis: "<strong>{x}</strong> made this"
xWroteThis: "<strong>{x}</strong> wrote this"
by: By

View file

@ -1,4 +0,0 @@
xMadeThis: "<strong>{x}</strong> a fait ceci"
xWroteThis: "<strong>{x}</strong> a écrit ceci"
by: By

View file

@ -1,4 +0,0 @@
xMadeThis: "<strong>{x}</strong> made this"
xWroteThis: "<strong>{x}</strong> wrote this"
by: By

View file

@ -1,4 +0,0 @@
xMadeThis: "<strong>{x}</strong> є автором цієї роботи"
xWroteThis: "<strong>{x}</strong> є автором цього тексту"
by: By

View file

@ -2,76 +2,50 @@ import { nsMerge } from 'shared/utils.mjs'
import { pages as posts } from 'site/prebuild/blog.mjs' import { pages as posts } from 'site/prebuild/blog.mjs'
import { getPostSlugPaths } from 'site/components/mdx/posts/utils.mjs' import { getPostSlugPaths } from 'site/components/mdx/posts/utils.mjs'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useDynamicMdx } from 'shared/hooks/use-dynamic-mdx.mjs' import { loadMdxAsStaticProps } from 'shared/mdx/load.mjs'
import { useCallback } from 'react' import { PostLayout, ns as layoutNs } from 'site/components/layouts/post.mjs'
import { PostArticle, ns as postNs } from 'site/components/mdx/posts/article.mjs'
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
const namespaces = nsMerge(postNs, pageNs) const ns = nsMerge(pageNs, layoutNs)
/*
* Each page MUST be wrapped in the PageWrapper component.
* You also MUST spread props.page into this wrapper component
* when path and locale come from static props (as here)
* or set them manually.
*/
const BlogPage = ({ locale, dir, page }) => {
// function to load the correct markdown
const loader = useCallback(() => import(`orgmarkdown/blog/${dir}/${locale}.md`), [dir, locale])
// load the markdown
const { frontmatter, MDX } = useDynamicMdx(loader)
const BlogPage = ({ locale, dir, page, mdx, frontmatter }) => {
return ( return (
<PageWrapper {...page} title={frontmatter.title}> <PageWrapper
<PostArticle {...{ frontmatter, MDX }} imgId={`blog-${dir}`} /> {...page}
</PageWrapper> title={frontmatter.title}
layout={(props) => (
<PostLayout
{...props}
slug={page.path.join('/')}
frontmatter={frontmatter}
mdx={mdx}
dir={dir}
type="blog"
/>
)}
/>
) )
} }
/*
* getStaticProps() is used to fetch data at build-time.
*
* On this page, it passes the name of the bundle to be loaded on the client.
*
* This, in combination with getStaticPaths() below means this
* page will be used to render/generate all blog content.
*
* To learn more, see: https://nextjs.org/docs/basic-features/data-fetching
*/
export async function getStaticProps({ params, locale }) { export async function getStaticProps({ params, locale }) {
const { dir } = params
// if the dir isn't present in the prebuilt posts, return 404
if (!Object.keys(posts[locale]).includes(`blog/${dir}`)) return { notFound: true }
return { return {
props: { props: {
dir, dir: params.dir,
locale, locale,
...(await serverSideTranslations(locale, namespaces)), ...(await serverSideTranslations(locale, ns)),
...(await loadMdxAsStaticProps({
language: locale,
site: 'org',
slug: `blog/${params.dir}`,
})),
page: { page: {
locale, locale,
path: ['blog', dir], path: ['blog', params.dir],
}, },
}, },
} }
} }
/*
* getStaticPaths() is used to specify for which routes (think URLs)
* this page should be used to generate the result.
*
* On this page, it is returning a truncated list of routes (think URLs) for all
* the mdx blog (markdown) content.
* That list comes from prebuild/blog-paths.mjs, which is built in the prebuild step
* and contains paths, titles, imageUrls, and intro for all blog posts.
*
* the fallback: 'blocking' property means that
* any pages that haven't been pre-generated
* will generate and cache the first time someone visits them
*
* To learn more, see: https://nextjs.org/docs/basic-features/data-fetching
*/
export const getStaticPaths = async () => { export const getStaticPaths = async () => {
return { return {
paths: getPostSlugPaths(posts), paths: getPostSlugPaths(posts),

View file

@ -42,7 +42,7 @@ export const Toc = ({ toc = [], wrap = false }) => {
border-2 bg-base-200 bg-opacity-30 p-4 rounded-lg border-base-200 border-2 bg-base-200 bg-opacity-30 p-4 rounded-lg border-base-200
`} `}
> >
<h4>{t('toc')}</h4> <h4>{t('docs:toc')}</h4>
{ul} {ul}
</div> </div>
) : ( ) : (

View file

@ -17,6 +17,7 @@ export const TimeAgo = ({ date }) => {
} else if (i.years < 1 && i.months < 1) { } else if (i.years < 1 && i.months < 1) {
if (Math.floor(i.days) === 1) ago += `${t('oneDay')}` if (Math.floor(i.days) === 1) ago += `${t('oneDay')}`
else if (Math.floor(i.days) === 0) ago += `${t('lessThanADay')}` else if (Math.floor(i.days) === 0) ago += `${t('lessThanADay')}`
else ago += `${i.days} ${t('days')}, ${Math.floor(i.hours)} ${t('hours')}`
} else { } else {
if (i.years === 1) ago += `${i.years} ${t('year')}, ` if (i.years === 1) ago += `${i.years} ${t('year')}, `
else if (i.years > 1) ago += `${i.years} ${t('years')}, ` else if (i.years > 1) ago += `${i.years} ${t('years')}, `

View file

@ -23,3 +23,7 @@ credits: Credits
contentsBy: Contents by contentsBy: Contents by
translators: Translators translators: Translators
title: Title title: Title
xMadeThis: "<strong>{x}</strong> made this"
xWroteThis: "<strong>{x}</strong> wrote this"
by: By

File diff suppressed because one or more lines are too long