fix(org): Port blog posts to new MDX loader
This commit is contained in:
parent
d84ae7af3b
commit
ec0b8b0322
13 changed files with 115 additions and 195 deletions
|
@ -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>
|
||||||
</>
|
)
|
||||||
)
|
}
|
||||||
|
|
|
@ -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>
|
|
||||||
)
|
|
|
@ -1,4 +0,0 @@
|
||||||
xMadeThis: "<strong>{x}</strong> hat dies erstellt"
|
|
||||||
xWroteThis: "<strong>{x}</strong> hat dies geschrieben"
|
|
||||||
by: By
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
xMadeThis: "<strong>{x}</strong> made this"
|
|
||||||
xWroteThis: "<strong>{x}</strong> wrote this"
|
|
||||||
by: By
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
xMadeThis: "<strong>{x}</strong> made this"
|
|
||||||
xWroteThis: "<strong>{x}</strong> wrote this"
|
|
||||||
by: By
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
xMadeThis: "<strong>{x}</strong> a fait ceci"
|
|
||||||
xWroteThis: "<strong>{x}</strong> a écrit ceci"
|
|
||||||
by: By
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
xMadeThis: "<strong>{x}</strong> made this"
|
|
||||||
xWroteThis: "<strong>{x}</strong> wrote this"
|
|
||||||
by: By
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
xMadeThis: "<strong>{x}</strong> є автором цієї роботи"
|
|
||||||
xWroteThis: "<strong>{x}</strong> є автором цього тексту"
|
|
||||||
by: By
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -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')}, `
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue