mirror of
https://github.com/SrIzan10/mainwebsite.git
synced 2026-06-06 00:56:58 +00:00
feat: switch to nextjs (#7)
* feat: switch to nextjs * chore: move everyting to main dir * fix: blogpostgen * chore: dot? * chore: remove blogposts * fix: everything * chore: why * chore: move to public * chore: fetch * chore: remove slash * chore: aedfa * feat: defenitive nextjs move
This commit is contained in:
27
src/app/(pages)/(root)/page.tsx
Normal file
27
src/app/(pages)/(root)/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import '../../_css/Root.css';
|
||||
import React from 'react';
|
||||
import { SiOsu } from 'react-icons/si';
|
||||
import { FaDiscord, FaGithub, FaMastodon, FaTwitter, FaBlog } from 'react-icons/fa6';
|
||||
import Link from "next/link";
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div>
|
||||
<div className='aboutMeBox'>
|
||||
{/* style={{ borderRadius: '70px' }} */}
|
||||
<Link href='/collab'><Image src='/pfp.webp' alt='main profile picture' width='200' height='200' /></Link>
|
||||
<p>A spanish hobbyist developer and osu! player</p>
|
||||
<p>Stalk me on social media:</p>
|
||||
<div className='icons'>
|
||||
<Link href='https://github.com/SrIzan10'><FaGithub /></Link>
|
||||
<Link href='/blog'><FaBlog /></Link>
|
||||
<Link href='https://discord.com/users/703974042700611634'><FaDiscord /></Link>
|
||||
<Link href='https://social.srizan.dev'><FaMastodon /></Link>
|
||||
<Link href='https://twitter.com/itssrizan'><FaTwitter /></Link>
|
||||
<Link href='https://osu.ppy.sh/users/25350735'><SiOsu /></Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +1,36 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import * as fs from 'node:fs/promises'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
||||
import { BlogNavBar } from "./BlogNavBar.tsx";
|
||||
import '../css/BlogPost.css';
|
||||
import BlogNavBar from "../../../_components/BlogNavBar";
|
||||
import '../../../_css/BlogPost.css';
|
||||
import React from "react";
|
||||
import jsonDataArray from '../../../../../public/blogPosts.json';
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export function BlogPost() {
|
||||
const id = Number(useParams().id);
|
||||
|
||||
if (isNaN(id)) {
|
||||
// if it's not a number, redirect to the blog page
|
||||
window.location.href = '/blog';
|
||||
}
|
||||
|
||||
const [jsonData, setJsonData] = useState<BlogPostJSONResponse>({
|
||||
export default async function Page({ params }: { params: { id: string } }) {
|
||||
const id = parseInt(params.id);
|
||||
if (Number.isNaN(id)) redirect('/blog')
|
||||
let jsonData = {
|
||||
id: 0,
|
||||
title: '',
|
||||
description: '',
|
||||
date: '',
|
||||
fileName: '',
|
||||
fileContent: ''
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const jsonFetch = await import('../../blogPosts.json');
|
||||
const jsonDataArray = jsonFetch.default;
|
||||
const filteredPost = jsonDataArray.filter((post) => post.id === id)[0];
|
||||
if (filteredPost) {
|
||||
setJsonData(filteredPost);
|
||||
} else {
|
||||
document.location.href = '/blog';
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [id]);
|
||||
const filteredPost = jsonDataArray.filter((post) => post.id === id)[0];
|
||||
if (filteredPost) {
|
||||
jsonData = filteredPost;
|
||||
} else {
|
||||
redirect('/blog')
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<Head>
|
||||
<title>{jsonData.title}</title>
|
||||
<meta name="description" content={jsonData.description} />
|
||||
<meta name="og:title" content={jsonData.title} />
|
||||
@@ -49,21 +38,19 @@ export function BlogPost() {
|
||||
<meta name="og:type" content="article" />
|
||||
<meta name="og:url" content={`https://srizan.dev/blog/${jsonData.id}`} />
|
||||
<meta name="og:article:author" content="Sr Izan" />
|
||||
</Helmet>
|
||||
</Head>
|
||||
<BlogNavBar title={jsonData.title} />
|
||||
<div className={'blogPostContent'}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={{
|
||||
code(props) {
|
||||
const { children, className, ...rest } = props
|
||||
code({node, className, children, ...props}) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return match ? (
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
style={atomDark}
|
||||
customStyle={{ backgroundColor: '#171717', outline: 'solid' }}
|
||||
codeTagProps={{ className: 'codeHighlighter' }}
|
||||
language={match[1]}
|
||||
PreTag="div"
|
||||
// eslint-disable-next-line react/no-children-prop
|
||||
children={String(children).replace(/\n$/, '')}
|
||||
/>
|
||||
) : (
|
||||
@@ -77,6 +64,7 @@ export function BlogPost() {
|
||||
)
|
||||
},
|
||||
img(props) {
|
||||
// eslint-disable-next-line jsx-a11y/alt-text
|
||||
return <img {...props} style={{ maxWidth: '100%' }} />
|
||||
}
|
||||
}}>
|
||||
30
src/app/(pages)/blog/page.tsx
Normal file
30
src/app/(pages)/blog/page.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import '../../../app/_css/Blog.css';
|
||||
import Head from 'next/head';
|
||||
import blogPosts from '../../../../public/blogPosts.json'
|
||||
import BlogPostCard from '../../../app/_components/BlogPostCard';
|
||||
import BlogNavBar from '../../../app/_components/BlogNavBar';
|
||||
import BlogRssDial from '@/app/_components/BlogRssDial';
|
||||
|
||||
function Blog() {
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>Blog</title>
|
||||
<meta name="theme-color" content="#0d0d0d" />
|
||||
</Head>
|
||||
<BlogNavBar />
|
||||
<div className="blogPosts">
|
||||
{blogPosts.map((post) => {
|
||||
return (
|
||||
<BlogPostCard {...post} key={post.id} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="bottomRight">
|
||||
<BlogRssDial />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Blog;
|
||||
@@ -1,7 +1,9 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Collab() {
|
||||
return (
|
||||
<div>
|
||||
<img src="/collab.webp" useMap="#image-map" />
|
||||
<Image src="/collab.webp" useMap="#image-map" alt="Collab image" />
|
||||
|
||||
<map name="image-map">
|
||||
<area target="_blank" alt="Sr Izan" title="Sr Izan" href="https://srizan.dev" coords="0,0,280,316" shape="rect" />
|
||||
@@ -1,8 +1,13 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import '../css/BlogNavBar.css'
|
||||
import { Link } from 'react-router-dom';
|
||||
// sadge
|
||||
'use client';
|
||||
|
||||
export function BlogNavBar(props: Props) {
|
||||
import { useEffect, useState } from 'react';
|
||||
import '../_css/BlogNavBar.css'
|
||||
import Link from 'next/link';
|
||||
import Pfp from '../../../public/pfp.webp';
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function BlogNavBar(props: Props) {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -26,7 +31,7 @@ export function BlogNavBar(props: Props) {
|
||||
<img src="/pfp.webp" alt="main profile picture" height="50vh" />
|
||||
<p>{props.title || 'Sr Izan\'s blog'}</p>
|
||||
</div>
|
||||
<Link to={props.title ? '/blog' : '/'} className="backHomeLink">Go back {props.title ? 'to posts' : 'home'}</Link>
|
||||
<Link href={props.title ? '/blog' : '/'} className="backHomeLink">Go back {props.title ? 'to posts' : 'home'}</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
"use client"
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Card from '@mui/material/Card';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import dayjs from 'dayjs'
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
|
||||
import '../css/BlogPostCard.css'
|
||||
import '../_css/BlogPostCard.css'
|
||||
import { Button, CardActions } from "@mui/material";
|
||||
import {Link} from "react-router-dom";
|
||||
import Link from "next/link";
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
export default function BlogPostCard(props: Props) {
|
||||
@@ -25,7 +27,7 @@ export default function BlogPostCard(props: Props) {
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions className={'actions'}>
|
||||
<Link to={`/blog/${props.id}`}><Button size="small">Read</Button></Link>
|
||||
<Link href={`/blog/${props.id}`}><Button size="small">Read</Button></Link>
|
||||
</CardActions>
|
||||
</Box>
|
||||
</Card>
|
||||
44
src/app/_components/BlogRssDial.tsx
Normal file
44
src/app/_components/BlogRssDial.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
'use client'
|
||||
|
||||
import SpeedDial from "@mui/material/SpeedDial";
|
||||
import SpeedDialAction from "@mui/material/SpeedDialAction";
|
||||
import { FaRss, FaAtom } from "react-icons/fa6";
|
||||
import { MdDataObject } from 'react-icons/md'
|
||||
|
||||
const actions = [
|
||||
{ icon: <FaRss />, name: 'RSS' },
|
||||
{ icon: <MdDataObject />, name: 'JSON' },
|
||||
{ icon: <FaAtom />, name: 'Atom' }
|
||||
];
|
||||
|
||||
export default function BlogRssDial() {
|
||||
const handleChange = (event: string) => {
|
||||
switch (event) {
|
||||
case 'RSS':
|
||||
window.location.href = '/blog/rss.xml'
|
||||
break;
|
||||
case 'JSON':
|
||||
window.location.href = '/blog/feed.json'
|
||||
break;
|
||||
case 'Atom':
|
||||
window.location.href = '/blog/atom.xml'
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<SpeedDial
|
||||
ariaLabel="SpeedDial basic example"
|
||||
sx={{ position: 'absolute', bottom: 16, right: 16 }}
|
||||
icon={<FaRss />}
|
||||
>
|
||||
{actions.map((action) => (
|
||||
<SpeedDialAction
|
||||
key={action.name}
|
||||
icon={action.icon}
|
||||
tooltipTitle={action.name}
|
||||
onClick={() => handleChange(action.name)}
|
||||
/>
|
||||
))}
|
||||
</SpeedDial>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
"use client";
|
||||
import { CacheProvider } from "@emotion/react";
|
||||
import createCache from "@emotion/cache";
|
||||
import { useServerInsertedHTML } from "next/navigation";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import theme from "./theme";
|
||||
|
||||
export function RootStyleRegistry({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const [{ cache, flush }] = useState(() => {
|
||||
const cache = createCache({ key: "my" });
|
||||
cache.compat = true;
|
||||
const prevInsert = cache.insert;
|
||||
let inserted: string[] = [];
|
||||
cache.insert = (...args) => {
|
||||
const serialized = args[1];
|
||||
if (cache.inserted[serialized.name] === undefined) {
|
||||
inserted.push(serialized.name);
|
||||
}
|
||||
return prevInsert(...args);
|
||||
};
|
||||
const flush = () => {
|
||||
const prevInserted = inserted;
|
||||
inserted = [];
|
||||
return prevInserted;
|
||||
};
|
||||
return { cache, flush };
|
||||
});
|
||||
|
||||
useServerInsertedHTML(() => {
|
||||
const names = flush();
|
||||
if (names.length === 0) return null;
|
||||
let styles = "";
|
||||
for (const name of names) {
|
||||
styles += cache.inserted[name];
|
||||
}
|
||||
return (
|
||||
<style
|
||||
data-emotion={`${cache.key} ${names.join(" ")}`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: styles,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<CacheProvider value={cache}>
|
||||
<ThemeProvider theme={theme}>{children}</ThemeProvider>
|
||||
</CacheProvider>
|
||||
);
|
||||
}
|
||||
36
src/app/_components/ThemeRegistry/theme.ts
Normal file
36
src/app/_components/ThemeRegistry/theme.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Roboto } from 'next/font/google';
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
const roboto = Roboto({
|
||||
weight: ['300', '400', '500', '700'],
|
||||
subsets: ['latin'],
|
||||
display: 'swap',
|
||||
});
|
||||
|
||||
// Check if window is defined before accessing its properties
|
||||
const prefersDarkMode = typeof window !== 'undefined' ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
||||
|
||||
// Add event listener only if prefersDarkMode is defined
|
||||
if (prefersDarkMode) {
|
||||
prefersDarkMode.addEventListener('change', () => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: prefersDarkMode && prefersDarkMode.matches ? 'dark' : 'light',
|
||||
background: {
|
||||
default: prefersDarkMode && prefersDarkMode.matches ? '#0d0d0d' : '#fafafa',
|
||||
paper: prefersDarkMode && prefersDarkMode.matches ? '#0d0d0d' : '#fafafa',
|
||||
},
|
||||
primary: {
|
||||
main: '#646cff',
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: roboto.style.fontFamily,
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
@@ -7,16 +7,18 @@
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
.codeHighlighter {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
shadow: 0;
|
||||
code .codeHighlighter {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.blogPostContent {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.codeHighlighter {
|
||||
background-color: #FFF;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
.aboutMeBox {
|
||||
width: 480px;
|
||||
height: 360px;
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@@ -17,6 +16,11 @@
|
||||
.icons * {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.icons svg {
|
||||
/* change from 1em */
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.icons *:last-child {
|
||||
margin-right: 0;
|
||||
@@ -26,4 +30,4 @@
|
||||
.aboutMeBox {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
--font-mono: ui-monospace, 'JetBrains Mono', Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
|
||||
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
|
||||
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
a {
|
||||
28
src/app/layout.tsx
Normal file
28
src/app/layout.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
import './globals.css'
|
||||
import { RootStyleRegistry } from './_components/ThemeRegistry/EmotionRootStyleRegistry'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Sr Izan\'s corner for the net',
|
||||
icons: { icon: '/pfp.webp' }
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<RootStyleRegistry>
|
||||
|
||||
{children}
|
||||
</RootStyleRegistry>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
3
src/blog/.idea/.gitignore
generated
vendored
Normal file
3
src/blog/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
8
src/blog/.idea/blog.iml
generated
Normal file
8
src/blog/.idea/blog.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
7
src/blog/.idea/discord.xml
generated
Normal file
7
src/blog/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
8
src/blog/.idea/modules.xml
generated
Normal file
8
src/blog/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/blog.iml" filepath="$PROJECT_DIR$/.idea/blog.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
src/blog/.idea/vcs.xml
generated
Normal file
6
src/blog/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -57,7 +57,7 @@ await glob('./src/blog/**/*.md').then(async (files) => {
|
||||
}
|
||||
})
|
||||
|
||||
fs.writeFileSync('./blogPosts.json', JSON.stringify(data))
|
||||
fs.writeFileSync('./public/blogPosts.json', JSON.stringify(data))
|
||||
fs.writeFileSync('./public/blog/feed.json', feed.json1())
|
||||
fs.writeFileSync('./public/blog/rss.xml', feed.rss2())
|
||||
fs.writeFileSync('./public/blog/atom.xml', feed.atom1())
|
||||
@@ -1,84 +0,0 @@
|
||||
import React from 'react';
|
||||
import MuiAlert, { AlertProps } from "@mui/material/Alert";
|
||||
import Snackbar from "@mui/material/Snackbar";
|
||||
import Button from '@mui/material/Button';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
const modalStyle = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
};
|
||||
|
||||
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
||||
props,
|
||||
ref,
|
||||
) {
|
||||
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />;
|
||||
});
|
||||
export default function AnalyticsNotice() {
|
||||
const [open, setOpen] = React.useState(window.localStorage.getItem('analyticsNotice') !== 'false');
|
||||
|
||||
const [modalOpen, setModalOpen] = React.useState(false);
|
||||
const handleModalOpen = () => setModalOpen(true);
|
||||
const handleClose = (_event?: React.SyntheticEvent | Event, reason?: string) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
setModalOpen(false);
|
||||
window.localStorage.setItem('analyticsNotice', 'false');
|
||||
};
|
||||
|
||||
const action = (
|
||||
<React.Fragment>
|
||||
<Button color="secondary" size="small" onClick={handleModalOpen} sx={{ color: 'white' }}>
|
||||
WAIT WHAT?
|
||||
</Button>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="close"
|
||||
color="inherit"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<CloseIcon fontSize="small" sx={{ color: 'white' }}/>
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Snackbar open={open} onClose={handleClose} action={action}>
|
||||
<Alert severity="info" action={action}>hi this website uses <a href='https://umami.is' style={{ color: "white" }}><u>umami</u></a> for analytics</Alert>
|
||||
</Snackbar>
|
||||
<Modal
|
||||
open={modalOpen}
|
||||
onClose={() => handleClose()}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={modalStyle}>
|
||||
<Typography id="modal-modal-title" variant="h6" component="h2">
|
||||
Analytics
|
||||
</Typography>
|
||||
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
|
||||
Umami is used to track page visits. These aren't sold to anyone and are just to see what you guys like.
|
||||
|
||||
Endpoint is https://analytics.srizan.dev
|
||||
</Typography>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
53
src/main.tsx
53
src/main.tsx
@@ -1,53 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './pages/App.tsx'
|
||||
import './css/index.css'
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import Blog from "./pages/Blog.tsx";
|
||||
import { BlogPost } from "./components/BlogPost.tsx";
|
||||
import AnalyticsNotice from "./components/AnalyticsNotice.tsx";
|
||||
import Collab from './pages/Collab.tsx';
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "/blog",
|
||||
element: <Blog />,
|
||||
},
|
||||
{
|
||||
path: "/blog/:id",
|
||||
element: <BlogPost />
|
||||
},
|
||||
{
|
||||
path: '/collab',
|
||||
element: <Collab />
|
||||
}
|
||||
]);
|
||||
|
||||
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const darkTheme = createTheme({
|
||||
palette: {
|
||||
mode: prefersDarkMode.matches ? 'dark' : 'light',
|
||||
background: {
|
||||
default: prefersDarkMode.matches ? '#0d0d0d' : '#fafafa',
|
||||
paper: prefersDarkMode.matches ? '#0d0d0d' : '#fafafa',
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
prefersDarkMode.addEventListener('change', () => {
|
||||
location.reload()
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<AnalyticsNotice />
|
||||
<RouterProvider router={router} />
|
||||
</ThemeProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
import '../css/App.css'
|
||||
import React from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faDiscord, faGithub, faMastodon, faTwitter } from '@fortawesome/free-brands-svg-icons'
|
||||
import { faBlog, faCircle } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
function App() {
|
||||
const [osuBeat, setOsuBeat] = React.useState(false)
|
||||
return (
|
||||
<div>
|
||||
<div className='aboutMeBox'>
|
||||
{/* style={{ borderRadius: '70px' }} */}
|
||||
<Link to='/collab'><img src='/pfp.webp' alt='main profile picture' height='200px' /></Link>
|
||||
<p>I'm a hobbyist developer and osu! player based on Spain who loves to open-source and to work on teams.</p>
|
||||
<p>Stalk me on social media:</p>
|
||||
<div className='icons'>
|
||||
<a href='https://github.com/SrIzan10'><FontAwesomeIcon icon={faGithub} /></a>
|
||||
<Link to='/blog'><FontAwesomeIcon icon={faBlog} /></Link>
|
||||
<a href='https://discord.com/users/703974042700611634'><FontAwesomeIcon icon={faDiscord} /></a>
|
||||
<a href='https://social.srizan.dev'><FontAwesomeIcon icon={faMastodon} /></a>
|
||||
<a href='https://twitter.com/itssrizan'><FontAwesomeIcon icon={faTwitter} /></a>
|
||||
<a href='https://osu.ppy.sh/users/25350735'><FontAwesomeIcon icon={faCircle} onMouseEnter={() => setOsuBeat(true)} onMouseLeave={() => setOsuBeat(false)} beatFade={osuBeat} /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,66 +0,0 @@
|
||||
import '../css/Blog.css';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import blogPosts from '../../blogPosts.json'
|
||||
import BlogPostCard from "../components/BlogPostCard.tsx";
|
||||
import { BlogNavBar } from "../components/BlogNavBar.tsx";
|
||||
import { faRss, faAtom } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import SpeedDial from '@mui/material/SpeedDial';
|
||||
import SpeedDialAction from '@mui/material/SpeedDialAction';
|
||||
import DataObject from "@mui/icons-material/DataObject";
|
||||
|
||||
const actions = [
|
||||
{ icon: <FontAwesomeIcon icon={faRss} />, name: 'RSS' },
|
||||
{ icon: <DataObject />, name: 'JSON' },
|
||||
{ icon: <FontAwesomeIcon icon={faAtom} />, name: 'Atom' }
|
||||
];
|
||||
|
||||
function Blog() {
|
||||
const handleChange = (event: string) => {
|
||||
switch (event) {
|
||||
case 'RSS':
|
||||
window.location.href = '/blog/rss.xml'
|
||||
break;
|
||||
case 'JSON':
|
||||
window.location.href = '/blog/feed.json'
|
||||
break;
|
||||
case 'Atom':
|
||||
window.location.href = '/blog/atom.xml'
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Blog | Sr Izan's website</title>
|
||||
<meta name="theme-color" content="#0d0d0d" />
|
||||
</Helmet>
|
||||
<BlogNavBar />
|
||||
<div className="blogPosts">
|
||||
{blogPosts.map((post) => {
|
||||
return (
|
||||
<BlogPostCard {...post} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="bottomRight">
|
||||
<SpeedDial
|
||||
ariaLabel="SpeedDial basic example"
|
||||
sx={{ position: 'absolute', bottom: 16, right: 16 }}
|
||||
icon={<FontAwesomeIcon icon={faRss} />}
|
||||
>
|
||||
{actions.map((action) => (
|
||||
<SpeedDialAction
|
||||
key={action.name}
|
||||
icon={action.icon}
|
||||
tooltipTitle={action.name}
|
||||
onClick={() => handleChange(action.name)}
|
||||
/>
|
||||
))}
|
||||
</SpeedDial>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Blog;
|
||||
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user