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
+123
View File
@@ -0,0 +1,123 @@
@import url(https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700;800;900&display=swap);
@import url(https://fonts.googleapis.com/css?family=Roboto+Mono);
@import url(https://fonts.googleapis.com/css?family=Roboto);
p {
font-family: 'Roboto Mono';
color: white;
text-align: left;
}
body {
background-color: #101010;
margin: 0;
padding: 0;
}
h1 {
text-align: left;
}
.blog_title {
font-family: 'Montserrat';
color: white;
text-align: left;
font-weight: 800;
font-size: 36px;
}
.title {
font-family: 'Montserrat';
color: #f5304c;
text-align: left;
font-weight: 800;
font-size: 36px;
margin-top: -1%;
}
.name {
font-family: sans-serif;
color: #f5304c;
text-align: center;
font-weight: 800;
}
.date {
font-family: 'Montserrat';
font-size: 12px;
}
.description {
font-family: 'Montserrat';
font-size: 15px;
}
a {
color: #f5304c;
font-family: 'Roboto Mono';
text-decoration: none;
}
.container {
margin: 10px auto;
width: 600px;
}
@media (max-width: 600px) {
.container {
width: 100%;
}
}
.blog {
background-color: #151515;
border-radius: 10px;
border: 10px solid #151515;
margin: 20px;
transition: transform 0.4s ease;
overflow-wrap: break-word;
}
.blog.blog:hover {
transform: scale(1.03);
}
.blog_container {
margin: 60px auto;
width: 730px;
text-align: left;
overflow-wrap: break-word;
user-select: text;
overflow: hidden;
}
@media (max-width: 730px) {
.blog_container {
width: 100%;
}
}
/*
MARKDOWN
*/
.markdown {
font-family: 'Roboto Mono';
margin-top: 30px;
text-align: left;
}
.markdown img {
max-width: 100%;
}
ol li::marker {
color: #f5304c;
}
ul li::marker {
color: #f5304c;
}
+16 -47
View File
@@ -3,56 +3,30 @@
import { useEffect, useState } from "react";
import ContentLoader from "react-content-loader"
export default function LoadingBlog(props: any) {
const [numComponents, setNumComponents] = useState(0);
export function LoadingBlog(props: any) {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
// Only set to client after mount
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) {
return null;
}
return (
<div className="container">
{Array.from({ length: numComponents }, (_, index) => (
<ContentLoader
speed={2}
viewBox="0 0 290 50"
backgroundColor="#3b3b3b"
foregroundColor="#f5304c"
key={index}
{...props}
>
<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="37" rx="3" ry="3" width="200" height="7" />
</ContentLoader>
))}
<ContentLoader
speed={2}
viewBox="0 0 290 50"
backgroundColor="#3b3b3b"
foregroundColor="#f5304c"
{...props}
>
<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="37" rx="3" ry="3" width="200" height="7" />
</ContentLoader>
</div>
);
}
@@ -67,29 +41,24 @@ export function LoadingBlogRead(props: any) {
useEffect(() => {
const updateRectCount = () => {
// Calculate the number of rectangles based on window height
const windowHeight = window.innerHeight;
const rectHeight = 46; // Height of each rectangle
const count = Math.floor(windowHeight / rectHeight); // Number of rectangles that fit
const rectHeight = 46;
const count = Math.floor(windowHeight / rectHeight);
setNumRects(count);
};
// Update on mount and window resize
updateRectCount();
window.addEventListener('resize', updateRectCount);
// Cleanup event listener on unmount
return () => {
window.removeEventListener('resize', updateRectCount);
};
}, []);
// Only render on the client
if (!isClient) {
return null;
}
// Dynamically set the viewBox height based on the number of rectangles
const viewBoxHeight = window.innerHeight / 3;
return (
@@ -106,10 +75,10 @@ export function LoadingBlogRead(props: any) {
{Array.from({ length: numRects }).map((_, i) => (
<rect
x="0"
y={45 + i * 14} // Adjust position based on index
y={45 + i * 14}
rx="3"
ry="3"
width={Math.random() * (300 - 80) + 80} // Random width
width={Math.random() * (300 - 80) + 80}
height="9"
key={i}
/>
+31 -51
View File
@@ -1,65 +1,45 @@
"use client";
import React, { useEffect, useState } from 'react';
import '../../Blog.css';
import '../Blog.css';
import MarkdownRenderer from './MarkdownRenderer';
import { LoadingBlogRead } from '../Loading';
import { BlogT2 } from '../page';
export default function Blog({ params }: { params: Promise<{ id: string }> }) {
const [blog, setBlog] = useState({ title: '', date: '', markdown: '' });
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const [blog, setBlog] = useState<BlogT2 | null>(null);
const date = new Date(blog.date).toLocaleString('default', { month: 'long', day: 'numeric', year: 'numeric' });
useEffect(() => {
const fetchBlog = async () => {
try {
console.log('obtaining blog')
const response = await fetch(`/api/${(await params).id}`);
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) {
useState(() => {
params.then(({id}) => {
fetch(`/api/${id}`).then((res) => {
res.json().then((data) => {
setBlog(data);
}).catch((err) => {
console.error(err);
});
});
});
});
if (blog !== null) {
return (
<main>
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
<div>Error: {error}</div>;
<div className='blog_container'>
<h2 className='blog_title'>{blog.title}</h2>
<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>
</main>
)
);
}
return (
<main>
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
<div className='blog_container'>
<h2 className='blog_title'>{blog.title}</h2>
<p className='date'>{date}</p>
<div className='markdown'><MarkdownRenderer>{blog.markdown.replaceAll('\n-\n', '\n<br/>\n')}</MarkdownRenderer></div>
</div>
</main>
);
else
return (
<main>
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
<div className='blog_container'>
<LoadingBlogRead/>
</div>
</main>
);
}
+104 -118
View File
@@ -1,143 +1,129 @@
"use client";
import React, { useEffect, useState } from 'react';
import '../Blog.css';
import LoadingBlog from './Loading';
import './Blog.css';
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]: {
background: string,
color: string,
} } = {
default: {
background: "#191919",
color: "#D4D4D4"
},
gray: {
background: "#252525",
color: "#9B9B9B"
},
brown: {
background: "#2E2724",
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"
}
default: { background: "#191919", color: "#D4D4D4" },
gray: { background: "#252525", color: "#9B9B9B" },
brown: { background: "#2E2724", 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() {
const [data, setData] = useState<Blog[]>([]);
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const [items, setItems] = useState<BlogT[]>([]);
const [hasMore, setHasMore] = useState(true);
const [cursor, setCursor] = useState('');
useEffect(() => {
const fetchData = async () => {
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();
}, []);
if (error) {
if (error) {
return (
<main>
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
<div>Error: {error}</div>;
</main>
)
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);
});
}
} if (loading) {
return (
<main>
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
<LoadingBlog/>
</main>
);
};
const refreshData = () => {
setItems([]);
setHasMore(true);
setCursor('');
}
document.title = "Leo's blog";
useEffect(() => {
document.title = "Leo's blog";
fetchData();
}, []);
return (
<main>
<a href="/blog"><h1 className='name'>Leo's blog</h1></a>
<div className='container'>
{data.map((blog, i) => {
return (
<a key={i} href={`/blog/${blog.id}`} style={{ textDecoration: 'none' }}>
<div className='blog'>
<h2 className='title'>{blog.title}</h2>
<p className='date'>{new Date(blog.date).toLocaleString('default', { month: 'long', day: 'numeric', year: 'numeric' })}</p>
<p className='description'>{blog.description}</p>
{blog.tags.map((tag, i) => (
notionColors[tag.color] !== undefined ?
<span key={i} style={{
background: notionColors[tag.color].background,
color: notionColors[tag.color].color,
fontSize: '12px',
border: '5px solid ' + notionColors[tag.color].background,
fontFamily: 'sans-serif',
borderRadius: '5px',
display: 'inline-block',
marginTop: '8px'
}}>{tag.name}</span>
<InfiniteScroll
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' }}>
<div className='blog'>
<h2 className='title'>{blog.title}</h2>
<p className='date'>{new Date(blog.date).toLocaleString('default', { month: 'long', day: 'numeric', year: 'numeric' })}</p>
<p className='description'>{blog.description}</p>
{blog.tags.map((tag, i) => (
notionColors[tag.color] !== undefined ?
<span key={i} style={{
background: notionColors[tag.color].background,
color: notionColors[tag.color].color,
fontSize: '12px',
border: '5px solid ' + notionColors[tag.color].background,
fontFamily: 'sans-serif',
borderRadius: '5px',
display: 'inline-block',
marginTop: '8px',
marginLeft: i === 0 ? '0px' : '8px',
}}>{tag.name}</span>
:
<span key={i} style={{
background: tag.color,
color: tag.color,
fontSize: '12px',
border: '5px solid ' + tag.color,
fontFamily: 'sans-serif',
borderRadius: '5px',
display: 'inline-block',
marginTop: '8px'
}}>{tag.name}</span>
))}
</div>
</a>
);
})}
<span key={i} style={{
background: tag.color,
color: tag.color,
fontSize: '12px',
border: '5px solid ' + tag.color,
fontFamily: 'sans-serif',
borderRadius: '5px',
display: 'inline-block',
marginTop: '8px',
marginLeft: i === 0 ? '0px' : '8px',
}}>{tag.name}</span>
))}
</div>
</a>
))}
</InfiniteScroll>
</div>
</main>
);