Skip to content

Commit

Permalink
Merge pull request #14 from ooooorobo/migrate-app-router
Browse files Browse the repository at this point in the history
Migrate to app router
  • Loading branch information
ooooorobo authored Nov 18, 2023
2 parents ed5d77d + f3305ad commit d8956a5
Show file tree
Hide file tree
Showing 23 changed files with 394 additions and 399 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"@actions/core": "^1.10.1",
"@giscus/react": "^2.3.0",
"@mdx-js/loader": "3.0.0",
"@mdx-js/react": "3.0.0",
"@next/mdx": "14.0.3",
Expand Down
16 changes: 16 additions & 0 deletions src/app/AnalyticsScript.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Script from "next/script";

export const AnalyticsScript = () => (
<>
<Script src="https://www.googletagmanager.com/gtag/js?id=G-W8WNQ2WC88" />
<Script id={"google-analytics"}>
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-W8WNQ2WC88');
`}
</Script>
</>
);
39 changes: 39 additions & 0 deletions src/app/Providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import React, { ReactNode, useState } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { MDXProvider } from "@mdx-js/react";
import ThemeProvider from "@src/utils/context/ThemeProvider";
import DarkModeProvider from "@src/utils/context/DarkModeContext";
import components from "@src/components/mdx/MDXComponents";
import StyledComponentsRegistry from "@src/registry/StyledComponentsRegistry";
import GlobalStyle from "@src/styles/GlobalStyle";

export const Providers = ({ children }: { children: ReactNode }) => {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
refetchInterval: false,
},
},
}),
);

return (
<QueryClientProvider client={queryClient}>
<DarkModeProvider>
<StyledComponentsRegistry>
<ThemeProvider>
<GlobalStyle />
<MDXProvider components={components}>{children}</MDXProvider>
</ThemeProvider>
</StyledComponentsRegistry>
</DarkModeProvider>
</QueryClientProvider>
);
};
36 changes: 36 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PropsWithChildren } from "react";
import { Metadata } from "next"; // import "data://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css";
import Header from "@src/components/Header";
import { Providers } from "@src/app/Providers";
import { AnalyticsScript } from "@src/app/AnalyticsScript";

export const metadata: Metadata = {
title: { template: "%s | oooooroblog", default: "oooooroblog" },
description: "웹 프론트엔드 개발 블로그",
icons: {
icon: "/favicon-32x32.png",
shortcut: "/favicon-32x32.png",
apple: "/apple-touch-icon.png",
},
manifest: "/site.webmanifest",
openGraph: {
type: "website",
url: "https://www.oooooroblog.com",
title: "oooooroblog",
description: "웹 프론트엔드 개발 블로그",
},
};

export default function RootLayout({ children }: PropsWithChildren) {
return (
<html lang={"ko"}>
<body>
<Providers>
<Header />
{children}
</Providers>
<AnalyticsScript />
</body>
</html>
);
}
40 changes: 40 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use client";
import styled from "styled-components";
import { useEffect } from "react";
import { getAllPostMeta } from "@src/business/post";
import PostList from "@src/components/main/PostList";
import { StorageKey } from "@src/constants/constants";
import Profile from "@src/components/main/Profile";

export default function Page() {
const posts = getAllPostMeta();

useEffect(() => {
const scroll = parseInt(
sessionStorage.getItem(StorageKey.MAIN_SCROLL_Y) ?? "0",
);
window.scrollTo({ top: scroll, behavior: "auto" });
}, []);

const onClickPost = () => {
sessionStorage.setItem(StorageKey.MAIN_SCROLL_Y, window.scrollY.toString());
};

return (
<Wrapper>
<Profile />
<div>
<PostList posts={posts} onClickPost={onClickPost} />
</div>
</Wrapper>
);
}

const Wrapper = styled.div`
margin: 2.5rem auto;
max-width: 760px;
padding: 0 1rem;
display: flex;
flex-direction: column;
row-gap: 20px;
`;
18 changes: 18 additions & 0 deletions src/app/posts/[slug]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PropsWithChildren } from "react";
import { Metadata } from "next";
import { getPostDetail } from "@src/business/post";
import { PostPageProps } from "@src/app/posts/[slug]/page";

export function generateMetadata({
params: { slug },
}: PostPageProps): Metadata {
const { detail: post } = getPostDetail(slug);
return {
title: post.title,
description: post.description,
};
}

export default function PostLayout({ children }: PropsWithChildren) {
return <>{children}</>;
}
74 changes: 16 additions & 58 deletions src/pages/posts/[slug].tsx → src/app/posts/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,32 @@
import { GetStaticPropsContext } from "next";
import { useRouter } from "next/router";
import { NextSeo } from "next-seo";
import styled from "styled-components";
"use client";
import { useMDXComponent } from "next-contentlayer/hooks";
import { allPosts, type Post } from "contentlayer/generated";

import components from "@src/components/mdx/MDXComponents";
import WavyLine from "@src/components/WavyLine";
import styled from "styled-components";
import Meta from "@src/components/post/Meta";
import PostTitle from "@src/components/post/PostTitle";
import WavyLine from "@src/components/WavyLine";
import components from "@src/components/mdx/MDXComponents";
import Profile from "@src/components/main/Profile";
import SidePost from "@src/components/post/SidePost";
import Comment from "@src/components/post/Comment";
import { getPostDetail, getPostMeta } from "@src/business/post";
import { FadeIn } from "@src/styles/animation";
import SidePost from "@src/components/post/SidePost";

export async function getStaticPaths() {
// Get a list of valid post paths.
const paths = allPosts.map((post) => ({
params: { slug: post._raw.flattenedPath },
}));

return { paths, fallback: false };
}

export async function getStaticProps(context: GetStaticPropsContext) {
// Find the post for the current page.
const postIdx = allPosts.findIndex(
(post) => post._raw.flattenedPath === context.params?.slug
);
const post = allPosts[postIdx];
const prevPost = allPosts[postIdx - 1] ?? null,
nextPost = allPosts[postIdx + 1] ?? null;

// Return notFound if the post does not exist.
if (!post) return { notFound: true };

// Return the post as page props.
return { props: { post, prevPost, nextPost } };
}

export type PostPageProps = { params: { slug: string } };
const codePrefix = `
if (typeof process === 'undefined') {
globalThis.process = { env: {} }
}
`;

export default function Page({
post,
prevPost,
nextPost,
}: {
post: Post;
prevPost: Post;
nextPost: Post;
}) {
// Parse the MDX file via the useMDXComponent hook.
export default function PostPage({ params: { slug } }: PostPageProps) {
const { postIdx, detail: post } = getPostDetail(slug);
const prevPost = getPostMeta(postIdx + 1),
nextPost = getPostMeta(postIdx - 1);

const MDXContent = useMDXComponent(codePrefix + post.body.code);
const router = useRouter();

return (
<>
<NextSeo
title={post.title}
description={post.description}
canonical={`https://www.oooooroblog.com${router.pathname}`}
openGraph={{
type: "website",
title: post.title,
description: post.description,
url: `https://www.oooooroblog.com${router.pathname}`,
}}
/>
<PostHeader>
<Meta
title={post.title}
Expand All @@ -92,7 +48,9 @@ export default function Page({
<WavyLine />
</ProfileWrapper>
<SidePost prevPost={prevPost} nextPost={nextPost} />
<Comment />
<div>
<Comment />
</div>
</>
);
}
Expand All @@ -105,7 +63,7 @@ const PostHeader = styled.div`
margin-bottom: 3rem;
`;

export const Article = styled.article`
const Article = styled.article`
animation: ${FadeIn("0%")} 1.4s;
max-width: 760px;
Expand Down
43 changes: 43 additions & 0 deletions src/business/post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { compareDesc } from "date-fns";
import { allPosts } from "contentlayer/generated";
import { PostListElement } from "@src/types/post";

const getSortedPosts = () => {
return allPosts.sort((a, b) => {
const compareByDate = compareDesc(new Date(a.date), new Date(b.date));
if (compareByDate !== 0) return compareByDate;
return b.id - a.id;
});
};

export const getAllPostMeta = (): PostListElement[] => {
return getSortedPosts().map(
(meta) =>
({
slug: meta.url,
meta: {
index: meta.id,
title: meta.title,
description: meta.description || "",
postedAt: meta.date,
category: "",
series: "",
tags: [],
keywords: [],
},
}) satisfies PostListElement,
);
};

export const getPostDetail = (slug: string) => {
const sorted = getSortedPosts();
const postIdx = sorted.findIndex((post) => post._raw.flattenedPath === slug);
return {
postIdx,
detail: sorted[postIdx],
};
};

export const getPostMeta = (idx: number) => {
return getSortedPosts()[idx];
};
24 changes: 5 additions & 19 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
"use client";
import styled from "styled-components";
import Link from "next/link";
import { useContext } from "react";
import { DarkModeContext } from "@src/utils/context/DarkModeContext";
import { ThemeToggleButton } from "@src/components/header/ThemeToggleButton";

export default function Header() {
const { isDarkMode, toggleDarkMode } = useContext(DarkModeContext);

return (
<Background>
<Wrapper>
<div>
<Link href={"/"}>
ooooorobo
</Link>
</div>
<div>
<button onClick={toggleDarkMode}>
{isDarkMode ? (
<i className="bi bi-moon-fill" />
) : (
<i className="bi bi-sun-fill" />
)}
</button>
</div>
<Link href={"/"}>ooooorobo</Link>
<ThemeToggleButton />
</Wrapper>
</Background>
);
}

const Background = styled.div`
const Background = styled.nav`
background-color: ${(props) => props.theme.colors.bg.secondary};
`;

Expand Down
17 changes: 17 additions & 0 deletions src/components/header/ThemeToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use client";
import { useContext } from "react";
import { DarkModeContext } from "@src/utils/context/DarkModeContext";

export const ThemeToggleButton = () => {
const { isDarkMode, toggleDarkMode } = useContext(DarkModeContext);

return (
<button onClick={toggleDarkMode}>
{isDarkMode ? (
<i className="bi bi-moon-fill" />
) : (
<i className="bi bi-sun-fill" />
)}
</button>
);
};
Loading

1 comment on commit d8956a5

@vercel
Copy link

@vercel vercel bot commented on d8956a5 Nov 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.