bug fixes and tags
This commit is contained in:
Generated
+22
@@ -21,6 +21,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-content-loader": "^7.0.2",
|
"react-content-loader": "^7.0.2",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"react-loading-skeleton": "^3.5.0",
|
"react-loading-skeleton": "^3.5.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
@@ -3393,6 +3394,18 @@
|
|||||||
"react": "^18.3.1"
|
"react": "^18.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-infinite-scroll-component": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"throttle-debounce": "^2.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
@@ -3963,6 +3976,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/throttle-debounce": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tr46": {
|
"node_modules/tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-content-loader": "^7.0.2",
|
"react-content-loader": "^7.0.2",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"react-loading-skeleton": "^3.5.0",
|
"react-loading-skeleton": "^3.5.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 391 B |
@@ -1 +0,0 @@
|
|||||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 128 B |
@@ -1 +0,0 @@
|
|||||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 385 B |
+12
-15
@@ -9,7 +9,10 @@ interface Blog {
|
|||||||
description: string,
|
description: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function queryDatabase(notion: Client, databaseId: string): Promise<[Array<any>, any]> {
|
type QueryResponse = { cursor: string | null, hasMore: boolean, items: Blog[] };
|
||||||
|
type Error = { msg: string };
|
||||||
|
|
||||||
|
export async function getBlogs(notion: Client, databaseId: string, cursor: string | undefined, pageSize: number): Promise<[number, QueryResponse | Error]> {
|
||||||
try {
|
try {
|
||||||
const response = await notion.databases.query({
|
const response = await notion.databases.query({
|
||||||
database_id: databaseId,
|
database_id: databaseId,
|
||||||
@@ -18,22 +21,16 @@ export async function queryDatabase(notion: Client, databaseId: string): Promise
|
|||||||
status: {
|
status: {
|
||||||
equals: "Posted"
|
equals: "Posted"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
start_cursor: cursor,
|
||||||
|
page_size: pageSize,
|
||||||
});
|
});
|
||||||
return [response.results, null];
|
return [200, {cursor: response.next_cursor, hasMore: response.has_more, items: response.results.map(convertBlog)}];
|
||||||
} catch (error: any) {
|
} catch (err: any) {
|
||||||
return [[], error];
|
return [400, {msg: String(err)}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function getBlogs(notion: Client, databaseId: string): Promise<[number, Blog[] | {msg: string}]> {
|
|
||||||
const [pages, err] = await queryDatabase(notion, databaseId);
|
|
||||||
if (err === null) {
|
|
||||||
return [200, pages.map(convertBlog)];
|
|
||||||
}
|
|
||||||
return [500, {msg: String(err)}];
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertBlog(page: any): Blog {
|
function convertBlog(page: any): Blog {
|
||||||
return {
|
return {
|
||||||
id: page.id,
|
id: page.id,
|
||||||
@@ -48,7 +45,7 @@ interface FullBlog extends Blog {
|
|||||||
markdown: string,
|
markdown: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getBlog(notion: Client, n2m: NotionToMarkdown, pageId: string): Promise<[number, FullBlog | {msg: string}]> {
|
export async function getBlog(notion: Client, n2m: NotionToMarkdown, pageId: string): Promise<[number, FullBlog | Error]> {
|
||||||
try {
|
try {
|
||||||
const page: any = await notion.pages.retrieve({ page_id: pageId })
|
const page: any = await notion.pages.retrieve({ page_id: pageId })
|
||||||
if (page.properties.Status.status.name !== 'Posted')
|
if (page.properties.Status.status.name !== 'Posted')
|
||||||
@@ -62,7 +59,7 @@ export async function getBlog(notion: Client, n2m: NotionToMarkdown, pageId: str
|
|||||||
description: blog.description,
|
description: blog.description,
|
||||||
tags: blog.tags,
|
tags: blog.tags,
|
||||||
date: blog.date,
|
date: blog.date,
|
||||||
markdown: n2m.toMarkdownString(mdBlocks).parent,
|
markdown: n2m.toMarkdownString(mdBlocks).parent || '',
|
||||||
}];
|
}];
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return [500, {msg: String(err)}];
|
return [500, {msg: String(err)}];
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import getBlogs from './blog';
|
import { getBlogs } from './blog';
|
||||||
import { notion, databaseId } from './notion';
|
import { notion, databaseId } from './notion';
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET(request: Request) {
|
||||||
let [status, data] = await getBlogs(notion, databaseId);
|
const url = new URL(request.url);
|
||||||
return Response.json(data, { status });
|
var cursor = url.searchParams.get('cursor') || undefined;
|
||||||
|
if (cursor === '')
|
||||||
|
cursor = undefined;
|
||||||
|
const size = parseInt(url.searchParams.get('size') || '5');
|
||||||
|
let [status, response] = await getBlogs(notion, databaseId, cursor, size);
|
||||||
|
return Response.json(response, { status });
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-family: 'Roboto Mono';
|
font-family: sans-serif;
|
||||||
color: #f5304c;
|
color: #f5304c;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@@ -59,7 +59,7 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin: 100px auto;
|
margin: 10px auto;
|
||||||
width: 600px;
|
width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,56 +3,30 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ContentLoader from "react-content-loader"
|
import ContentLoader from "react-content-loader"
|
||||||
|
|
||||||
export default function LoadingBlog(props: any) {
|
export function LoadingBlog(props: any) {
|
||||||
const [numComponents, setNumComponents] = useState(0);
|
|
||||||
const [isClient, setIsClient] = useState(false);
|
const [isClient, setIsClient] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only set to client after mount
|
|
||||||
setIsClient(true);
|
setIsClient(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const updateComponentCount = () => {
|
|
||||||
const windowHeight = window.innerHeight; // Get window height
|
|
||||||
const componentHeight = 100; // Set the height of your component
|
|
||||||
const count = Math.floor(windowHeight / componentHeight); // Calculate how many components fit
|
|
||||||
setNumComponents(count);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial calculation
|
|
||||||
updateComponentCount();
|
|
||||||
|
|
||||||
// Add resize event listener
|
|
||||||
window.addEventListener('resize', updateComponentCount);
|
|
||||||
|
|
||||||
// Cleanup listener on unmount
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('resize', updateComponentCount);
|
|
||||||
};
|
|
||||||
}, []); // Empty dependency array ensures the effect runs once after the initial render
|
|
||||||
|
|
||||||
// Only render on the client
|
|
||||||
if (!isClient) {
|
if (!isClient) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
{Array.from({ length: numComponents }, (_, index) => (
|
|
||||||
<ContentLoader
|
<ContentLoader
|
||||||
speed={2}
|
speed={2}
|
||||||
viewBox="0 0 290 50"
|
viewBox="0 0 290 50"
|
||||||
backgroundColor="#3b3b3b"
|
backgroundColor="#3b3b3b"
|
||||||
foregroundColor="#f5304c"
|
foregroundColor="#f5304c"
|
||||||
key={index}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<rect x="0" y="0" rx="7" ry="7" width="290" height="18" />
|
<rect x="0" y="0" rx="7" ry="7" width="290" height="18" />
|
||||||
<rect x="0" y="25" rx="3" ry="3" width="78" height="7" />
|
<rect x="0" y="25" rx="3" ry="3" width="78" height="7" />
|
||||||
<rect x="0" y="37" rx="3" ry="3" width="200" height="7" />
|
<rect x="0" y="37" rx="3" ry="3" width="200" height="7" />
|
||||||
</ContentLoader>
|
</ContentLoader>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -67,29 +41,24 @@ export function LoadingBlogRead(props: any) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateRectCount = () => {
|
const updateRectCount = () => {
|
||||||
// Calculate the number of rectangles based on window height
|
|
||||||
const windowHeight = window.innerHeight;
|
const windowHeight = window.innerHeight;
|
||||||
const rectHeight = 46; // Height of each rectangle
|
const rectHeight = 46;
|
||||||
const count = Math.floor(windowHeight / rectHeight); // Number of rectangles that fit
|
const count = Math.floor(windowHeight / rectHeight);
|
||||||
setNumRects(count);
|
setNumRects(count);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update on mount and window resize
|
|
||||||
updateRectCount();
|
updateRectCount();
|
||||||
window.addEventListener('resize', updateRectCount);
|
window.addEventListener('resize', updateRectCount);
|
||||||
|
|
||||||
// Cleanup event listener on unmount
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('resize', updateRectCount);
|
window.removeEventListener('resize', updateRectCount);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Only render on the client
|
|
||||||
if (!isClient) {
|
if (!isClient) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamically set the viewBox height based on the number of rectangles
|
|
||||||
const viewBoxHeight = window.innerHeight / 3;
|
const viewBoxHeight = window.innerHeight / 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -106,10 +75,10 @@ export function LoadingBlogRead(props: any) {
|
|||||||
{Array.from({ length: numRects }).map((_, i) => (
|
{Array.from({ length: numRects }).map((_, i) => (
|
||||||
<rect
|
<rect
|
||||||
x="0"
|
x="0"
|
||||||
y={45 + i * 14} // Adjust position based on index
|
y={45 + i * 14}
|
||||||
rx="3"
|
rx="3"
|
||||||
ry="3"
|
ry="3"
|
||||||
width={Math.random() * (300 - 80) + 80} // Random width
|
width={Math.random() * (300 - 80) + 80}
|
||||||
height="9"
|
height="9"
|
||||||
key={i}
|
key={i}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+26
-46
@@ -1,65 +1,45 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import '../../Blog.css';
|
import '../Blog.css';
|
||||||
import MarkdownRenderer from './MarkdownRenderer';
|
import MarkdownRenderer from './MarkdownRenderer';
|
||||||
import { LoadingBlogRead } from '../Loading';
|
import { LoadingBlogRead } from '../Loading';
|
||||||
|
import { BlogT2 } from '../page';
|
||||||
|
|
||||||
export default function Blog({ params }: { params: Promise<{ id: string }> }) {
|
export default function Blog({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const [blog, setBlog] = useState({ title: '', date: '', markdown: '' });
|
const [blog, setBlog] = useState<BlogT2 | null>(null);
|
||||||
const [error, setError] = useState('');
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
const date = new Date(blog.date).toLocaleString('default', { month: 'long', day: 'numeric', year: 'numeric' });
|
useState(() => {
|
||||||
|
params.then(({id}) => {
|
||||||
useEffect(() => {
|
fetch(`/api/${id}`).then((res) => {
|
||||||
const fetchBlog = async () => {
|
res.json().then((data) => {
|
||||||
try {
|
setBlog(data);
|
||||||
console.log('obtaining blog')
|
}).catch((err) => {
|
||||||
const response = await fetch(`/api/${(await params).id}`);
|
console.error(err);
|
||||||
if (response.ok) {
|
});
|
||||||
const blog = await response.json();
|
});
|
||||||
document.title = blog.title + " - Leo's blog";
|
});
|
||||||
setBlog(blog);
|
});
|
||||||
} else {
|
|
||||||
const errorData = await response.text();
|
|
||||||
setError(errorData);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
setError(err);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchBlog();
|
|
||||||
}, [params]);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <main>
|
|
||||||
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
|
||||||
<div className='blog_container'>
|
|
||||||
<LoadingBlogRead/>
|
|
||||||
</div>
|
|
||||||
</main>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
|
||||||
<div>Error: {error}</div>;
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (blog !== null) {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
||||||
<div className='blog_container'>
|
<div className='blog_container'>
|
||||||
<h2 className='blog_title'>{blog.title}</h2>
|
<h2 className='blog_title'>{blog.title}</h2>
|
||||||
<p className='date'>{date}</p>
|
<p className='date'>{new Date(blog.date).toLocaleString('default', { month: 'long', day: 'numeric', year: 'numeric' })}</p>
|
||||||
<div className='markdown'><MarkdownRenderer>{blog.markdown.replaceAll('\n-\n', '\n<br/>\n')}</MarkdownRenderer></div>
|
<div className='markdown'><MarkdownRenderer>{blog.markdown.replaceAll('\n-\n', '\n<br/>\n')}</MarkdownRenderer></div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
||||||
|
<div className='blog_container'>
|
||||||
|
<LoadingBlogRead/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
+76
-90
@@ -1,110 +1,94 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import '../Blog.css';
|
import './Blog.css';
|
||||||
import LoadingBlog from './Loading';
|
import { LoadingBlog } from './Loading';
|
||||||
|
import InfiniteScroll from "react-infinite-scroll-component";
|
||||||
|
|
||||||
type Blog = { title: string, description: string, id: string, date: string, tags: { name: string, color: string }[] };
|
export type BlogT = {
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
id: string,
|
||||||
|
date: string,
|
||||||
|
tags: { name: string, color: string }[]
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BlogT2 = {
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
id: string,
|
||||||
|
date: string,
|
||||||
|
tags: { name: string, color: string }[],
|
||||||
|
markdown: string,
|
||||||
|
};
|
||||||
|
|
||||||
const notionColors: { [index: string]: {
|
const notionColors: { [index: string]: {
|
||||||
background: string,
|
background: string,
|
||||||
color: string,
|
color: string,
|
||||||
} } = {
|
} } = {
|
||||||
default: {
|
default: { background: "#191919", color: "#D4D4D4" },
|
||||||
background: "#191919",
|
gray: { background: "#252525", color: "#9B9B9B" },
|
||||||
color: "#D4D4D4"
|
brown: { background: "#2E2724", color: "#A27763" },
|
||||||
},
|
orange: { background: "#36291F", color: "#CB7B37" },
|
||||||
gray: {
|
yellow: { background: "#372E20", color: "#C19138" },
|
||||||
background: "#252525",
|
green: { background: "#242B26", color: "#4F9768" },
|
||||||
color: "#9B9B9B"
|
blue: { background: "#1F282D", color: "#447ACB" },
|
||||||
},
|
purple: { background: "#2A2430", color: "#865DBB" },
|
||||||
brown: {
|
pink: { background: "#2E2328", color: "#BA4A78" },
|
||||||
background: "#2E2724",
|
red: { background: "#332523", color: "#BE524B" }
|
||||||
color: "#A27763"
|
|
||||||
},
|
|
||||||
orange: {
|
|
||||||
background: "#36291F",
|
|
||||||
color: "#CB7B37"
|
|
||||||
},
|
|
||||||
yellow: {
|
|
||||||
background: "#372E20",
|
|
||||||
color: "#C19138"
|
|
||||||
},
|
|
||||||
green: {
|
|
||||||
background: "#242B26",
|
|
||||||
color: "#4F9768"
|
|
||||||
},
|
|
||||||
blue: {
|
|
||||||
background: "#1F282D",
|
|
||||||
color: "#447ACB"
|
|
||||||
},
|
|
||||||
purple: {
|
|
||||||
background: "#2A2430",
|
|
||||||
color: "#865DBB"
|
|
||||||
},
|
|
||||||
pink: {
|
|
||||||
background: "#2E2328",
|
|
||||||
color: "#BA4A78"
|
|
||||||
},
|
|
||||||
red: {
|
|
||||||
background: "#332523",
|
|
||||||
color: "#BE524B"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Blog() {
|
export default function Blog() {
|
||||||
const [data, setData] = useState<Blog[]>([]);
|
const [items, setItems] = useState<BlogT[]>([]);
|
||||||
const [error, setError] = useState('');
|
const [hasMore, setHasMore] = useState(true);
|
||||||
const [loading, setLoading] = useState(true);
|
const [cursor, setCursor] = useState('');
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
if (hasMore) {
|
||||||
|
fetch(`/api/?cursor=${cursor}&size=10`).then((res) => {
|
||||||
|
res.json().then((data: { items: BlogT[], hasMore: boolean, cursor: string }) => {
|
||||||
|
setItems((prev) => [...prev, ...data.items]);
|
||||||
|
setHasMore(data.hasMore);
|
||||||
|
setCursor(data.cursor);
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshData = () => {
|
||||||
|
setItems([]);
|
||||||
|
setHasMore(true);
|
||||||
|
setCursor('');
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
document.title = "Leo's blog";
|
||||||
setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/');
|
|
||||||
if (response.ok) {
|
|
||||||
const result = await response.json();
|
|
||||||
setData(result);
|
|
||||||
setLoading(false);
|
|
||||||
} else {
|
|
||||||
const errorData = await response.text();
|
|
||||||
setError(errorData);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
setError(err.toString());
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
|
||||||
<div>Error: {error}</div>;
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} if (loading) {
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
|
||||||
<LoadingBlog/>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = "Leo's blog";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
{data.map((blog, i) => {
|
<InfiniteScroll
|
||||||
return (
|
pullDownToRefresh
|
||||||
|
pullDownToRefreshThreshold={50}
|
||||||
|
dataLength={items.length}
|
||||||
|
next={fetchData}
|
||||||
|
hasMore={hasMore}
|
||||||
|
loader={<LoadingBlog/>}
|
||||||
|
refreshFunction={refreshData}
|
||||||
|
endMessage={
|
||||||
|
<p style={{ textAlign: 'center' }}>
|
||||||
|
<a>Yay! You have seen it all</a>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{items.map((blog, i) => (
|
||||||
<a key={i} href={`/blog/${blog.id}`} style={{ textDecoration: 'none' }}>
|
<a key={i} href={`/blog/${blog.id}`} style={{ textDecoration: 'none' }}>
|
||||||
<div className='blog'>
|
<div className='blog'>
|
||||||
<h2 className='title'>{blog.title}</h2>
|
<h2 className='title'>{blog.title}</h2>
|
||||||
@@ -120,7 +104,8 @@ export default function Blog() {
|
|||||||
fontFamily: 'sans-serif',
|
fontFamily: 'sans-serif',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
marginTop: '8px'
|
marginTop: '8px',
|
||||||
|
marginLeft: i === 0 ? '0px' : '8px',
|
||||||
}}>{tag.name}</span>
|
}}>{tag.name}</span>
|
||||||
:
|
:
|
||||||
<span key={i} style={{
|
<span key={i} style={{
|
||||||
@@ -131,13 +116,14 @@ export default function Blog() {
|
|||||||
fontFamily: 'sans-serif',
|
fontFamily: 'sans-serif',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
marginTop: '8px'
|
marginTop: '8px',
|
||||||
|
marginLeft: i === 0 ? '0px' : '8px',
|
||||||
}}>{tag.name}</span>
|
}}>{tag.name}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
);
|
))}
|
||||||
})}
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
+7
-3
@@ -2,8 +2,8 @@ import type { Metadata } from "next";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Leo's portfolio",
|
||||||
description: "Generated by create next app",
|
description: "Leo's portfolio"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@@ -14,7 +14,11 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<title>Leo's portfolio</title>
|
<title>Leo's portfolio</title>
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico"></link>
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/logo.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/logo.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo.png" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<body>
|
<body>
|
||||||
{children}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user