mirror of
https://github.com/timmypidashev/web.git
synced 2026-04-14 02:53:51 +00:00
Update post index, rss feed, 404 page
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"@astrojs/sitemap": "^3.2.1",
|
||||
"@react-hook/intersection-observer": "^3.1.2",
|
||||
"lucide-react": "^0.468.0",
|
||||
"marked": "^15.0.6",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-responsive": "^10.0.0",
|
||||
|
||||
10
src/pnpm-lock.yaml
generated
10
src/pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
||||
lucide-react:
|
||||
specifier: ^0.468.0
|
||||
version: 0.468.0(react@18.3.1)
|
||||
marked:
|
||||
specifier: ^15.0.6
|
||||
version: 15.0.6
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
@@ -1353,6 +1356,11 @@ packages:
|
||||
markdown-table@3.0.4:
|
||||
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||
|
||||
marked@15.0.6:
|
||||
resolution: {integrity: sha512-Y07CUOE+HQXbVDCGl3LXggqJDbXDP2pArc2C1N1RRMN0ONiShoSsIInMd5Gsxupe7fKLpgimTV+HOJ9r7bA+pg==}
|
||||
engines: {node: '>= 18'}
|
||||
hasBin: true
|
||||
|
||||
matchmediaquery@0.4.2:
|
||||
resolution: {integrity: sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==}
|
||||
|
||||
@@ -3717,6 +3725,8 @@ snapshots:
|
||||
|
||||
markdown-table@3.0.4: {}
|
||||
|
||||
marked@15.0.6: {}
|
||||
|
||||
matchmediaquery@0.4.2:
|
||||
dependencies:
|
||||
css-mediaquery: 0.1.2
|
||||
|
||||
56
src/src/components/404/glitched-text.tsx
Normal file
56
src/src/components/404/glitched-text.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
const GlitchText = () => {
|
||||
const originalText = 'Error 404';
|
||||
const [characters, setCharacters] = useState(
|
||||
originalText.split("").map(char => ({ char, isGlitched: false }))
|
||||
);
|
||||
const glitchChars = "!<>-_\\/[]{}—=+*^?#________";
|
||||
|
||||
useEffect(() => {
|
||||
const glitchInterval = setInterval(() => {
|
||||
if (Math.random() < 0.2) { // 20% chance to trigger glitch
|
||||
setCharacters(prev => {
|
||||
return originalText.split('').map((originalChar, index) => {
|
||||
if (Math.random() < 0.3) { // 30% chance to glitch each character
|
||||
return {
|
||||
char: glitchChars[Math.floor(Math.random() * glitchChars.length)],
|
||||
isGlitched: true
|
||||
};
|
||||
}
|
||||
return { char: originalChar, isGlitched: false };
|
||||
});
|
||||
});
|
||||
|
||||
// Reset after short delay
|
||||
setTimeout(() => {
|
||||
setCharacters(originalText.split('').map(char => ({
|
||||
char,
|
||||
isGlitched: false
|
||||
})));
|
||||
}, 100);
|
||||
}
|
||||
}, 50);
|
||||
|
||||
return () => clearInterval(glitchInterval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<h1 className="text-6xl font-bold mb-4 relative">
|
||||
<span className="relative inline-block">
|
||||
{characters.map((charObj, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className={charObj.isGlitched ? "text-red" : "text-purple"}
|
||||
>
|
||||
{charObj.char}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GlitchText;
|
||||
38
src/src/components/blog/header.tsx
Normal file
38
src/src/components/blog/header.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from "react";
|
||||
import { RssIcon, TagIcon, TrendingUpIcon } from "lucide-react";
|
||||
|
||||
export const BlogHeader = () => {
|
||||
return (
|
||||
<div className="w-full max-w-6xl mx-auto px-4 pt-24 sm:pt-24">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-purple mb-3 text-center px-4 leading-relaxed">
|
||||
Latest Thoughts <br className="sm:hidden" />
|
||||
& Writings
|
||||
</h1>
|
||||
<div className="flex flex-wrap justify-center gap-4 mb-12 text-sm sm:text-base">
|
||||
<a
|
||||
href="/rss"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-background border border-foreground/20 text-orange hover:text-orange-bright hover:border-orange/50 transition-colors duration-200"
|
||||
>
|
||||
<RssIcon className="w-4 h-4" />
|
||||
<span>RSS Feed</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/blog/tags"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-background border border-foreground/20 text-aqua hover:text-aqua-bright hover:border-aqua/50 transition-colors duration-200"
|
||||
>
|
||||
<TagIcon className="w-4 h-4" />
|
||||
<span>Browse Tags</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/blog/popular"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-background border border-foreground/20 text-blue hover:text-blue-bright hover:border-blue/50 transition-colors duration-200"
|
||||
>
|
||||
<TrendingUpIcon className="w-4 h-4" />
|
||||
<span>Most Popular</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -27,12 +27,7 @@ const formatDate = (dateString: string) => {
|
||||
|
||||
export const BlogPostList = ({ posts }: BlogPostListProps) => {
|
||||
return (
|
||||
<div className="w-full max-w-6xl mx-auto pt-24 sm:pt-24">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-purple mb-12 text-center px-4 leading-relaxed">
|
||||
Latest Thoughts <br className="sm:hidden" />
|
||||
& Writings
|
||||
</h1>
|
||||
|
||||
<div className="w-full max-w-6xl mx-auto">
|
||||
<ul className="space-y-6 md:space-y-10">
|
||||
{posts.map((post) => (
|
||||
<li key={post.slug} className="group px-4 md:px-0">
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
import IndexLayout from "@/layouts/index.astro";
|
||||
|
||||
import GlitchText from "@/components/404/glitched-text";
|
||||
const title = "404 Not Found";
|
||||
---
|
||||
|
||||
<IndexLayout content={{ title: "404 | Timothy Pidashev" }}>
|
||||
<main class="min-h-screen flex flex-col items-center justify-center p-4 text-center">
|
||||
<h1 class="text-6xl font-bold mb-4">404</h1>
|
||||
<p class="text-xl mb-8">Whoops! This page doesn't exist.</p>
|
||||
<GlitchText client:only />
|
||||
<p class="text-xl text-orange mb-8">Whoops! This page doesn't exist :(</p>
|
||||
<button
|
||||
onclick="window.history.back()"
|
||||
class="underline hover:opacity-70 transition-opacity"
|
||||
class="underline text-green hover:opacity-70 transition-opacity"
|
||||
>
|
||||
go back
|
||||
</button>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import ContentLayout from "@/layouts/content.astro";
|
||||
import { BlogHeader } from "@/components/blog/header";
|
||||
import { BlogPostList } from "@/components/blog/post-list";
|
||||
|
||||
const posts = (await getCollection("blog", ({ data }) => {
|
||||
return data.isDraft !== true;
|
||||
})).sort((a, b) => {
|
||||
@@ -22,5 +24,6 @@ const posts = (await getCollection("blog", ({ data }) => {
|
||||
title="Blog | Timothy Pidashev"
|
||||
description="My experiences and technical insights into software development and the ever-evolving world of programming."
|
||||
>
|
||||
<BlogHeader client:load />
|
||||
<BlogPostList posts={posts} client:load />
|
||||
</ContentLayout>
|
||||
|
||||
@@ -5,16 +5,25 @@ import type { APIContext } from "astro";
|
||||
export async function GET(context: APIContext) {
|
||||
const blog = await getCollection("blog");
|
||||
|
||||
const sortedPosts = blog
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
|
||||
return rss({
|
||||
title: "Timothy Pidashev",
|
||||
description: "My experiences and technical insights into software development and the ever-evolving world of programming.",
|
||||
site: context.site!,
|
||||
items: blog.map((post) => ({
|
||||
items: sortedPosts.map((post) => ({
|
||||
title: post.data.title,
|
||||
pubDate: post.data.date,
|
||||
description: post.data.description,
|
||||
link: `/blog/${post.slug}/`,
|
||||
author: post.data.author,
|
||||
categories: post.data.tags,
|
||||
enclosure: post.data.image ? {
|
||||
url: new URL(`blog/${post.slug}/thumbnail.png`, context.site).toString(),
|
||||
type: 'image/jpeg',
|
||||
length: 0
|
||||
} : undefined
|
||||
})),
|
||||
customData: `<language>en-us</language>`,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user