Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refacorting to new Visual Editing API (Do not merge yet) #66

Closed
wants to merge 1 commit into from
Closed
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
7 changes: 7 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
/** @type {import('next').NextConfig} */
const config = {
experimental: {
// Used to guard against accidentally leaking SANITY_API_READ_TOKEN to the browser
taint: true,
},
logging: {
fetches: { fullUrl: false },
},
images: { remotePatterns: [{ hostname: 'cdn.sanity.io' }] },
}

Expand Down
1,924 changes: 1,178 additions & 746 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 18 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,29 @@
},
"dependencies": {
"@portabletext/react": "3.0.11",
"@sanity/client": "6.12.3",
"@sanity/demo": "1.0.2",
"@sanity/vision": "3.28.0",
"@sanity/client": "^6.15.3",
"@sanity/demo": "^2.0.0",
"@sanity/vision": "^3.32.0",
"@tailwindcss/typography": "0.5.10",
"next": "14.1.0",
"next-sanity": "5.5.11",
"next": "^14.1.3",
"next-sanity": "^8.3.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"sanity": "3.28.0",
"sanity-plugin-iframe-pane": "2.6.1",
"styled-components": "6.1.8"
"sanity": "^3.32.0",
"styled-components": "^6.1.8"
},
"devDependencies": {
"@types/react": "18.2.55",
"autoprefixer": "10.4.17",
"eslint": "8.56.0",
"eslint-config-next": "14.1.0",
"eslint-plugin-simple-import-sort": "10.0.0",
"postcss": "8.4.35",
"prettier": "3.2.5",
"prettier-plugin-packagejson": "2.4.10",
"prettier-plugin-tailwindcss": "0.5.11",
"tailwindcss": "3.4.1",
"typescript": "5.3.3"
"@types/react": "^18.2.64",
"autoprefixer": "^10.4.18",
"eslint": "^8.57.0",
"eslint-config-next": "^14.1.3",
"eslint-plugin-simple-import-sort": "^12.0.0",
"postcss": "^8.4.35",
"prettier": "^3.2.5",
"prettier-plugin-packagejson": "^2.4.12",
"prettier-plugin-tailwindcss": "^0.5.12",
"tailwindcss": "^3.4.1",
"typescript": "5.4.2"
},
"engines": {
"node": ">=16"
Expand Down
46 changes: 10 additions & 36 deletions sanity.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
'use client'
/**
* This config is used to set up Sanity Studio that's mounted on the `/pages/studio/[[...index]].tsx` route
*/

import { visionTool } from '@sanity/vision'
import { defineConfig } from 'sanity'
import { deskTool } from 'sanity/desk'
import {
defineUrlResolver,
Iframe,
IframeOptions,
} from 'sanity-plugin-iframe-pane'
import { previewUrl } from 'sanity-plugin-iframe-pane/preview-url'
import { presentationTool } from 'sanity/presentation'
import { structureTool } from 'sanity/structure'

// see https://www.sanity.io/docs/api-versioning for how versioning works
import {
Expand All @@ -19,16 +14,9 @@
previewSecretId,
projectId,
} from '~/lib/sanity.api'
import { schema } from '~/schemas'
import { schema } from '~/schema'

const iframeOptions = {
url: defineUrlResolver({
base: '/api/draft',
requiresSlug: ['post'],
}),
urlSecretId: previewSecretId,
reload: { button: true },
} satisfies IframeOptions
import { studioUrl } from './src/lib/sanity.api'

export default defineConfig({
basePath: '/studio',
Expand All @@ -39,27 +27,13 @@
//edit schemas in './src/schemas'
schema,
plugins: [
deskTool({
// `defaultDocumentNode` is responsible for adding a “Preview” tab to the document pane
// You can add any React component to `S.view.component` and it will be rendered in the pane
// and have access to content in the form in real-time.
// It's part of the Studio's “Structure Builder API” and is documented here:
// https://www.sanity.io/docs/structure-builder-reference
defaultDocumentNode: (S, { schemaType }) => {
return S.document().views([
// Default form view
S.view.form(),
// Preview
S.view.component(Iframe).options(iframeOptions).title('Preview'),
])
},
presentationTool({
previewUrl: { previewMode: '/api/draft' },

Check failure on line 31 in sanity.config.ts

View workflow job for this annotation

GitHub Actions / build

Type 'string' is not assignable to type '{ enable: string; check?: string; disable?: string; }'.
}),
// Add the "Open preview" action
previewUrl({
base: '/api/draft',
requiresSlug: ['post'],
urlSecretId: previewSecretId,
structureTool({
schema,

Check failure on line 34 in sanity.config.ts

View workflow job for this annotation

GitHub Actions / build

Object literal may only specify known properties, and 'schema' does not exist in type 'StructureToolOptions'.
}),

// Vision lets you query your content with GROQ in the studio
// https://www.sanity.io/docs/the-vision-plugin
visionTool({ defaultApiVersion: apiVersion }),
Expand Down
27 changes: 27 additions & 0 deletions src/app/api/draft/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* This file is used to allow Presentation to set the app in Draft Mode, which will load Visual Editing
* and query draft content and preview the content as it will appear once everything is published
*/

import { validatePreviewUrl } from '@sanity/preview-url-secret'
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

import { client } from '@/lib/sanity.client'

Check failure on line 10 in src/app/api/draft/route.ts

View workflow job for this annotation

GitHub Actions / build

Cannot find module '@/lib/sanity.client' or its corresponding type declarations.
import { token } from '@/lib/sanity.token'

Check failure on line 11 in src/app/api/draft/route.ts

View workflow job for this annotation

GitHub Actions / build

Cannot find module '@/lib/sanity.token' or its corresponding type declarations.

const clientWithToken = client.withConfig({ token })

export async function GET(request: Request) {
const { isValid, redirectTo = '/' } = await validatePreviewUrl(
clientWithToken,
request.url,
)
if (!isValid) {
return new Response('Invalid secret', { status: 401 })
}

draftMode().enable()

redirect(redirectTo)
}
14 changes: 14 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import '~/styles/global.css'

import {draftMode} from 'next/headers'
import { VisualEditing } from 'next-sanity'

export default function RootLayout({children}: {children: React.ReactNode}) {

return (
<>
<main>{children}</main>
{draftMode().isEnabled && <VisualEditing />}
</>
)
}
27 changes: 27 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { GetStaticProps, InferGetStaticPropsType } from 'next'
import { draftMode } from 'next/headers'
import { LiveQueryProvider, useLiveQuery } from 'next-sanity/preview'

import type { SharedPageProps } from '~/app/layout'

Check failure on line 5 in src/app/page.tsx

View workflow job for this annotation

GitHub Actions / build

Module '"~/app/layout"' has no exported member 'SharedPageProps'. Did you mean to use 'import SharedPageProps from "~/app/layout"' instead?
import Card from '~/components/Card'
import Container from '~/components/Container'
import Welcome from '~/components/Welcome'
import { sanityFetch } from '~/lib/sanity.fetch'
import { getPosts, type Post, postsQuery } from '~/lib/sanity.queries'

Check failure on line 10 in src/app/page.tsx

View workflow job for this annotation

GitHub Actions / build

Module '"~/lib/sanity.queries"' has no exported member 'getPosts'.


export default async function IndexPage() {
const posts = await sanityFetch<Post[]>({query: postsQuery})

return (
<Container>
<section>
{posts.length ? (
posts.map((post) => <Card key={post._id} post={post} />)
) : (
<Welcome />
)}
</section>
</Container>
)
}
56 changes: 16 additions & 40 deletions src/pages/post/[slug].tsx → src/app/post/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,41 @@
import { PortableText } from '@portabletext/react'
import type { GetStaticProps, InferGetStaticPropsType } from 'next'
import Image from 'next/image'
import { useLiveQuery } from 'next-sanity/preview'
import { notFound } from 'next/navigation'

import type { SharedPageProps } from '~/app/layout'

Check failure on line 6 in src/app/post/[slug]/page.tsx

View workflow job for this annotation

GitHub Actions / build

Module '"~/app/layout"' has no exported member 'SharedPageProps'. Did you mean to use 'import SharedPageProps from "~/app/layout"' instead?
import Container from '~/components/Container'
import { readToken } from '~/lib/sanity.api'
import { getClient } from '~/lib/sanity.client'
import { sanityFetch } from '~/lib/sanity.fetch'
import { urlForImage } from '~/lib/sanity.image'
import {
getPost,

Check failure on line 11 in src/app/post/[slug]/page.tsx

View workflow job for this annotation

GitHub Actions / build

Module '"~/lib/sanity.queries"' has no exported member 'getPost'.
type Post,
postBySlugQuery,
postSlugsQuery,
} from '~/lib/sanity.queries'
import type { SharedPageProps } from '~/pages/_app'
import { formatDate } from '~/utils'

interface Query {
[key: string]: string
}

export const getStaticProps: GetStaticProps<
SharedPageProps & {
post: Post
},
Query
> = async ({ draftMode = false, params = {} }) => {
const client = getClient(draftMode ? { token: readToken } : undefined)
const post = await getPost(client, params.slug)

if (!post) {
return {
notFound: true,
}
}

return {
props: {
draftMode,
token: draftMode ? readToken : '',
post,
},
}
export async function generateStaticParams() {
return sanityFetch({
query: postSlugsQuery,
perspective: 'published',

Check failure on line 26 in src/app/post/[slug]/page.tsx

View workflow job for this annotation

GitHub Actions / build

Type 'string' is not assignable to type 'Omit<ClientPerspective, "raw">'.
stega: false
})
}

export default function ProjectSlugRoute(
props: InferGetStaticPropsType<typeof getStaticProps>,
export default async function PostSlugRoute(
{params}: {params: any}
) {
const [post] = useLiveQuery(props.post, postBySlugQuery, {
slug: props.post.slug.current,
})
const post = await sanityFetch({ query: postBySlugQuery, params, stega: false})

if (!post?._id) {

Check failure on line 36 in src/app/post/[slug]/page.tsx

View workflow job for this annotation

GitHub Actions / build

Property '_id' does not exist on type 'unknown'.
return notFound()
}

return (
<Container>
Expand Down Expand Up @@ -77,13 +63,3 @@
</Container>
)
}

export const getStaticPaths = async () => {
const client = getClient()
const slugs = await client.fetch(postSlugsQuery)

return {
paths: slugs?.map(({ slug }) => `/post/${slug}`) || [],
fallback: 'blocking',
}
}
18 changes: 18 additions & 0 deletions src/app/studio/[[...tool]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* This route is responsible for the built-in authoring environment using Sanity Studio v3.
* All routes under /studio will be handled by this file using Next.js' catch-all routes:
* https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes
*
* You can learn more about the next-sanity package here:
* https://github.com/sanity-io/next-sanity
*/

import { NextStudio } from 'next-sanity/studio'

import config from '@/sanity.config'

export const dynamic = 'force-static'

export default function StudioPage() {
return <NextStudio config={config} />
}
Empty file added src/app/studio/favicon.ico
Empty file.
21 changes: 21 additions & 0 deletions src/app/studio/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Inter } from "next/font/google";

const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
display: "swap",
});

export { metadata, viewport } from "next-sanity/studio";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={inter.variable}>
<body className="min-h-screen">{children}</body>
</html>
);
}
19 changes: 0 additions & 19 deletions src/components/PreviewProvider.tsx

This file was deleted.

12 changes: 8 additions & 4 deletions src/lib/sanity.api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export const useCdn = false

/**
* As this file is reused in several other files, try to keep it lean and small.
* Importing other npm packages here could lead to needlessly increasing the client bundle size, or end up in a server-only function that don't need it.
Expand All @@ -15,8 +13,6 @@ export const projectId = assertValue(
'Missing environment variable: NEXT_PUBLIC_SANITY_PROJECT_ID',
)

export const readToken = process.env.SANITY_API_READ_TOKEN || ''

// see https://www.sanity.io/docs/api-versioning for how versioning works
export const apiVersion =
process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2023-06-21'
Expand All @@ -25,6 +21,14 @@ export const apiVersion =
// The secret protects against unauthorized access to your draft content and have a lifetime of 60 minutes, to protect against bruteforcing.
export const previewSecretId: `${string}.${string}` = 'preview.secret'

/**
* Used to configure edit intent links, for Presentation Mode, as well as to configure where the Studio is mounted in the router.
*/
export const studioUrl = '/studio'

/**
* Helper function to throw an error if necessary environment variables haven't been set.
*/
function assertValue<T>(v: T | undefined, errorMessage: string): T {
if (v === undefined) {
throw new Error(errorMessage)
Expand Down
Loading