diff --git a/app/components/cms-section.tsx b/app/components/cms-section.tsx index aa37b41..6af2d23 100644 --- a/app/components/cms-section.tsx +++ b/app/components/cms-section.tsx @@ -19,6 +19,7 @@ type CmsSectionProps = FooterDataType | SectionDataType; export function CmsSection(props: { data: CmsSectionProps; + dataSanity?: string; encodeDataAttribute?: EncodeDataAttributeCallback; index?: number; type?: CmsSectionType; @@ -50,6 +51,7 @@ export function CmsSection(props: { return Section ? (
diff --git a/app/components/sections-renderer.tsx b/app/components/sections-renderer.tsx new file mode 100644 index 0000000..1ef41ea --- /dev/null +++ b/app/components/sections-renderer.tsx @@ -0,0 +1,79 @@ +import type {EncodeDataAttributeCallback} from '@sanity/react-loader'; +import type {SanityDocument} from '@sanity/client'; +import type {SectionDataType} from 'types'; + +import {createDataAttribute, useOptimistic} from '@sanity/visual-editing/react'; +import {useMemo} from 'react'; + +import {CmsSection} from './cms-section'; +import {useRootLoaderData} from '~/root'; +import {SANITY_STUDIO_PATH} from '~/sanity/constants'; + +type SectionsRendererProps = { + documentId: string; + documentType: string; + encodeDataAttribute?: EncodeDataAttributeCallback; + sections: SectionDataType[]; +}; + +export function SectionsRenderer(props: SectionsRendererProps) { + const {documentId, documentType, encodeDataAttribute, sections} = props; + const {env, sanityPreviewMode} = useRootLoaderData(); + + const optimisticSections = useOptimistic< + SectionDataType[], + SanityDocument<{sections: Array<{_key: string}>}> + >(sections, (currentSections, action) => { + if (action.id === documentId && action.document?.sections) { + // Reconcile: use the new order from the mutation but keep the + // GROQ-projected data from currentSections. The raw document + // has unresolved portable text/references that can't be rendered. + return action.document.sections.map( + (section) => + currentSections?.find((s) => s._key === section._key) || section, + ) as SectionDataType[]; + } + return currentSections; + }); + + const baseDataAttribute = useMemo( + () => + sanityPreviewMode + ? createDataAttribute({ + baseUrl: SANITY_STUDIO_PATH, + id: documentId, + type: documentType, + projectId: env.PUBLIC_SANITY_STUDIO_PROJECT_ID, + dataset: env.PUBLIC_SANITY_STUDIO_DATASET, + }) + : undefined, + [ + documentId, + documentType, + env.PUBLIC_SANITY_STUDIO_DATASET, + env.PUBLIC_SANITY_STUDIO_PROJECT_ID, + sanityPreviewMode, + ], + ); + + if (!optimisticSections || optimisticSections.length === 0) return null; + + return ( +
+ {optimisticSections.map((section, index) => ( + + ))} +
+ ); +} diff --git a/app/routes/($locale).$.tsx b/app/routes/($locale).$.tsx index 1d483d8..c1a8647 100644 --- a/app/routes/($locale).$.tsx +++ b/app/routes/($locale).$.tsx @@ -5,9 +5,9 @@ import type {Route} from './+types/($locale).$'; import {DEFAULT_LOCALE} from 'countries'; -import {CmsSection} from '~/components/cms-section'; import {PAGE_QUERY} from '~/data/sanity/queries'; import {useEncodeDataAttribute} from '~/hooks/use-encode-data-attribute'; +import {SectionsRenderer} from '~/components/sections-renderer'; import {resolveShopifyPromises} from '~/lib/resolve-shopify-promises'; import {getSeoMetaFromMatches} from '~/lib/seo'; @@ -73,16 +73,14 @@ export default function PageRoute({loaderData}: Route.ComponentProps) { const {data} = loaderData.page; const encodeDataAttribute = useEncodeDataAttribute(data ?? {}); - return data?.sections && data.sections.length > 0 - ? data.sections.map((section, index) => ( - - )) - : null; + return data?.sections && data.sections.length > 0 ? ( + + ) : null; } function getPageHandle(args: { diff --git a/app/routes/($locale).collections.$collectionHandle.tsx b/app/routes/($locale).collections.$collectionHandle.tsx index e7b24ee..c6a5c8e 100644 --- a/app/routes/($locale).collections.$collectionHandle.tsx +++ b/app/routes/($locale).collections.$collectionHandle.tsx @@ -7,9 +7,9 @@ import invariant from 'tiny-invariant'; import type {Route} from './+types/($locale).collections.$collectionHandle'; -import {CmsSection} from '~/components/cms-section'; import {COLLECTION_QUERY as CMS_COLLECTION_QUERY} from '~/data/sanity/queries'; import {useEncodeDataAttribute} from '~/hooks/use-encode-data-attribute'; +import {SectionsRenderer} from '~/components/sections-renderer'; import {COLLECTION_QUERY} from '~/data/shopify/queries'; import {mergeRouteModuleMeta} from '~/lib/meta'; import {resolveShopifyPromises} from '~/lib/resolve-shopify-promises'; @@ -88,16 +88,14 @@ export default function Collection({loaderData}: Route.ComponentProps) { return ( <> - {template?.sections && template.sections.length > 0 - ? template.sections.map((section, index) => ( - - )) - : null} + {template?.sections && template.sections.length > 0 ? ( + + ) : null} - {template?.sections && - template.sections.length > 0 && - template.sections.map((section, index) => ( - - ))} + {template?.sections && template.sections.length > 0 && ( + + )}