diff --git a/src/app/cookie-policy/page.tsx b/src/app/cookie-policy/page.tsx
index 00e9366..3cbb660 100644
--- a/src/app/cookie-policy/page.tsx
+++ b/src/app/cookie-policy/page.tsx
@@ -10,7 +10,7 @@ export default function CookiePolicyPage() {
return (
-
+
Cookie policy
diff --git a/src/components/blog/BlogPost/index.tsx b/src/components/blog/BlogPost/index.tsx
index 40a12c3..1f8f387 100644
--- a/src/components/blog/BlogPost/index.tsx
+++ b/src/components/blog/BlogPost/index.tsx
@@ -1,15 +1,16 @@
'use client';
-import Markdown from 'marked-react';
import useSWR from 'swr';
-import PrimaryLink from '@/components/common/links/PrimaryLink';
+import MarkdownContent from '@/components/blog/MarkdownContent';
+import UnstyledLink from '@/components/common/links/UnstyledLink';
import NextImage from '@/components/common/NextImage';
import { siteConfig } from '@/constants/config';
import { HashnodePost } from '@/interfaces/hashnode';
+import { getUserLink } from '@/utils/hashnode';
-export default function BlogPostList({ slug }: { slug: string }) {
+export default function BlogPost({ slug }: { slug: string }) {
const url = `${siteConfig.url}/blog/${slug}`;
const { data, error } = useSWR(`/api/blog/post?slug=${slug}`);
@@ -21,6 +22,8 @@ export default function BlogPostList({ slug }: { slug: string }) {
return null;
}
+ const authorLink = data.author ? getUserLink(data.author) : undefined;
+
return (
-
-
+
-
+ {/* {data.tags?.map((tag) => (
+
+ {tag.name}
+
+ ))} */}
-
{data.coverImage ? (
) : null}
-
-
{text}
,
- list: (body: string, ordered: boolean, start?: number) =>
- ordered ? (
-
- {body}
-
- ) : (
-
- ),
- link: (href: string, text: string) => (
- {text}
- ),
- image: (href: string, title: string, text: string) => (
-
- ),
- }}
+
+
- {data.content?.markdown}
-
+ {data.title}
+
+
+ {data.content?.markdown}
+
+
);
}
diff --git a/src/components/blog/BlogPostList/BlogPostListItem.tsx b/src/components/blog/BlogPostList/BlogPostListItem.tsx
index 8f503e0..15567c3 100644
--- a/src/components/blog/BlogPostList/BlogPostListItem.tsx
+++ b/src/components/blog/BlogPostList/BlogPostListItem.tsx
@@ -14,7 +14,12 @@ export default function BlogPostListItem({ post }: { post: HashnodePost }) {
const authorLink = post.author ? getUserLink(post.author) : undefined;
return (
-
+
{post.coverImage ? (
) : (
@@ -30,15 +36,19 @@ export default function BlogPostListItem({ post }: { post: HashnodePost }) {
-
-
+ ))} */}
+
-
-
+
+
{post.title}
- {post.brief}
+
+ {post.brief}
+
-
-
+
);
diff --git a/src/components/blog/BlogPostList/index.tsx b/src/components/blog/BlogPostList/index.tsx
index 56b41cd..1c7d7b2 100644
--- a/src/components/blog/BlogPostList/index.tsx
+++ b/src/components/blog/BlogPostList/index.tsx
@@ -1,34 +1,47 @@
'use client';
+import { useState } from 'react';
+import useInfiniteScroll from 'react-infinite-scroll-hook';
import useSWRInfinite from 'swr/infinite';
import BlogPostListItem from '@/components/blog/BlogPostList/BlogPostListItem';
import { HashnodePostEdge } from '@/interfaces/hashnode';
-const getKey = (pageIndex: number, previousPageData: HashnodePostEdge[]) => {
- const endpoint = '/api/blog/posts';
+export default function BlogPostList() {
+ const [hasNextPage, setHasNextPage] = useState(true);
- // reached the end
- if (previousPageData && previousPageData.length === 0) {
- return null;
- }
+ const getKey = (pageIndex: number, previousPageData: HashnodePostEdge[]) => {
+ const endpoint = '/api/blog/posts';
- // first page, we don't have `previousPageData`
- if (pageIndex === 0) {
- return endpoint;
- }
+ // reached the end
+ if (previousPageData && previousPageData.length === 0) {
+ setHasNextPage(false);
+ return null;
+ }
- // add the cursor to the API endpoint
- return `${endpoint}?after=${
- previousPageData[previousPageData.length - 1].cursor
- }`;
-};
+ // first page, we don't have `previousPageData`
+ if (pageIndex === 0) {
+ return endpoint;
+ }
-export default function BlogPostList() {
- const { data, error, size, setSize } =
+ // add the cursor to the API endpoint
+ return `${endpoint}?after=${
+ previousPageData[previousPageData.length - 1].cursor
+ }`;
+ };
+
+ const { data, error, size, setSize, isLoading, isValidating } =
useSWRInfinite(getKey);
+ const [sentryRef] = useInfiniteScroll({
+ loading: isLoading || isValidating,
+ hasNextPage,
+ onLoadMore: () => setSize(size + 1),
+ disabled: !!error,
+ rootMargin: '0px 0px 400px 0px',
+ });
+
if (!data && !error) {
return null;
}
@@ -42,7 +55,7 @@ export default function BlogPostList() {
)),
)}
-
+
);
}
diff --git a/src/components/blog/MarkdownContent/Heading.tsx b/src/components/blog/MarkdownContent/Heading.tsx
new file mode 100644
index 0000000..a9ef244
--- /dev/null
+++ b/src/components/blog/MarkdownContent/Heading.tsx
@@ -0,0 +1,32 @@
+import { ReactNode } from 'react';
+
+import { cn } from '@/utils/css';
+
+const headingClasses = {
+ h2: 'mt-16 mb-8 text-2xl',
+ h3: 'mt-12 mb-8 text-xl',
+ h4: 'my-8 text-lg',
+ h5: 'my-8 text-base',
+ h6: 'my-8 text-base',
+};
+
+export default function Heading({
+ as: As,
+ children,
+ ...props
+}: {
+ as: 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
+ children: ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/blog/MarkdownContent/index.tsx b/src/components/blog/MarkdownContent/index.tsx
new file mode 100644
index 0000000..a999859
--- /dev/null
+++ b/src/components/blog/MarkdownContent/index.tsx
@@ -0,0 +1,54 @@
+import Markdown from 'marked-react';
+
+import Heading from '@/components/blog/MarkdownContent/Heading';
+import PrimaryLink from '@/components/common/links/PrimaryLink';
+import NextImage from '@/components/common/NextImage';
+
+export default function MarkdownContent({ children }: { children?: string }) {
+ return (
+
(
+
+ {text}
+
+ ),
+ paragraph: (text: string) => {text}
,
+ list: (body: string, ordered: boolean, start?: number) =>
+ ordered ? (
+
+ {body}
+
+ ) : (
+
+ ),
+ link: (href: string, text: string) => (
+ {text}
+ ),
+ image: (href: string, title: string, text: string) => (
+
+ ),
+ }}
+ >
+ {children}
+
+ );
+}
diff --git a/yarn.lock b/yarn.lock
index 1e53810..fb24816 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4401,6 +4401,18 @@ react-icons@4.11.0:
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.11.0.tgz#4b0e31c9bfc919608095cc429c4f1846f4d66c65"
integrity sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==
+react-infinite-scroll-hook@4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/react-infinite-scroll-hook/-/react-infinite-scroll-hook-4.1.1.tgz#25c0c2cb9a41039019bd723823e21db1404c137d"
+ integrity sha512-1bu2572rF3DtjFMhIOzoasLMdYW0vMWxROtl99M5FYGSxm84Ro4aNBZW6ivgE45ofus4Ymo7jIS0Be3zcuLk8g==
+ dependencies:
+ react-intersection-observer-hook "^2.1.1"
+
+react-intersection-observer-hook@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/react-intersection-observer-hook/-/react-intersection-observer-hook-2.1.1.tgz#6222a82624d2a507aa5ad187c99d2d530e746e4f"
+ integrity sha512-MeFGpYtcfHB9v6oGqQuHAbSwaWBpd7yZ4wMIeVtboWRdGusAF4V+/8QQ0OKZ36Ez19grYnoDVhRUCjtwI2ZVaw==
+
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"