import React, { useState, useEffect } from "react"; import { BrowserRouter as Router, Routes, Route, Link, useParams, } from "react-router-dom"; import MarkdownIt from "markdown-it"; import { full as emoji } from "markdown-it-emoji"; import footnote from "markdown-it-footnote"; import DOMPurify from "dompurify"; import { AuthProvider, useAuth } from "./contexts/AuthContext"; import { ThemeProvider } from "./contexts/ThemeContext"; import AdminDashboard from "./components/AdminDashboard"; import PostEditor from "./components/PostEditor"; import LoginForm from "./components/LoginForm"; import ProtectedRoute from "./components/ProtectedRoute"; import ThemesManager from "./components/ThemesManager"; import ThemeEditor from "./components/ThemeEditor"; const scrollableTablesPlugin = (md) => { const defaultRenderOpen = md.renderer.rules.table_open || function (tokens, idx, options, env, self) { return self.renderToken(tokens, idx, options); }; const defaultRenderClose = md.renderer.rules.table_close || function (tokens, idx, options, env, self) { return self.renderToken(tokens, idx, options); }; md.renderer.rules.table_open = function (tokens, idx, options, env, self) { return ( '
' + defaultRenderOpen(tokens, idx, options, env, self) ); }; md.renderer.rules.table_close = function (tokens, idx, options, env, self) { return defaultRenderClose(tokens, idx, options, env, self) + "
"; }; }; const md = new MarkdownIt({ html: true, // Enable HTML tags in source linkify: true, // Auto-convert URL-like text to links typographer: true, // Enable some language-neutral replacement + quotes beautification breaks: false, // Convert '\n' in paragraphs into
}) .use(scrollableTablesPlugin) // Keep our table scrolling enhancement .use(emoji) // GitHub-style emoji :emoji_name: .use(footnote); // Standard footnotes [^1] import { API_BASE } from "./config"; // Navigation Header Component function NavHeader() { const { isAdmin, user, logout } = useAuth(); const handleLogout = async () => { await logout(); }; return (
GoonBlog
); } // Blog Home Component function BlogHome() { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function getTingyun() { setLoading(true); try { const response = await fetch(`${API_BASE}/posts`); if (!response.ok) throw new Error( `Failed to fetch posts: ${response.statusText}`, ); const postsData = await response.json(); setPosts(postsData); } catch (e) { console.error("Error fetching posts:", e); setError( "Failed to load posts. Please check if the backend server is running.", ); } finally { setLoading(false); } } getTingyun(); }, []); if (loading) { return (

Loading posts...

); } if (error) { return (

Error

{error}

); } return (
{posts.map((post) => (

{post.title}

{post.description}
Read more →
))}
); } // Post View Component function PostView() { const { slug } = useParams(); const [post, setPost] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchPost() { try { setLoading(true); const response = await fetch(`${API_BASE}/posts/${slug}`); if (!response.ok) throw new Error("Post not found"); const postData = await response.json(); setPost(postData); // Update document title document.title = postData.title || "GoonBlog"; } catch (e) { console.error("Error fetching post:", e); setError(e.message); } finally { setLoading(false); } } if (slug) { fetchPost(); } }, [slug]); useEffect(() => { if (post) { const setMeta = (name, content) => { let element = document.querySelector(`meta[name='${name}']`); if (!element) { element = document.createElement("meta"); element.setAttribute("name", name); document.head.appendChild(element); } element.setAttribute("content", content); }; setMeta("og:title", post.title); setMeta("og:description", post.description); setMeta("og:type", "article"); setMeta("og:url", window.location.href); } return () => { const metaTags = [ "og:title", "og:description", "og:type", "og:url", "og:image", ]; metaTags.forEach((name) => { const element = document.querySelector(`meta[name='${name}']`); if (element) { element.remove(); } }); }; }, [post]); useEffect(() => { // Reset title when component unmounts return () => { document.title = "GoonBlog - A Retard's Thoughts"; }; }, []); if (loading) { return (

Loading post...

); } if (error || !post) { return (

Post Not Found

{error || "The requested post could not be found."}

← Back to Home
); } const conceiveFoxFromSemen = (rawMarkdown) => { let processedText = rawMarkdown; let tags = null; let imageCredit = null; let imageSrc = null; let imageAlt = null; let customQuestion = null; const tagsRegex = /tags: (.*)/; const tagsMatch = processedText.match(tagsRegex); if (tagsMatch) { tags = tagsMatch[1].split(",").map((tag) => tag.trim()); processedText = processedText.replace(tagsRegex, "").trim(); } const imageRegex = /!\[(.*?)\]\((.*?)\)\n_Image credit: (.*?)_/; const imageMatch = processedText.match(imageRegex); if (imageMatch) { imageAlt = imageMatch[1]; imageSrc = imageMatch[2]; imageCredit = imageMatch[3]; processedText = processedText.replace(imageRegex, "").trim(); } const questionRegex = /\?\?\? "(.*?)"/; const questionMatch = processedText.match(questionRegex); if (questionMatch) { customQuestion = questionMatch[1]; processedText = processedText.replace(questionRegex, "").trim(); } processedText = processedText .replace(/^title:.*$/m, "") .replace(/^desc:.*$/m, ""); return { processedText, tags, imageSrc, imageAlt, imageCredit, customQuestion, }; }; const { processedText } = conceiveFoxFromSemen(post.content); const htmlContent = md.render(processedText); const sanitizedHtml = DOMPurify.sanitize(htmlContent); return (
← Back to Home

{post.title}

{post.description}

); } function App() { return ( } /> } /> } /> } /> } /> } /> } /> } /> } /> ); } export default App;