bug fixes and tags

This commit is contained in:
2024-11-15 01:12:36 +01:00
parent 77760e857e
commit f28d59df04
15 changed files with 205 additions and 246 deletions
+22
View File
@@ -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",
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)}];
+9 -4
View File
@@ -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 });
} }
+2 -2
View File
@@ -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;
} }
+5 -36
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>