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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
- uses: actions/setup-node@v6
with:
node-version: lts/*
- run: npm ci
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
if: ${{ github.ref_name == 'main' }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
- uses: actions/setup-node@v6
with:
node-version: lts/*
- run: npm ci --ignore-scripts --only-dev
Expand Down
26 changes: 23 additions & 3 deletions app/(personal)/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {sanityFetch} from '@/sanity/lib/live'
import {pagesBySlugQuery, slugsByTypeQuery} from '@/sanity/lib/queries'
import type {Metadata, ResolvingMetadata} from 'next'
import {toPlainText, type PortableTextBlock} from 'next-sanity'
import {draftMode} from 'next/headers'
import {cookies, draftMode} from 'next/headers'
import {notFound} from 'next/navigation'
import {resolvePerspectiveFromCookies} from 'next-sanity/experimental/live'
import type {PagesBySlugQueryResult} from '@/sanity.types'

type Props = {
params: Promise<{slug: string}>
Expand All @@ -15,9 +17,13 @@ export async function generateMetadata(
{params}: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
const isDraftMode = (await draftMode()).isEnabled
const {data: page} = await sanityFetch({
query: pagesBySlugQuery,
params,
perspective: isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published',
stega: false,
})

Expand All @@ -38,13 +44,27 @@ export async function generateStaticParams() {
}

export default async function PageSlugRoute({params}: Props) {
const {data} = await sanityFetch({query: pagesBySlugQuery, params})
const isDraftMode = (await draftMode()).isEnabled
const {data} = await sanityFetch({
query: pagesBySlugQuery,
params,
perspective: isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published',
stega: isDraftMode,
})

// Only show the 404 page if we're in production, when in draft mode we might be about to create a page on this slug, and live reload won't work on the 404 route
if (!data?._id && !(await draftMode()).isEnabled) {
if (!data?._id && !isDraftMode) {
notFound()
}

return <CachedPageSlugRoute data={data} />
}

async function CachedPageSlugRoute({data}: {data: PagesBySlugQueryResult}) {
'use cache'

const {body, overview, title} = data ?? {}

return (
Expand Down
2 changes: 1 addition & 1 deletion app/(personal)/client-functions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import {isCorsOriginError} from 'next-sanity/live'
import {isCorsOriginError} from 'next-sanity'
import {toast} from 'sonner'

export function handleError(error: unknown) {
Expand Down
27 changes: 16 additions & 11 deletions app/(personal)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import '@/styles/index.css'
import {CustomPortableText} from '@/components/CustomPortableText'
import {Navbar} from '@/components/Navbar'
import IntroTemplate from '@/intro-template'
import {sanityFetch, SanityLive} from '@/sanity/lib/live'
import {homePageQuery, settingsQuery} from '@/sanity/lib/queries'
import {urlForOpenGraphImage} from '@/sanity/lib/utils'
import type {Metadata, Viewport} from 'next'
import {toPlainText, type PortableTextBlock} from 'next-sanity'
import {VisualEditing} from 'next-sanity/visual-editing'
import {draftMode} from 'next/headers'
import {Suspense} from 'react'
import {cookies, draftMode} from 'next/headers'
import {Toaster} from 'sonner'
import {handleError} from './client-functions'
import {DraftModeToast} from './DraftModeToast'
import {SpeedInsights} from '@vercel/speed-insights/next'
import {resolvePerspectiveFromCookies} from 'next-sanity/experimental/live'

export async function generateMetadata(): Promise<Metadata> {
const isDraftMode = (await draftMode()).isEnabled
const perspective = isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published'
const [{data: settings}, {data: homePage}] = await Promise.all([
sanityFetch({query: settingsQuery, stega: false}),
sanityFetch({query: homePageQuery, stega: false}),
sanityFetch({query: settingsQuery, perspective}),
sanityFetch({query: homePageQuery, perspective}),
])

const ogImage = urlForOpenGraphImage(
Expand All @@ -43,8 +46,13 @@ export const viewport: Viewport = {
themeColor: '#000',
}

export default async function IndexRoute({children}: {children: React.ReactNode}) {
const {data} = await sanityFetch({query: settingsQuery})
export default async function PersonalLayout({children}: {children: React.ReactNode}) {
const isDraftMode = (await draftMode()).isEnabled
const perspective = isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published'
const {data} = await sanityFetch({query: settingsQuery, perspective, stega: isDraftMode})

return (
<>
<div className="flex min-h-screen flex-col bg-white text-black">
Expand All @@ -61,13 +69,10 @@ export default async function IndexRoute({children}: {children: React.ReactNode}
/>
)}
</footer>
<Suspense>
<IntroTemplate />
</Suspense>
</div>
<Toaster />
<SanityLive onError={handleError} />
{(await draftMode()).isEnabled && (
{isDraftMode && (
<>
<DraftModeToast />
<VisualEditing />
Expand Down
42 changes: 25 additions & 17 deletions app/(personal)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import {HomePage} from '@/components/HomePage'
import {studioUrl} from '@/sanity/lib/api'
import IntroTemplate from '@/intro-template'
import {sanityFetch} from '@/sanity/lib/live'
import {homePageQuery} from '@/sanity/lib/queries'
import Link from 'next/link'
import {resolvePerspectiveFromCookies} from 'next-sanity/experimental/live'
import {cookies, draftMode} from 'next/headers'
import {Suspense} from 'react'

export default async function IndexRoute() {
const {data} = await sanityFetch({query: homePageQuery})
export default async function IndexRoute({
searchParams,
}: {
searchParams: Promise<{[key: string]: string | string[] | undefined}>
}) {
const isDraftMode = (await draftMode()).isEnabled
const {data} = await sanityFetch({
query: homePageQuery,
perspective: isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published',
stega: isDraftMode,
})

if (!data) {
return (
<div className="text-center">
You don&rsquo;t have a homepage yet,{' '}
<Link href={`${studioUrl}/structure/home`} className="underline">
create one now
</Link>
!
</div>
)
}

return <HomePage data={data} />
return (
<>
<HomePage data={data} />
<Suspense>
<IntroTemplate searchParams={searchParams} />
</Suspense>
</>
)
}
26 changes: 23 additions & 3 deletions app/(personal)/projects/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {projectBySlugQuery, slugsByTypeQuery} from '@/sanity/lib/queries'
import {urlForOpenGraphImage} from '@/sanity/lib/utils'
import type {Metadata, ResolvingMetadata} from 'next'
import {createDataAttribute, toPlainText} from 'next-sanity'
import {draftMode} from 'next/headers'
import {cookies, draftMode} from 'next/headers'
import Link from 'next/link'
import {notFound} from 'next/navigation'
import {resolvePerspectiveFromCookies} from 'next-sanity/experimental/live'
import type {ProjectBySlugQueryResult} from '@/sanity.types'

type Props = {
params: Promise<{slug: string}>
Expand All @@ -19,9 +21,13 @@ export async function generateMetadata(
{params}: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
const isDraftMode = (await draftMode()).isEnabled
const {data: project} = await sanityFetch({
query: projectBySlugQuery,
params,
perspective: isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published',
stega: false,
})
const ogImage = urlForOpenGraphImage(
Expand Down Expand Up @@ -51,13 +57,27 @@ export async function generateStaticParams() {
}

export default async function ProjectSlugRoute({params}: Props) {
const {data} = await sanityFetch({query: projectBySlugQuery, params})
const isDraftMode = (await draftMode()).isEnabled
const {data} = await sanityFetch({
query: projectBySlugQuery,
params,
perspective: isDraftMode
? await resolvePerspectiveFromCookies({cookies: await cookies()})
: 'published',
stega: isDraftMode,
})

// Only show the 404 page if we're in production, when in draft mode we might be about to create a project on this slug, and live reload won't work on the 404 route
if (!data?._id && !(await draftMode()).isEnabled) {
if (!data?._id && !isDraftMode) {
notFound()
}

return <CachedProjectSlugRoute data={data} />
}

async function CachedProjectSlugRoute({data}: {data: ProjectBySlugQueryResult}) {
'use cache'

const dataAttribute =
data?._id && data._type
? createDataAttribute({
Expand Down
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const mono = IBM_Plex_Mono({
weight: ['500', '700'],
})

export default async function RootLayout({children}: {children: React.ReactNode}) {
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en" className={`${mono.variable} ${sans.variable} ${serif.variable}`}>
<body>{children}</body>
Expand Down
10 changes: 6 additions & 4 deletions app/studio/[[...index]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This route is responsible for the built-in authoring environment using Sanity Studio v3.
* This route is responsible for the built-in authoring environment using Sanity Studio v4.
* 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
*
Expand All @@ -10,10 +10,12 @@
import config from '@/sanity.config'
import {NextStudio} from 'next-sanity/studio'

export const dynamic = 'force-static'

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

export default function StudioPage() {
return <NextStudio config={config} />
return (
<>
<NextStudio config={config} />
</>
)
}
16 changes: 14 additions & 2 deletions components/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,21 @@ export interface HomePageProps {
data: HomePageQueryResult | null
}

export async function HomePage({data}: HomePageProps) {
export function HomePage({data}: HomePageProps) {
if (!data) {
return (
<div className="text-center">
You don&rsquo;t have a homepage yet,{' '}
<Link href={`${studioUrl}/structure/home`} className="underline">
create one now
</Link>
!
</div>
)
}

// Default to an empty object to allow previews on non-existent documents
const {overview = [], showcaseProjects = [], title = ''} = data ?? {}
const {overview = [], showcaseProjects = [], title = ''} = data

const dataAttribute =
data?._id && data?._type
Expand Down
46 changes: 3 additions & 43 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,47 +1,7 @@
import {FlatCompat} from '@eslint/eslintrc'
import reactCompiler from 'eslint-plugin-react-compiler'
import {defineConfig} from 'eslint/config'

const compat = new FlatCompat({
// import.meta.dirname is available after Node.js v20.11.0
baseDirectory: import.meta.dirname,
})
import reactHooks from 'eslint-plugin-react-hooks'

export default defineConfig([
...compat.config({
ignorePatterns: ['.next/', 'next-env.d.ts', 'public/', 'node_modules/'],
extends: ['next/core-web-vitals', 'next/typescript'],
rules: {
// A bit too strict
'@typescript-eslint/no-explicit-any': 'off',
// Should always error on deps issues
'react-hooks/exhaustive-deps': 'error',
// Temporarily disable the rule that isn't using react compiler as next.js is still using v5 of eslint-plugin-react-hooks
'react-hooks/rules-of-hooks': 'off',
},
}),
{
// Setup react compiler using the react-hooks plugin RC
plugins: {
'react-compiler': reactCompiler,
},
rules: {
'react-compiler/rules-of-hooks': 'error',
'react-compiler/no-unused-directives': 'error',
'react-compiler/static-components': 'error',
'react-compiler/use-memo': 'error',
'react-compiler/component-hook-factories': 'error',
'react-compiler/preserve-manual-memoization': 'error',
'react-compiler/immutability': 'error',
'react-compiler/globals': 'error',
'react-compiler/refs': 'error',
'react-compiler/set-state-in-effect': 'error',
'react-compiler/error-boundaries': 'error',
'react-compiler/purity': 'error',
'react-compiler/set-state-in-render': 'error',
'react-compiler/unsupported-syntax': 'error',
'react-compiler/config': 'error',
'react-compiler/gating': 'error',
},
},
{ignores: ['.next/', 'next-env.d.ts', 'public/', 'node_modules/']},
reactHooks.configs.flat.recommended,
])
Loading