-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(basic-starter): upgrade starters to App Router
Fixes #601
- Loading branch information
Showing
34 changed files
with
497 additions
and
448 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { draftMode } from "next/headers" | ||
import { notFound } from "next/navigation" | ||
import { getDraftData } from "next-drupal/draft" | ||
import { Article } from "@/components/drupal/Article" | ||
import { BasicPage } from "@/components/drupal/BasicPage" | ||
import { drupal } from "@/lib/drupal" | ||
import type { Metadata, ResolvingMetadata } from "next" | ||
import type { DrupalNode, JsonApiParams } from "next-drupal" | ||
|
||
async function getNode(slug: string[]) { | ||
const path = `/${slug.join("/")}` | ||
|
||
const params: JsonApiParams = {} | ||
|
||
const draftData = getDraftData() | ||
|
||
if (draftData.path === path) { | ||
params.resourceVersion = draftData.resourceVersion | ||
} | ||
|
||
// Translating the path also allows us to discover the entity type. | ||
const translatedPath = await drupal.translatePath(path) | ||
|
||
if (!translatedPath) { | ||
throw new Error("Resource not found", { cause: "NotFound" }) | ||
} | ||
|
||
const type = translatedPath.jsonapi?.resourceName! | ||
const uuid = translatedPath.entity.uuid | ||
|
||
if (type === "node--article") { | ||
params.include = "field_image,uid" | ||
} | ||
|
||
const resource = await drupal.getResource<DrupalNode>(type, uuid, { | ||
params, | ||
}) | ||
|
||
if (!resource) { | ||
throw new Error( | ||
`Failed to fetch resource: ${translatedPath?.jsonapi?.individual}`, | ||
{ | ||
cause: "DrupalError", | ||
} | ||
) | ||
} | ||
|
||
return resource | ||
} | ||
|
||
type NodePageParams = { | ||
slug: string[] | ||
} | ||
type NodePageProps = { | ||
params: NodePageParams | ||
searchParams: { [key: string]: string | string[] | undefined } | ||
} | ||
|
||
export async function generateMetadata( | ||
{ params: { slug } }: NodePageProps, | ||
parent: ResolvingMetadata | ||
): Promise<Metadata> { | ||
let node | ||
try { | ||
node = await getNode(slug) | ||
} catch (e) { | ||
// If we fail to fetch the node, don't return any metadata. | ||
return {} | ||
} | ||
|
||
return { | ||
title: node.title, | ||
} | ||
} | ||
|
||
const RESOURCE_TYPES = ["node--page", "node--article"] | ||
|
||
export async function generateStaticParams(): Promise<NodePageParams[]> { | ||
const resources = await drupal.getResourceCollectionPathSegments( | ||
RESOURCE_TYPES, | ||
{ | ||
// The pathPrefix will be removed from the returned path segments array. | ||
// pathPrefix: "/blog", | ||
// The list of locales to return. | ||
// locales: ["en", "es"], | ||
// The default locale. | ||
// defaultLocale: "en", | ||
} | ||
) | ||
|
||
return resources.map((resource) => { | ||
// resources is an array containing objects like: { | ||
// path: "/blog/some-category/a-blog-post", | ||
// type: "node--article", | ||
// locale: "en", // or `undefined` if no `locales` requested. | ||
// segments: ["blog", "some-category", "a-blog-post"], | ||
// } | ||
return { | ||
slug: resource.segments, | ||
} | ||
}) | ||
} | ||
|
||
export default async function NodePage({ | ||
params: { slug }, | ||
searchParams, | ||
}: NodePageProps) { | ||
const isDraftMode = draftMode().isEnabled | ||
|
||
let node | ||
try { | ||
node = await getNode(slug) | ||
} catch (error) { | ||
// If getNode throws an error, tell Next.js the path is 404. | ||
notFound() | ||
} | ||
|
||
// If we're not in draft mode and the resource is not published, return a 404. | ||
if (!isDraftMode && node?.status === false) { | ||
notFound() | ||
} | ||
|
||
return ( | ||
<> | ||
{node.type === "node--page" && <BasicPage node={node} />} | ||
{node.type === "node--article" && <Article node={node} />} | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { disableDraftMode } from "next-drupal/draft" | ||
import type { NextRequest } from "next/server" | ||
|
||
export async function GET(request: NextRequest) { | ||
return disableDraftMode() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { drupal } from "@/lib/drupal" | ||
import { enableDraftMode } from "next-drupal/draft" | ||
import type { NextRequest } from "next/server" | ||
|
||
export async function GET(request: NextRequest): Promise<Response | never> { | ||
return enableDraftMode(request, drupal) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { revalidatePath } from "next/cache" | ||
import type { NextRequest } from "next/server" | ||
|
||
async function handler(request: NextRequest) { | ||
const searchParams = request.nextUrl.searchParams | ||
const path = searchParams.get("path") | ||
const secret = searchParams.get("secret") | ||
|
||
// Validate secret. | ||
if (secret !== process.env.DRUPAL_REVALIDATE_SECRET) { | ||
return new Response("Invalid secret.", { status: 401 }) | ||
} | ||
|
||
// Validate path. | ||
if (!path) { | ||
return new Response("Invalid path.", { status: 400 }) | ||
} | ||
|
||
try { | ||
revalidatePath(path) | ||
|
||
return new Response("Revalidated.") | ||
} catch (error) { | ||
return new Response((error as Error).message, { status: 500 }) | ||
} | ||
} | ||
|
||
export { handler as GET, handler as POST } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { DraftAlert } from "@/components/misc/DraftAlert" | ||
import { HeaderNav } from "@/components/navigation/HeaderNav" | ||
import type { Metadata } from "next" | ||
import type { ReactNode } from "react" | ||
|
||
import "@/styles/globals.css" | ||
|
||
export const metadata: Metadata = { | ||
title: { | ||
default: "Next.js for Drupal", | ||
template: "%s | Next.js for Drupal", | ||
}, | ||
description: "A Next.js site powered by a Drupal backend.", | ||
icons: { | ||
icon: "/favicon.ico", | ||
}, | ||
} | ||
|
||
export default function RootLayout({ | ||
// Layouts must accept a children prop. | ||
// This will be populated with nested layouts or pages | ||
children, | ||
}: { | ||
children: ReactNode | ||
}) { | ||
return ( | ||
<html lang="en"> | ||
<body> | ||
<DraftAlert /> | ||
<div className="max-w-screen-md px-6 mx-auto"> | ||
<HeaderNav /> | ||
<main className="container py-10 mx-auto">{children}</main> | ||
</div> | ||
</body> | ||
</html> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
38 changes: 38 additions & 0 deletions
38
starters/basic-starter/components/misc/DraftAlert/Client.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
"use client" | ||
|
||
import { useEffect, useState } from "react" | ||
|
||
export function DraftAlertClient({ | ||
isDraftEnabled, | ||
}: { | ||
isDraftEnabled: boolean | ||
}) { | ||
const [showDraftAlert, setShowDraftAlert] = useState<boolean>(false) | ||
|
||
useEffect(() => { | ||
setShowDraftAlert(isDraftEnabled && window.top === window.self) | ||
}, [isDraftEnabled]) | ||
|
||
if (!showDraftAlert) { | ||
return null | ||
} | ||
|
||
function buttonHandler() { | ||
void fetch("/api/disable-draft") | ||
setShowDraftAlert(false) | ||
} | ||
|
||
return ( | ||
<div className="sticky top-0 left-0 z-50 w-full px-2 py-1 text-center text-white bg-black"> | ||
<p className="mb-0"> | ||
This page is a draft. | ||
<button | ||
className="inline-block ml-3 rounded border px-1.5 hover:bg-white hover:text-black active:bg-gray-200 active:text-gray-500" | ||
onClick={buttonHandler} | ||
> | ||
Exit draft mode | ||
</button> | ||
</p> | ||
</div> | ||
) | ||
} |
13 changes: 13 additions & 0 deletions
13
starters/basic-starter/components/misc/DraftAlert/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Suspense } from "react" | ||
import { draftMode } from "next/headers" | ||
import { DraftAlertClient } from "./Client" | ||
|
||
export function DraftAlert() { | ||
const isDraftEnabled = draftMode().isEnabled | ||
|
||
return ( | ||
<Suspense fallback={null}> | ||
<DraftAlertClient isDraftEnabled={isDraftEnabled} /> | ||
</Suspense> | ||
) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.