From e9d21ddd62fd4ed85e95b928d8ffd2071d3a082b Mon Sep 17 00:00:00 2001 From: Enoch Riese Date: Mon, 19 Jun 2023 16:27:13 -0500 Subject: [PATCH] shared components and utils for sanity pages --- .eslintrc.cjs | 1 + sites/org/components/sanity/author.mjs | 43 +++++++ sites/org/components/sanity/mdx-wrapper.mjs | 29 +++++ sites/org/components/sanity/page-wrapper.mjs | 80 ++++++++++++ .../components/sanity/utils.mjs} | 6 +- sites/org/pages/blog/[slug].mjs | 115 ++---------------- sites/org/pages/blog/index.mjs | 2 +- sites/org/pages/showcase/[slug].mjs | 72 +++++++++++ sites/org/pages/showcase/index.mjs | 95 ++++++++++++--- sites/shared/components/wrappers/mdx.mjs | 22 ++-- sites/shared/hooks/use-evaled-mdx.mjs | 21 ++++ 11 files changed, 350 insertions(+), 136 deletions(-) create mode 100644 sites/org/components/sanity/author.mjs create mode 100644 sites/org/components/sanity/mdx-wrapper.mjs create mode 100644 sites/org/components/sanity/page-wrapper.mjs rename sites/{shared/sanity.mjs => org/components/sanity/utils.mjs} (85%) create mode 100644 sites/org/pages/showcase/[slug].mjs create mode 100644 sites/shared/hooks/use-evaled-mdx.mjs diff --git a/.eslintrc.cjs b/.eslintrc.cjs index b6fe3292172..f91c6fb0f0d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -27,6 +27,7 @@ module.exports = { extends: 'eslint:recommended', env: { es2021: true, + node: true, }, // Required when using experimental EcmaScript features parser: '@babel/eslint-parser', diff --git a/sites/org/components/sanity/author.mjs b/sites/org/components/sanity/author.mjs new file mode 100644 index 00000000000..ea79a9e9fea --- /dev/null +++ b/sites/org/components/sanity/author.mjs @@ -0,0 +1,43 @@ +import { SanityMdxWrapper } from './mdx-wrapper.mjs' +import { useTranslation } from 'next-i18next' + +export const Author = ({ author = {} }) => { + const { t } = useTranslation(['posts']) + return ( +
+
+ +
+ +
+ {author.displayname} +
+
+

+

+ +
+
+
+ ) +} diff --git a/sites/org/components/sanity/mdx-wrapper.mjs b/sites/org/components/sanity/mdx-wrapper.mjs new file mode 100644 index 00000000000..82350d5e937 --- /dev/null +++ b/sites/org/components/sanity/mdx-wrapper.mjs @@ -0,0 +1,29 @@ +import { compile, run } from '@mdx-js/mdx' +import * as runtime from 'react/jsx-runtime' // Production. +import { useState, useEffect } from 'react' + +import { PlainMdxWrapper } from 'shared/components/wrappers/mdx.mjs' + +export const useEvaledMdx = (mdxStr = '') => { + const [mdxModule, setMdxModule] = useState(false) + + useEffect(() => { + ;(async () => { + const code = await compile(mdxStr, { + outputFormat: 'function-body', + development: false, + }) + const evaled = await run(code, runtime) + setMdxModule(() => evaled.default) + })() + }, [mdxStr]) + + return mdxModule +} + +export const MdxEvalWrapper = ({ MDX = false, components = {}, site = 'org' }) => { + const evaled = useEvaledMdx(MDX) + return +} + +export const SanityMdxWrapper = MdxEvalWrapper diff --git a/sites/org/components/sanity/page-wrapper.mjs b/sites/org/components/sanity/page-wrapper.mjs new file mode 100644 index 00000000000..4a4951734c9 --- /dev/null +++ b/sites/org/components/sanity/page-wrapper.mjs @@ -0,0 +1,80 @@ +import Head from 'next/head' +import { PageLink } from 'shared/components/page-link.mjs' +import { Lightbox } from 'shared/components/lightbox.mjs' +import { ImageWrapper } from 'shared/components/wrappers/img.mjs' +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' +import { Author } from './author.mjs' +import { TimeAgo } from 'shared/components/wrappers/mdx.mjs' +import { SanityMdxWrapper } from './mdx-wrapper.mjs' +import { useTranslation } from 'next-i18next' + +export const ns = ['common', 'posts', ...pageNs] + +export const SanityPageWrapper = ({ + post = {}, + author = {}, + page = {}, + namespaces = ['common'], +}) => { + const { t } = useTranslation(namespaces) + return ( + + + + + + + + + + + + + + + + ) +} diff --git a/sites/shared/sanity.mjs b/sites/org/components/sanity/utils.mjs similarity index 85% rename from sites/shared/sanity.mjs rename to sites/org/components/sanity/utils.mjs index 8838d17e4b5..9b43f773ccc 100644 --- a/sites/shared/sanity.mjs +++ b/sites/org/components/sanity/utils.mjs @@ -3,7 +3,7 @@ import { createClient, groq } from 'next-sanity' const sanityId = process.env.SANITY_PROJECT || 'hl5bw8cj' let sanityClient -export const sanityLoader = ({ query, language, type, slug }) => { +export const sanityLoader = ({ query, language, type, slug, order }) => { sanityClient = sanityClient || createClient({ @@ -20,6 +20,10 @@ export const sanityLoader = ({ query, language, type, slug }) => { query += ']' } + if (order) { + query += ` | order(${order})` + } + return sanityClient.fetch(query) } diff --git a/sites/org/pages/blog/[slug].mjs b/sites/org/pages/blog/[slug].mjs index a4673379728..ac44335fabb 100644 --- a/sites/org/pages/blog/[slug].mjs +++ b/sites/org/pages/blog/[slug].mjs @@ -1,113 +1,12 @@ -import { useState, useEffect } from 'react' -import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' -import { PlainMdxWrapper, TimeAgo } from 'shared/components/wrappers/mdx.mjs' -import ReactMarkdown from 'react-markdown' -import Head from 'next/head' -import { Lightbox } from 'shared/components/lightbox.mjs' -import { ImageWrapper } from 'shared/components/wrappers/img.mjs' +import { SanityPageWrapper, ns as sanityNs } from 'site/components/sanity/page-wrapper.mjs' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { sanityLoader, sanityImage } from 'site/components/sanity/utils.mjs' import { useTranslation } from 'next-i18next' -import { sanityLoader, sanityImage } from 'shared/strapi/loader.js' -import { strapiImage } from 'shared/utils.mjs' -const strapi = 'https://posts.freesewing.org' +const namespaces = [...sanityNs] -const Author = ({ author }) => { - return ( -
-
- -
- -
- {author?.displayname} -
-
-

- {author?.displayname} - Wrote this -

-
- - {author.about} - -
-
-
- ) -} - -const BlogPostPage = ({ post, author }) => { - const { t } = useTranslation(['common']) - return ( - - - - - - - - - - - - - - - - ) +const BlogPostPage = (props) => { + return } /* @@ -122,7 +21,7 @@ const BlogPostPage = ({ post, author }) => { */ export async function getStaticProps({ params, locale }) { const { slug } = params - const post = await sanityLoader({ type: 'blog', language: locale, slug, withImage: true }) + const post = await sanityLoader({ type: 'blog', language: locale, slug }) .then((data) => data[0]) .catch((err) => console.log(err)) @@ -144,7 +43,7 @@ export async function getStaticProps({ params, locale }) { // image: strapiImage(post.author.picture, ['small']), // about: post.author.about, }, - ...(await serverSideTranslations(locale)), + ...(await serverSideTranslations(locale, namespaces)), }, } } diff --git a/sites/org/pages/blog/index.mjs b/sites/org/pages/blog/index.mjs index ac62e259cf1..ba6b5b2a2c9 100644 --- a/sites/org/pages/blog/index.mjs +++ b/sites/org/pages/blog/index.mjs @@ -1,6 +1,6 @@ // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import { sanityLoader, sanityImage } from 'shared/sanity.mjs' +import { sanityLoader, sanityImage } from 'site/components/sanity/utils.mjs' // Hooks import { useTranslation } from 'next-i18next' // Components diff --git a/sites/org/pages/showcase/[slug].mjs b/sites/org/pages/showcase/[slug].mjs new file mode 100644 index 00000000000..550eba68162 --- /dev/null +++ b/sites/org/pages/showcase/[slug].mjs @@ -0,0 +1,72 @@ +import { SanityPageWrapper, ns as sanityNs } from 'site/components/sanity/page-wrapper.mjs' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { sanityLoader, sanityImage } from 'site/components/sanity/utils.mjs' +import { useTranslation } from 'next-i18next' + +const namespaces = [...sanityNs] + +const ShowcasePage = (props) => { + return +} + +/* + * getStaticProps() is used to fetch data at build-time. + * + * On this page, it is loading the showcase content from strapi. + * + * This, in combination with getStaticPaths() below means this + * page will be used to render/generate all showcase content. + * + * To learn more, see: https://nextjs.org/docs/basic-features/data-fetching + */ +export async function getStaticProps({ params, locale }) { + const { slug } = params + const post = await sanityLoader({ type: 'showcase', language: locale, slug }) + .then((data) => data[0]) + .catch((err) => console.log(err)) + + const designs = [post.design1 || null] + if (post.design2 && post.design2.length > 2) designs.push(post.design2) + if (post.design3 && post.design3.length > 2) designs.push(post.design3) + + return { + props: { + post: { + slug, + body: post.body, + title: post.title, + date: post.date, + caption: post.caption, + image: sanityImage(post.image[0]), + designs, + }, + // FIXME load the author separately + author: { + displayname: post.maker, + // slug: post.maker.slug, + // image: strapiImage(post.maker.picture, ['small']), + // ...(await mdxCompiler(post.maker.about)), + }, + ...(await serverSideTranslations(locale, namespaces)), + }, + } +} + +export const getStaticPaths = async () => { + const paths = await sanityLoader({ language: 'en', type: 'showcase' }) + .then((data) => data.map((post) => `/showcase/${post.slug.current}`)) + .catch((err) => console.log(err)) + + return { + paths: [ + ...paths, + ...paths.map((p) => `/de${p}`), + ...paths.map((p) => `/es${p}`), + ...paths.map((p) => `/fr${p}`), + ...paths.map((p) => `/nl${p}`), + ], + fallback: false, + } +} + +export default ShowcasePage diff --git a/sites/org/pages/showcase/index.mjs b/sites/org/pages/showcase/index.mjs index 8b39fb430d3..9a2e128d518 100644 --- a/sites/org/pages/showcase/index.mjs +++ b/sites/org/pages/showcase/index.mjs @@ -1,34 +1,99 @@ // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { useTranslation } from 'next-i18next' +import { useMemo } from 'react' +import { sanityLoader, sanityImage } from 'site/components/sanity/utils.mjs' // Components import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' -import { V3Wip } from 'shared/components/v3-wip.mjs' +import Link from 'next/link' +import { TimeAgo } from 'shared/components/wrappers/mdx.mjs' // Translation namespaces used on this page -const namespaces = [...new Set(['showcase', ...pageNs])] +const namespaces = [...new Set(['common', 'designs', ...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 DesignsPage = ({ page }) => ( - -
- -
-
+export const PreviewTile = ({ img, slug, title }) => ( + + +

{title}

+ ) -export default DesignsPage +// const DesignPosts = ({ design, posts }) => { +// const { t } = useTranslation(['patterns']) +// return ( +//
+//

+// +// {t(`${design}.t`)} +// +//

+ +//
+// ) +// } + +// FIXME paginate +const Posts = ({ posts }) => ( +
+ {posts.map((post) => ( + + ))} +
+) + +const ShowcaseIndexPage = (props) => { + const { t } = useTranslation() + const { posts } = props + // const designKeys = useMemo(() => Object.keys(designs).sort(), [designs]) + return ( + + + + ) +} + +export default ShowcaseIndexPage export async function getStaticProps({ locale }) { + const posts = await sanityLoader({ + language: locale, + type: 'showcase', + order: 'date desc', + }).catch((err) => console.log(err)) + + const designs = {} + const propPosts = [] + posts.forEach((post) => { + // for (const design of post.designs) { + // if (typeof designs[design] === 'undefined') designs[design] = [] + // designs[design].push(post) + // } + + propPosts.push({ + slug: post.slug.current, + title: post.title, + date: post.date, + // FIXME get the authors separately + author: post.maker, + image: sanityImage(post.image[0]) + '?fit=clip&w=400', + }) + }) + return { props: { + posts: propPosts, + designs, ...(await serverSideTranslations(locale, namespaces)), page: { locale, + // title: 'Freesewing Blog', path: ['showcase'], }, }, diff --git a/sites/shared/components/wrappers/mdx.mjs b/sites/shared/components/wrappers/mdx.mjs index 20087f954b3..18634583c6c 100644 --- a/sites/shared/components/wrappers/mdx.mjs +++ b/sites/shared/components/wrappers/mdx.mjs @@ -102,20 +102,20 @@ const MetaData = ({ authors = [], maintainers = [], updated = '20220825', locale ) -export const PlainMdxWrapper = ({ MDX = false, components = {}, compile, children }) => { - const allComponents = { ...baseComponents, ...components } - const compiledMdx = MDX ? ( - - ) : compile ? ( - {children} - ) : ( - children - ) +export const PlainMdxWrapper = ({ MDX = false, components = {}, children, site = 'org' }) => { + const allComponents = { ...baseComponents(site), ...components } + const CompiledMdx = MDX ? : children || '' - return
{compiledMdx}
+ return
{MDX ? : children}
} -export const MdxWrapper = ({ MDX = false, frontmatter = {}, components = {}, children = [] }) => { +export const MdxWrapper = ({ + MDX = false, + frontmatter = {}, + components = {}, + children = [], + site = 'org', +}) => { const { t } = useTranslation('docs') const { locale, slug } = useContext(NavigationContext) diff --git a/sites/shared/hooks/use-evaled-mdx.mjs b/sites/shared/hooks/use-evaled-mdx.mjs new file mode 100644 index 00000000000..09c8ebe89bb --- /dev/null +++ b/sites/shared/hooks/use-evaled-mdx.mjs @@ -0,0 +1,21 @@ +import { compile, run } from '@mdx-js/mdx' +import * as runtime from 'react/jsx-runtime' // Production. + +import { useState, useEffect, Fragment } from 'react' + +export const useEvaledMdx = (mdxStr = '') => { + const [mdxModule, setMdxModule] = useState(false) + + useEffect(() => { + ;(async () => { + const code = await compile(mdxStr, { + outputFormat: 'function-body', + development: false, + }) + const evaled = await run(code, runtime) + setMdxModule(() => evaled.default) + })() + }, [mdxStr]) + + return mdxModule +}