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
|
||||
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 {
|
||||
BaseLayout,
|
||||
BaseLayoutLeft,
|
||||
BaseLayoutProse,
|
||||
BaseLayoutRight,
|
||||
BaseLayoutWide,
|
||||
} from 'shared/components/base-layout.mjs'
|
||||
import {
|
||||
NavLinks,
|
||||
|
@ -12,41 +19,99 @@ import {
|
|||
MainSections,
|
||||
ns as navNs,
|
||||
} 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'
|
||||
|
||||
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 */
|
||||
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 */
|
||||
export const PostLayout = ({ children = [], slug, frontmatter, locale }) => (
|
||||
<>
|
||||
<FrontmatterHead {...{ frontmatter, slug, locale }} />
|
||||
export const PostLayout = ({ mdx, slug, frontmatter, imgId, locale, type, dir }) => {
|
||||
const { t } = useTranslation(ns)
|
||||
|
||||
return (
|
||||
<BaseLayout>
|
||||
<FrontmatterHead {...{ frontmatter, slug, locale }} />
|
||||
<BaseLayoutLeft>
|
||||
<MainSections />
|
||||
<NavLinks />
|
||||
</BaseLayoutLeft>
|
||||
|
||||
<BaseLayoutProse>
|
||||
<div className="w-full">
|
||||
<BaseLayoutWide>
|
||||
<div className="w-full max-w-4xl">
|
||||
<Breadcrumbs />
|
||||
<h1 className="break-words searchme">{frontmatter.title}</h1>
|
||||
<PostMeta frontmatter={frontmatter} t={t} />
|
||||
<PostImage imgId={`${type}-${dir}`} frontmatter={frontmatter} />
|
||||
<div className="block xl:hidden">
|
||||
<Toc toc={frontmatter.toc} wrap />
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
<PrevNext noPrev={isEndSlug} noNext={isEndSlug} />
|
||||
<div className="flex flex-row">
|
||||
<BaseLayoutProse>
|
||||
<article className="mb-12 max-w-7xl">
|
||||
<PostContent {...{ mdx }} />
|
||||
</article>
|
||||
</BaseLayoutProse>
|
||||
|
||||
<BaseLayoutRight>
|
||||
<div className="hidden xl:block">
|
||||
<div className="hidden xl:block xl:sticky xl:top-4">
|
||||
<Toc toc={frontmatter.toc} wrap />
|
||||
</div>
|
||||
</BaseLayoutRight>
|
||||
</div>
|
||||
</BaseLayoutWide>
|
||||
</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 { getPostSlugPaths } from 'site/components/mdx/posts/utils.mjs'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import { useDynamicMdx } from 'shared/hooks/use-dynamic-mdx.mjs'
|
||||
import { useCallback } from 'react'
|
||||
import { PostArticle, ns as postNs } from 'site/components/mdx/posts/article.mjs'
|
||||
import { loadMdxAsStaticProps } from 'shared/mdx/load.mjs'
|
||||
import { PostLayout, ns as layoutNs } from 'site/components/layouts/post.mjs'
|
||||
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
|
||||
|
||||
const namespaces = nsMerge(postNs, pageNs)
|
||||
|
||||
/*
|
||||
* 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 ns = nsMerge(pageNs, layoutNs)
|
||||
|
||||
const BlogPage = ({ locale, dir, page, mdx, frontmatter }) => {
|
||||
return (
|
||||
<PageWrapper {...page} title={frontmatter.title}>
|
||||
<PostArticle {...{ frontmatter, MDX }} imgId={`blog-${dir}`} />
|
||||
</PageWrapper>
|
||||
<PageWrapper
|
||||
{...page}
|
||||
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 }) {
|
||||
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 {
|
||||
props: {
|
||||
dir,
|
||||
dir: params.dir,
|
||||
locale,
|
||||
...(await serverSideTranslations(locale, namespaces)),
|
||||
...(await serverSideTranslations(locale, ns)),
|
||||
...(await loadMdxAsStaticProps({
|
||||
language: locale,
|
||||
site: 'org',
|
||||
slug: `blog/${params.dir}`,
|
||||
})),
|
||||
page: {
|
||||
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 () => {
|
||||
return {
|
||||
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
|
||||
`}
|
||||
>
|
||||
<h4>{t('toc')}</h4>
|
||||
<h4>{t('docs:toc')}</h4>
|
||||
{ul}
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
@ -17,6 +17,7 @@ export const TimeAgo = ({ date }) => {
|
|||
} else if (i.years < 1 && i.months < 1) {
|
||||
if (Math.floor(i.days) === 1) ago += `${t('oneDay')}`
|
||||
else if (Math.floor(i.days) === 0) ago += `${t('lessThanADay')}`
|
||||
else ago += `${i.days} ${t('days')}, ${Math.floor(i.hours)} ${t('hours')}`
|
||||
} else {
|
||||
if (i.years === 1) ago += `${i.years} ${t('year')}, `
|
||||
else if (i.years > 1) ago += `${i.years} ${t('years')}, `
|
||||
|
|
|
@ -23,3 +23,7 @@ credits: Credits
|
|||
contentsBy: Contents by
|
||||
translators: Translators
|
||||
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