Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions apps/live-next/sanity.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* ---------------------------------------------------------------------------------
*/

export declare const internalGroqTypeReferenceTo: unique symbol

// Source: node_modules/@repo/sanity-extracted-schema/live-demo.json
export type SanityImageAssetReference = {
_ref: string
Expand Down Expand Up @@ -465,8 +467,6 @@ export type AllSanitySchemaTypes =
| SanityImageAsset
| Geopoint

export declare const internalGroqTypeReferenceTo: unique symbol

// Source: app/posts/[slug]/OptimisticPostContent.tsx
// Variable: OPTIMISTIC_POST_CONTENT_QUERY
// Query: *[_id == $id && _type == "post"][0].content
Expand Down Expand Up @@ -596,8 +596,8 @@ export type HeroQueryResult = {
author: {
_id: string
_originalId: null
projectId: unknown
dataset: unknown
projectId: string | null
dataset: string | null
name: string | 'Anonymous'
picture: {
asset?: SanityImageAssetReference
Expand Down Expand Up @@ -631,8 +631,8 @@ export type MoreStoriesQueryResult = Array<{
author: {
_id: string
_originalId: null
projectId: unknown
dataset: unknown
projectId: string | null
dataset: string | null
name: string | 'Anonymous'
picture: {
asset?: SanityImageAssetReference
Expand Down Expand Up @@ -684,8 +684,8 @@ export type PostQueryResult = {
author: {
_id: string
_originalId: null
projectId: unknown
dataset: unknown
projectId: string | null
dataset: string | null
name: string | 'Anonymous'
picture: {
asset?: SanityImageAssetReference
Expand Down
1 change: 1 addition & 0 deletions apps/page-builder-demo/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {NextConfig} from 'next'

const nextConfig: NextConfig = {
reactCompiler: true,
transpilePackages: ['@repo/page-builder-shared'],
compiler: {
styledComponents: {
displayName: true,
Expand Down
2 changes: 1 addition & 1 deletion apps/page-builder-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
"typegen": "sanity typegen generate"
},
"dependencies": {
"@portabletext/react": "^6.2.0",
"@react-spring/three": "^10.0.3",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.5.0",
"@repo/env": "workspace:*",
"@repo/page-builder-shared": "workspace:*",
"@repo/sanity-extracted-schema": "workspace:*",
"@repo/studio-url": "workspace:*",
"@sanity/client": "catalog:",
Expand Down
3 changes: 1 addition & 2 deletions apps/page-builder-demo/src/app/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
'use client'

import type {LayoutQueryResult} from '@repo/page-builder-shared'
import {CloseIcon} from '@sanity/icons/Close'
import {MenuIcon} from '@sanity/icons/Menu'
import clsx from 'clsx'
import Link from 'next/link'
import {usePathname} from 'next/navigation'
import {useState} from 'react'

import type {LayoutQueryResult} from '@/sanity.types'

const links = [
// {
// href: '/projects',
Expand Down
50 changes: 50 additions & 0 deletions apps/page-builder-demo/src/app/draft-mode-status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client'

import type {ClientPerspective} from '@sanity/client'
import {usePresentationQuery, useVisualEditingEnvironment} from '@sanity/visual-editing/react'
import {defineQuery} from 'next-sanity'
import {ViewTransition} from 'react'

const statusQuery = defineQuery(`*[_id == "siteSettings"][0]{_id}`)

function formatValue(value: unknown): string {
return JSON.stringify(value)
}

export function DraftModeStatus({
perspective,
variant,
}: {
perspective: Exclude<ClientPerspective, 'raw'>
variant: string | null
}) {
const environment = useVisualEditingEnvironment()
const presentation = usePresentationQuery({query: statusQuery, stega: false})
if (environment === null) return null

const displayPerspective = presentation.perspective ?? perspective
const displayVariant = presentation.variant ?? variant

return (
<div className="fixed right-3 bottom-3 z-50 block rounded border border-black/10 bg-white/95 px-2 py-1 text-xs text-black shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/95 dark:text-white">
<p className="will-change-contents">
perspective:{' '}
<ViewTransition>
<code>{formatValue(displayPerspective)}</code>
</ViewTransition>
</p>
<p className="will-change-contents">
variant:{' '}
<ViewTransition>
<code>{formatValue(displayVariant)}</code>
</ViewTransition>
</p>
<p className="will-change-contents">
environment:{' '}
<ViewTransition>
<code>{formatValue(environment)}</code>
</ViewTransition>
</p>
</div>
)
}
60 changes: 46 additions & 14 deletions apps/page-builder-demo/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import './globals.css'
import {layoutQuery} from '@repo/page-builder-shared'
import {validateApiPerspective, type ClientPerspective} from '@sanity/client'
import {perspectiveCookieName, variantCookieName} from '@sanity/preview-url-secret/constants'
import type {Metadata} from 'next'
import {defineQuery} from 'next-sanity'
import {VisualEditing} from 'next-sanity/visual-editing'
import {IBM_Plex_Mono, Inter, Libre_Caslon_Text} from 'next/font/google'
import {draftMode} from 'next/headers'
import {cookies, draftMode} from 'next/headers'

import {plugins} from '@/components/overlay-plugins'
import {components} from '@/components/overlays/resolver'
import {sanityFetch, SanityLive} from '@/sanity/live'

import AlertBanner from './alert-banner'
import {AppLayout} from './AppLayout'
import {DraftModeStatus} from './draft-mode-status'
import {DemoPageBuilderProvider} from './page-builder'

const serif = Libre_Caslon_Text({
variable: '--font-serif',
Expand All @@ -30,13 +34,6 @@ const mono = IBM_Plex_Mono({
weight: ['500', '700'],
})

const layoutQuery = defineQuery(`
*[_id == "siteSettings"][0]{
title,
description,
copyrightText
}`)

export async function generateMetadata(): Promise<Metadata> {
const {data} = await sanityFetch({query: layoutQuery, stega: false})
return {
Expand All @@ -52,16 +49,51 @@ export async function generateMetadata(): Promise<Metadata> {

export default async function RootLayout({children}: {children: React.ReactNode}) {
const {data} = await sanityFetch({query: layoutQuery})
const {isEnabled: isDraftModeEnabled} = await draftMode()
const perspective = isDraftModeEnabled ? await resolvePerspectiveFromCookies() : 'published'
const variant = isDraftModeEnabled ? await resolveVariantFromCookies() : null
return (
<html lang="en" className={`${mono.variable} ${sans.variable} ${serif.variable}`}>
<body className="bg-white text-black dark:bg-black dark:text-white">
{(await draftMode()).isEnabled && <AlertBanner />}
<AppLayout data={data}>{children}</AppLayout>
{(await draftMode()).isEnabled && (
<VisualEditing components={components} plugins={plugins} />
)}
{isDraftModeEnabled && <AlertBanner />}
<DraftModeStatus perspective={perspective} variant={variant} />
<DemoPageBuilderProvider>
<AppLayout data={data}>{children}</AppLayout>
</DemoPageBuilderProvider>
{isDraftModeEnabled && <VisualEditing components={components} plugins={plugins} />}
<SanityLive />
</body>
</html>
)
}

async function resolvePerspectiveFromCookies(): Promise<Exclude<ClientPerspective, 'raw'>> {
const jar = await cookies()
return jar.has(perspectiveCookieName)
? sanitizePerspective(jar.get(perspectiveCookieName)?.value, 'drafts')
: 'drafts'
}

async function resolveVariantFromCookies(): Promise<string | null> {
const jar = await cookies()
return jar.has(variantCookieName) ? (jar.get(variantCookieName)?.value ?? null) : null
}

function sanitizePerspective(
_perspective: unknown,
fallback: 'drafts' | 'published',
): Exclude<ClientPerspective, 'raw'> {
const perspective =
typeof _perspective === 'string' && _perspective.includes(',')
? _perspective.split(',')
: _perspective
try {
validateApiPerspective(perspective)
return perspective === 'raw'
? fallback
: (Array.isArray(perspective) ? perspective.filter(Boolean) : perspective) || fallback
} catch (err) {
console.warn(`Invalid perspective:`, _perspective, perspective, err)
return fallback
}
}
26 changes: 26 additions & 0 deletions apps/page-builder-demo/src/app/page-builder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client'

import {PageBuilderProvider} from '@repo/page-builder-shared'
import Link from 'next/link'
import type {ReactNode} from 'react'

import {Image} from '@/components/image'
import {ProductModel} from '@/components/page/ProductModel'
import {dataAttribute} from '@/sanity/dataAttribute'

/**
* Wires the shared page-builder components up with the Next.js specific
* pieces: next/link, the next/image based Image and the 3D intro model.
*/
export function DemoPageBuilderProvider({children}: {children: ReactNode}) {
return (
<PageBuilderProvider
dataAttribute={dataAttribute}
Link={Link}
Image={Image}
IntroModel={ProductModel}
>
{children}
</PageBuilderProvider>
)
}
15 changes: 1 addition & 14 deletions apps/page-builder-demo/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
import {defineQuery} from 'next-sanity'
import {frontPageQuery, Page} from '@repo/page-builder-shared'

import {Page, sectionFragment} from '@/components/page'
import {sanityFetch} from '@/sanity/live'

const frontPageQuery = defineQuery(`
*[_id == "siteSettings"][0]{
frontPage->{
_type,
_id,
title,
${sectionFragment},
style
}
}.frontPage
`)

export default async function IndexPage() {
const {data} = await sanityFetch({query: frontPageQuery})

Expand Down
16 changes: 2 additions & 14 deletions apps/page-builder-demo/src/app/pages/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
import {defineQuery} from 'next-sanity'
import {Page, pageQuery, pageSlugsQuery} from '@repo/page-builder-shared'

import {Page, sectionFragment} from '@/components/page'
import {sanityFetch} from '@/sanity/live'

const pageQuery = defineQuery(`
*[_type == "page" && slug.current == $slug][0]{
_type,
_id,
title,
${sectionFragment},
style
}
`)

const pageSlugs = defineQuery(`*[_type == "page" && defined(slug.current)]{"slug": slug.current}`)
export async function generateStaticParams() {
const {data} = await sanityFetch({
query: pageSlugs,
query: pageSlugsQuery,
perspective: 'published',
stega: false,
})
Expand Down
8 changes: 1 addition & 7 deletions apps/page-builder-demo/src/app/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import {defineQuery} from 'next-sanity'
import {productPageQuery, productSlugsQuery, SimpleContent} from '@repo/page-builder-shared'

import {SimpleContent} from '@/components/page'
import {Slideshow} from '@/components/slideshow'
import {dataAttribute} from '@/sanity/dataAttribute'
import {sanityFetch} from '@/sanity/live'

const productSlugsQuery = defineQuery(
/* groq */ `*[_type == "product" && defined(slug.current)]{"slug": slug.current}`,
)
export async function generateStaticParams() {
const {data} = await sanityFetch({
query: productSlugsQuery,
Expand All @@ -17,8 +13,6 @@ export async function generateStaticParams() {
return data
}

const productPageQuery = defineQuery(`*[_type == "product" && slug.current == $slug][0]`)

export default async function ProductPage({params}: {params: Promise<{slug: string}>}) {
const {data} = await sanityFetch({
query: productPageQuery,
Expand Down
13 changes: 1 addition & 12 deletions apps/page-builder-demo/src/app/products/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
import {defineQuery} from 'next-sanity'
import {productsPageQuery, SimpleContent} from '@repo/page-builder-shared'
import Link from 'next/link'

import {Image} from '@/components/image'
import {SimpleContent} from '@/components/page'
import {dataAttribute} from '@/sanity/dataAttribute'
import {sanityFetch} from '@/sanity/live'

const productsPageQuery = defineQuery(`
*[_type == "product" && defined(slug.current)]{
_id,
title,
description,
slug,
"media": media[0]
}
`)

export default async function ProductsPage() {
const {data} = await sanityFetch({query: productsPageQuery})

Expand Down
7 changes: 1 addition & 6 deletions apps/page-builder-demo/src/app/project/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {defineQuery} from 'next-sanity'
import {projectPageQuery, projectSlugsQuery} from '@repo/page-builder-shared'

import {sanityFetch} from '@/sanity/live'

Expand All @@ -12,9 +12,6 @@ export interface ProjectPageData {
project: ProjectData | null
}

const projectSlugsQuery = defineQuery(
/* groq */ `*[_type == "project" && defined(slug.current)]{"slug": slug.current}`,
)
export async function generateStaticParams() {
const {data} = await sanityFetch({
query: projectSlugsQuery,
Expand All @@ -24,8 +21,6 @@ export async function generateStaticParams() {
return data
}

const projectPageQuery = defineQuery(`*[_type == "project" && slug.current == $slug][0]`)

export default async function ProjectPage({params}: {params: Promise<{slug: string}>}) {
// @TODO fix typegen vs manual types issues
const {data} = (await sanityFetch({
Expand Down
4 changes: 1 addition & 3 deletions apps/page-builder-demo/src/app/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {defineQuery} from 'next-sanity'
import {projectsPageQuery} from '@repo/page-builder-shared'
import Link from 'next/link'

import {sanityFetch} from '@/sanity/live'

const projectsPageQuery = defineQuery(`*[_type == "project" && defined(slug.current)]`)

export default async function ProjectsPage() {
// @TODO fix typegen vs manual types issues
const {data} = (await sanityFetch({query: projectsPageQuery})) as unknown as {
Expand Down
Loading