Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
SeanPreusse committed Jan 4, 2024
0 parents commit 8742321
Show file tree
Hide file tree
Showing 61 changed files with 6,444 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## NextJS Notion Starter Blog (NextJs)

Make your personal website and blog with Nextjs 14, tailwind and Notion.

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
41 changes: 41 additions & 0 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { NotionRenderer } from 'react-notion';
import notion from '@/lib';
import { convertToPost } from '@/functions/convertToPost';
import TopScrollButton from '@/components/TopScrollButton';
import { aboutPageId } from "@/site";

export default async function AboutPage({
searchParams,
}: {
searchParams: { [key: string]: string };
}) {
const pageid = aboutPageId

// Fetching blockMap
const response = await fetch(`https://notion-api.splitbee.io/v1/page/${pageid}`, {
next: { revalidate: 60 },
});
const blockMap = await response.json();

// Fetching pageProperties
const pageProperties = await notion.pages.retrieve({ page_id: pageid });
const postDetails = convertToPost(pageProperties);

return (
<div className="space-y-5 max-w-7xl m-auto min-h-screen">
<img className="object-cover w-full h-52 rounded-[20px] aspect-video" src={postDetails.coverImage} />

<div>
<div className="text-center space-y-5 text-sm mx-auto mt-3">
<div className="tracking-tight sm:text-4xl">
{postDetails.title}
</div>
</div>

<div className="max-w-4xl px-6 mx-auto mb-24 space-y-8 md:px-8 pt-4 border-t mt-4">
<NotionRenderer blockMap={blockMap} />
</div>
</div>
</div>
);
}
77 changes: 77 additions & 0 deletions app/articles/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import "react-notion/src/styles.css";
import "prismjs/themes/prism-tomorrow.css";
import { NotionRenderer } from "react-notion";
import notion from "@/lib";
import { convertToPost } from "@/functions/convertToPost";
import Link from "next/link";
import TopScrollButton from "../../../components/TopScrollButton";
import Container from '@/components/Container';
import ArticleList from '@/components/ArticleList';
import { getAllPosts } from "@/functions/getAllPosts";
import { Article } from "@/layouts/types";
import getLocalizedDate from '@/app/utils/getLocalizedDate';
import { getTagFilteredPosts } from "@/functions/articleFilteredPosts";

export default async function Page({
searchParams,
}: {
searchParams: { [key: string]: string };
}) {
const { id } = searchParams;

const response = await fetch(`https://notion-api.splitbee.io/v1/page/${id}`, {
next: { revalidate: 60 },
});
const blockMap = await response.json();
const pageProperties = await notion.pages.retrieve({ page_id: id });
const postDetails = convertToPost(pageProperties);
const moreArticles: Article[] = await getAllPosts();

const formattedTime = getLocalizedDate(postDetails.date);
const slug = postDetails.slug || [];
const tags = postDetails.tags || [];

// Pass id instead of uniqueId
const tagPosts: Article[] = await getTagFilteredPosts({ tags, slug });

return (
<div className="space-y-5 max-w-7xl m-auto min-h-screen">
<img className="object-cover w-full h-52 rounded-[20px] aspect-video" src={postDetails.coverImage} />

<div>
<div className="text-center space-y-5 text-sm mx-auto mt-3">
<div className="tracking-tight sm:text-4xl">
{postDetails.title}
</div>
<div className="text-md leading-8 sm:mt-4">
<div>
<time dateTime={formattedTime}>{formattedTime}</time>
</div>
<div className="font-semibold">
{postDetails.author}
</div>
</div>
</div>

<div className="max-w-4xl px-6 mx-auto mb-24 space-y-8 md:px-8 pt-4 border-t mt-4">
<NotionRenderer blockMap={blockMap} />

</div>
<div className="py-12 border-t">
<Container>
<div className="flex items-center justify-between my-8">
<div className="text-3xl font-bold text-gray-900">Latest articles</div>
<Link href="/articles">
<span className="font-semibold text-gray-900 cursor-pointer">
More articles ➜
</span>
</Link>
</div>
<ArticleList articles={tagPosts} />
</Container>
</div>
</div>
<TopScrollButton />
</div>
);
}
21 changes: 21 additions & 0 deletions app/articles/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getAllPosts } from "@/functions/getAllPosts";
import { Article } from "@/layouts/types";
import Search from "../../components/Search";
import { calculateTagFrequency } from "@/functions/getAllTags";

const Articles = async () => {
const publishedPosts: Article[] = await getAllPosts();
const tagFrequencyMap = await calculateTagFrequency({ publishedPosts });

return (
<div className="max-w-5xl m-auto p-4 min-h-screen">
<Search
publishedPosts={publishedPosts}
tagFrequencyMap={tagFrequencyMap}
/>
</div>
);
};

export default Articles;

Binary file added app/favicon.ico
Binary file not shown.
82 changes: 82 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

html,
body,
:root {
height: 100%;
}

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;

--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;

--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;

--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;

--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;

--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;

--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;

--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;

--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;

--radius: 0.5rem;
}

.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;

--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;

--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;

--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;

--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;

--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;

--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;

--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;

--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
29 changes: 29 additions & 0 deletions app/hook/useScrollToTop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";

const useScrollToTop = () => {
const [showTopButton, setShowTopButton] = useState(false);

useEffect(() => {
const handleScroll = () => {
const scrolledToTop = window.scrollY === 0;
setShowTopButton(!scrolledToTop);
};

window.addEventListener("scroll", handleScroll);

return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);

const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: "smooth",
});
};

return { showTopButton, scrollToTop };
};

export default useScrollToTop;
28 changes: 28 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "../components/Header";
import Footer from "../components/Footer";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Header />
<main className="">{children}</main>
<Footer />
</body>
</html>
);
}
33 changes: 33 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getAllPosts } from "@/functions/getAllPosts";
import { Article } from "@/layouts/types";
import { postsPerPage } from "@/site";
import Feed from "@/components/Feed";
import HeroSection from "../components/HeroSection";



const HomePage = async ({
searchParams,
}: {
searchParams: { [key: string]: string };
}) => {
const page = Number(searchParams.page) || 1;
const publishedPosts: Article[] = await getAllPosts();
// const startIndex = (page - 1) * postsPerPage;
// const endIndex = startIndex + postsPerPage;
// const postsToShow = publishedPosts.slice(startIndex, endIndex);


return (
<>
<div className="">
<HeroSection />
<div className = "mt-4 max-w-5xl m-auto p-4 min-h-screen">
<Feed articles={publishedPosts} />
</div>
</div>
</>
);
};

export default HomePage;
Loading

0 comments on commit 8742321

Please sign in to comment.