Skip to content

Commit 2f811d8

Browse files
authored
Merge pull request #1 from tkokhing/next15
completed testing on Next V15
2 parents e864995 + 7dc797f commit 2f811d8

8 files changed

Lines changed: 588 additions & 198 deletions

File tree

package-lock.json

Lines changed: 495 additions & 108 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
"lodash": "^4.17.21",
2828
"moment": "^2.30.1",
2929
"morgan": "^1.10.0",
30-
"next": "^14.2.14",
30+
"next": "^15.3.2",
3131
"next-mdx-remote": "^5.0.0",
3232
"path-to-regexp": "^8.2.0",
3333
"qs": "^6.14.0",
34-
"react": "^18.3.1",
35-
"react-dom": "^18.3.1",
34+
"react": "^19.1.0",
35+
"react-dom": "^19.1.0",
3636
"react-icons": "^5.4.0",
3737
"rehype-autolink-headings": "^7.1.0",
3838
"rehype-slug": "^6.0.0",

src/app/_components/main_frame/switch.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
display: inline-block;
77
color: currentColor;
88
border-radius: 50%;
9-
border: 1.5px dashed currentColor;
9+
border: 1.6px dashed currentColor;
1010
cursor: pointer;
1111
--size: 24px;
1212
height: var(--size);
Lines changed: 53 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,92 @@
1-
"use client";
1+
'use client';
22

3-
import styles from "./switch.module.css";
4-
import { memo, useEffect, useState } from "react";
3+
import { memo, useEffect, useState } from 'react';
4+
import styles from './switch.module.css';
55

6-
declare global {
7-
var updateDOM: () => void;
8-
}
6+
const STORAGE_KEY = 'this-theme';
7+
const MODES = ['system', 'dark', 'light'] as const;
8+
type ColorSchemePreference = typeof MODES[number];
99

10-
type ColorSchemePreference = "system" | "dark" | "light";
11-
12-
const STORAGE_KEY = "this-theme";
13-
const modes: ColorSchemePreference[] = ["system", "dark", "light"];
14-
15-
/** to reuse updateDOM function defined inside injected script */
16-
17-
/** function to be injected in script tag for avoiding FOUC (Flash of Unstyled Content) */
18-
const NoFOUCScript = (storageKey: string) => {
19-
/* can not use outside constants or function as this script will be injected in a different context */
20-
const [SYSTEM, DARK, LIGHT] = ["system", "dark", "light"];
10+
// Injects theme script before hydration to avoid FOUC
11+
const noFOUCScript = (storageKey: string) => {
12+
const [SYSTEM, DARK, LIGHT] = ['system', 'dark', 'light'];
13+
const media = matchMedia(`(prefers-color-scheme: ${DARK})`);
2114

22-
/** Modify transition globally to avoid patched transitions */
23-
const modifyTransition = () => {
24-
const css = document.createElement("style");
25-
css.textContent = "*,*:after,*:before{transition:none !important;}";
26-
document.head.appendChild(css);
15+
const disableTransitionsTemporarily = () => {
16+
const style = document.createElement('style');
17+
style.textContent = '*, *::before, *::after { transition: none !important; }';
18+
document.head.appendChild(style);
2719

2820
return () => {
29-
/* Force restyle */
3021
getComputedStyle(document.body);
31-
/* Wait for next tick before removing */
32-
setTimeout(() => document.head.removeChild(css), 1);
22+
setTimeout(() => document.head.removeChild(style), 1);
3323
};
3424
};
3525

36-
const media = matchMedia(`(prefers-color-scheme: ${DARK})`);
37-
38-
/** function to add remove dark class */
39-
window.updateDOM = () => {
40-
const restoreTransitions = modifyTransition();
41-
const mode = localStorage.getItem(storageKey) ?? SYSTEM;
26+
const applyTheme = () => {
27+
const restoreTransitions = disableTransitionsTemporarily();
28+
const storedMode = localStorage.getItem(storageKey) ?? SYSTEM;
4229
const systemMode = media.matches ? DARK : LIGHT;
43-
const resolvedMode = mode === SYSTEM ? systemMode : mode;
30+
const resolvedMode = storedMode === SYSTEM ? systemMode : storedMode;
31+
4432
const classList = document.documentElement.classList;
45-
if (resolvedMode === DARK) classList.add(DARK);
46-
else classList.remove(DARK);
47-
document.documentElement.setAttribute("data-mode", mode);
33+
classList.toggle(DARK, resolvedMode === DARK);
34+
document.documentElement.setAttribute('data-mode', storedMode);
4835
restoreTransitions();
4936
};
50-
window.updateDOM();
51-
media.addEventListener("change", window.updateDOM);
52-
};
5337

54-
let updateDOM: () => void;
38+
applyTheme();
39+
media.addEventListener('change', applyTheme);
40+
};
5541

56-
/**
57-
* Switch button to quickly toggle user preference.
58-
*/
5942
const Switch = () => {
60-
const [mode, setMode] = useState<ColorSchemePreference>(
61-
() =>
62-
((typeof localStorage !== "undefined" &&
63-
localStorage.getItem(STORAGE_KEY)) ??
64-
"system") as ColorSchemePreference,
65-
);
43+
const [mode, setMode] = useState<ColorSchemePreference>(() => {
44+
return (typeof localStorage !== 'undefined'
45+
? (localStorage.getItem(STORAGE_KEY) as ColorSchemePreference)
46+
: 'system') ?? 'system';
47+
});
6648

6749
useEffect(() => {
68-
// store global functions to local variables to avoid any interference
69-
updateDOM = window.updateDOM;
70-
/** Sync the tabs */
71-
addEventListener("storage", (e: StorageEvent): void => {
72-
e.key === STORAGE_KEY && setMode(e.newValue as ColorSchemePreference);
73-
});
50+
const storedMode = localStorage.getItem(STORAGE_KEY) as ColorSchemePreference;
51+
setMode(storedMode ?? 'system');
7452
}, []);
7553

7654
useEffect(() => {
7755
localStorage.setItem(STORAGE_KEY, mode);
78-
updateDOM();
56+
const systemMode = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
57+
const resolvedMode = mode === 'system' ? systemMode : mode;
58+
const classList = document.documentElement.classList;
59+
classList.toggle('dark', resolvedMode === 'dark');
60+
document.documentElement.setAttribute('data-mode', mode);
7961
}, [mode]);
8062

81-
/** toggle mode */
82-
const handleModeSwitch = () => {
83-
const index = modes.indexOf(mode);
84-
setMode(modes[(index + 1) % modes.length]);
63+
const handleToggle = () => {
64+
const nextIndex = (MODES.indexOf(mode) + 1) % MODES.length;
65+
setMode(MODES[nextIndex]);
8566
};
67+
8668
return (
8769
<button
88-
suppressHydrationWarning={true}
70+
suppressHydrationWarning
8971
className={styles.switch}
90-
aria-label="ThemeSwitch"
91-
onClick={handleModeSwitch}
92-
>
93-
</button>
72+
aria-label="Toggle theme"
73+
onClick={handleToggle}
74+
/>
9475
);
9576
};
9677

9778
const Script = memo(() => (
9879
<script
80+
suppressHydrationWarning
9981
dangerouslySetInnerHTML={{
100-
__html: `(${NoFOUCScript.toString()})('${STORAGE_KEY}')`,
82+
__html: `(${noFOUCScript.toString()})('${STORAGE_KEY}')`,
10183
}}
10284
/>
10385
));
10486

105-
/**
106-
* This component wich applies classes and transitions.
107-
*/
108-
export const ThemeSwitcher = () => {
109-
return (
110-
<>
111-
<Script />
112-
<Switch />
113-
</>
114-
);
115-
};
87+
export const ThemeSwitcher = () => (
88+
<>
89+
<Script />
90+
<Switch />
91+
</>
92+
);

src/app/blog/posts/[slug]/page.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// pull from private repo: [tkokhing/blog_post/_posts] MDX_FOLDER
2+
import { Metadata } from "next";
23
import { notFound } from "next/navigation";
34
import Container from "@/app/_components/container";
45
import { PostHeader } from "@/app/_components/post-header";
@@ -9,7 +10,14 @@ import { generatePageStaticParams } from "@/lib/generatePageStaticParams";
910

1011
const MDX_FOLDER = "_posts";
1112

12-
export default async function Post({ params }: { params: { slug: string } }) {
13+
type Params = {
14+
params: Promise<{
15+
slug: string;
16+
}>;
17+
};
18+
19+
export default async function Post(props: Params) {
20+
const params = await props.params;
1321
const post = getPostBySlug(params.slug, MDX_FOLDER);
1422
if (!post) return notFound();
1523

@@ -32,7 +40,8 @@ export default async function Post({ params }: { params: { slug: string } }) {
3240
);
3341
}
3442

35-
export async function generateMetadata({ params }: { params: { slug: string } }) {
43+
export async function generateMetadata(props: Params): Promise<Metadata> {
44+
const params = await props.params;
3645
return generatePageMetadata(params.slug, MDX_FOLDER);
3746
}
3847

src/app/layout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ export default function RootLayout({
4949
children,
5050
}: Readonly<{ children: React.ReactNode }>) {
5151
return (
52-
<html lang="en">
52+
<html lang="en" suppressHydrationWarning={true}>
5353
<body
5454
className={cn(inter.className, "bg-slate-50 text-sky-700 dark:bg-slate-900 dark:text-slate-300")}
5555
>
56-
<AlertBar />
56+
<ThemeSwitcher />
5757
<NavigationProvider>
58+
<AlertBar />
5859
<Navigationbar />
59-
<ThemeSwitcher />
6060
<SubpageHeader />
6161
<div className="min-h-screen">{children}</div>
6262
<Footer />

src/app/topics/AtoZ/[slug]/page.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// pull from private repo: [tkokhing/topic_post/_topics] MDX_FOLDER
2+
import { Metadata } from "next";
23
import { notFound } from "next/navigation";
34
import Container from "@/app/_components/container";
45
import { PostHeader } from "@/app/_components/post-header";
@@ -9,8 +10,14 @@ import { generatePageStaticParams } from "@/lib/generatePageStaticParams";
910

1011
const MDX_FOLDER = "_topics";
1112

12-
export default async function Post({ params }: { params: { slug: string } }) {
13-
const post = getPostBySlug(params.slug, MDX_FOLDER);
13+
type Params = {
14+
params: Promise<{
15+
slug: string;
16+
}>;
17+
};
18+
19+
export default async function Post(props: Params) {
20+
const params = await props.params; const post = getPostBySlug(params.slug, MDX_FOLDER);
1421
if (!post) return notFound();
1522

1623
return (
@@ -32,7 +39,8 @@ export default async function Post({ params }: { params: { slug: string } }) {
3239
);
3340
}
3441

35-
export async function generateMetadata({ params }: { params: { slug: string } }) {
42+
export async function generateMetadata(props: Params): Promise<Metadata> {
43+
const params = await props.params;
3644
return generatePageMetadata(params.slug, MDX_FOLDER);
3745
}
3846

src/app/topics/AtoZ/terrified-by-linux/[slug]/page.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// pull from private repo: [tkokhing/linux_post/_linux] MDX_FOLDER
2+
import { Metadata } from "next";
23
import { notFound } from "next/navigation";
34
import Container from "@/app/_components/container";
45
import { PostHeader } from "@/app/_components/post-header";
@@ -13,7 +14,14 @@ import Tip from "@/app/_components/blog_frame/tip";
1314

1415
const MDX_FOLDER = "_linux";
1516

16-
export default async function Post({ params }: { params: { slug: string } }) {
17+
type Params = {
18+
params: Promise<{
19+
slug: string;
20+
}>;
21+
};
22+
23+
export default async function Post(props: Params) {
24+
const params = await props.params;
1725
const post = getPostBySlug(params.slug, MDX_FOLDER);
1826
const LinuxBlogComponents = {
1927
Tip,
@@ -41,7 +49,8 @@ export default async function Post({ params }: { params: { slug: string } }) {
4149
);
4250
}
4351

44-
export async function generateMetadata({ params }: { params: { slug: string } }) {
52+
export async function generateMetadata(props: Params): Promise<Metadata> {
53+
const params = await props.params;
4554
return generatePageMetadata(params.slug, MDX_FOLDER);
4655
}
4756

0 commit comments

Comments
 (0)