Skip to content

Commit

Permalink
feat: migrate to app router (#15)
Browse files Browse the repository at this point in the history
* feat: migrate to app router

* chore: remove export

* fix: format

* fix: formatting
  • Loading branch information
alegemaate authored Feb 10, 2024
1 parent 859c88c commit 8dc3bba
Show file tree
Hide file tree
Showing 34 changed files with 1,798 additions and 2,595 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/check-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
with:
fetch-depth: 0

- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
cache: yarn

- run: yarn install
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ yarn-error.log*
# Sentry
.sentryclirc

public/sitemap*.xml
public/sitemap*.xml
# Sentry Config File
.sentryclirc
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"semi": true,
"importOrder": ["<THIRD_PARTY_MODULES>", "@/", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
"importOrderSortSpecifiers": true,
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
34 changes: 34 additions & 0 deletions app/[sound_id]/audio-player.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client";

import { sendGAEvent } from "@next/third-parties/google";

/* eslint-disable jsx-a11y/media-has-caption */

export interface AudioSource {
ext: string;
mime: string;
url: string;
}

interface AudioPlayerProps {
readonly soundId: string;
readonly sources: AudioSource[];
}

export const AudioPlayer = ({ soundId, sources }: AudioPlayerProps) => (
<audio
autoPlay
controls
onPlay={() => {
sendGAEvent("play_sound", {
value: soundId,
});
}}
>
{sources.map(({ mime, url }) => (
<source key={url} src={url} type={mime} />
))}

<p>Your user agent does not support the HTML5 Audio element.</p>
</audio>
);
54 changes: 54 additions & 0 deletions app/[sound_id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";

import { SAYINGS } from "@/constants/sayings";
import { getSayingData } from "@/lib/sayings";

import { AudioPlayer } from "./audio-player";
import { RandomSayings } from "./random-sayings";

interface PlayPageProps {
readonly params: {
sound_id: string;
};
}

export const generateMetadata = ({ params }: PlayPageProps): Metadata => {
const data = getSayingData(params.sound_id);

if (!data) {
return {};
}

return {
title: `${data.text} - Duke Nukem Says`,
description: `Duke Nukem Says "${data.text}". Play Duke Nukem quotes on Duke Nukem Says!`,
};
};

export const generateStaticParams = () => {
return SAYINGS.map((saying) => ({
sound_id: saying.id,
}));
};

const PlayPage = ({ params }: PlayPageProps) => {
const data = getSayingData(params.sound_id);

if (!data) {
notFound();
}

const { soundId, text, sources } = data;

return (
<>
<h1>Duke Nukem Says...</h1>
<h2>{`"${text}"`}</h2>
<AudioPlayer soundId={soundId} sources={sources} />
<RandomSayings key={soundId} />
</>
);
};

export default PlayPage;
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use client";

import Link from "next/link";
import { useEffect, useState } from "react";

import type { Saying } from "@/constants/sayings";
import { randomSayings } from "@/lib/random-sayings";

import styles from "./RandomSayings.module.css";
import styles from "./random-sayings.module.css";

export const RandomSayings = () => {
const [sayings, setSayings] = useState<Saying[]>([]);
Expand All @@ -19,15 +21,11 @@ export const RandomSayings = () => {
<ul className={styles.list}>
{sayings.map((saying) => (
<li className={styles.list_item} key={saying.id}>
<Link href={`/${saying.id}`}>
<a>{saying.text}</a>
</Link>
<Link href={`/${saying.id}`}>{saying.text}</Link>
</li>
))}
<li className={styles.list_item}>
<Link href="/">
<a>View All</a>
</Link>
<Link href="/">View All</Link>
</li>
</ul>
</section>
Expand Down
25 changes: 25 additions & 0 deletions app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import * as Sentry from "@sentry/nextjs";
import Error from "next/error";
import { useEffect } from "react";

interface GlobalErrorProps {
readonly error: Error;
}

const GlobalError = ({ error }: GlobalErrorProps) => {
useEffect(() => {
Sentry.captureException(error);
}, [error]);

return (
<html lang="en">
<body>
<Error statusCode={undefined as unknown as number} />
</body>
</html>
);
};

export default GlobalError;
4 changes: 3 additions & 1 deletion styles/global.css → app/global.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
body {
font: 18px "Verdana", sans-serif;
font:
18px "Verdana",
sans-serif;
background-color: #000000;
}

Expand Down
File renamed without changes.
65 changes: 65 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable react/no-danger */
import { GoogleAnalytics } from "@next/third-parties/google";
import type { Metadata, Viewport } from "next";
import Image from "next/image";
import Link from "next/link";

import { WEBSITE_INFO } from "@/constants/website-info";

import Logo from "../public/logo.webp";
import "./global.css";
import styles from "./layout.module.css";

export const metadata: Metadata = {
openGraph: {
siteName: "Duke Nukem Says",
type: "website",
images: ["/logo.webp"],
},
authors: [{ name: "Allan Legemaate" }],
manifest: "/manifest.json",
metadataBase: new URL(WEBSITE_INFO.url),
};

export const viewport: Viewport = {
themeColor: "#000",
};

interface LayoutProps {
readonly children: React.ReactNode;
}

const Layout = ({ children }: LayoutProps) => (
<html lang="en">
<head>
<link href="/icon.png" rel="shortcut icon" />
<link href="/icon.png" rel="apple-touch-icon" />
<link href="/favicon.png" rel="icon" />
<script
async
crossOrigin="anonymous"
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${
process.env.NEXT_PUBLIC_ADSENSE_CLIENT ?? ""
}`}
/>
</head>

<body>
<header className={styles.header}>
<Link href="/">
<Image alt="Duke Nuke Says Logo" height={79} src={Logo} width={300} />
</Link>
</header>

<main className={styles.content}>{children}</main>

<footer className={styles.link}>
Created by a bored employee at{" "}
<Link href="https://www.adsgames.net">A.D.S. Games</Link>
</footer>
</body>
<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_ID ?? ""} />
</html>
);

export default Layout;
16 changes: 16 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Metadata } from "next";
import Link from "next/link";

export const metadata: Metadata = {
title: "404 Not Found - Duke Nuke Says",
description: "Page not found",
};

const NotFound = () => (
<>
<h2>404 Page not found</h2>
<Link href="/">Go back to the homepage</Link>
</>
);

export default NotFound;
15 changes: 9 additions & 6 deletions pages/index.tsx → app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import type { Metadata } from "next";
import Link from "next/link";

import { Layout } from "@/components/Layout";
import { SAYINGS } from "@/constants/sayings";

export const metadata: Metadata = {
title: "Home - Duke Nuke Says",
description: "Duke Nukem soundboard",
};

const HomePage = () => (
<Layout description="Duke Nukem soundboard" title="Home">
<>
<h1>Duke Nukem Says...</h1>
<div className="sayings">
{SAYINGS.map((saying) => (
<Link href={`/${saying.id}`} key={saying.id} prefetch={false}>
<a>
<p>{saying.text}</p>
</a>
<p>{saying.text}</p>
</Link>
))}
</div>
</Layout>
</>
);

export default HomePage;
26 changes: 26 additions & 0 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { MetadataRoute } from "next";

import { SAYINGS } from "@/constants/sayings";
import { WEBSITE_INFO } from "@/constants/website-info";

export default function sitemap(): MetadataRoute.Sitemap {
const paths = SAYINGS.map(
(saying) =>
({
url: `${WEBSITE_INFO.url}/${saying.id}`,
lastModified: new Date(),
changeFrequency: "monthly",
priority: 0.7,
}) as const,
);

return [
{
url: WEBSITE_INFO.url,
lastModified: new Date(),
changeFrequency: "monthly",
priority: 1,
},
...paths,
];
}
56 changes: 0 additions & 56 deletions components/Layout.tsx

This file was deleted.

3 changes: 3 additions & 0 deletions constants/website-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const WEBSITE_INFO = {
url: "https://duke.adsgames.net",
} as const;
Loading

0 comments on commit 8dc3bba

Please sign in to comment.