Add /about

This commit is contained in:
powermaker450 2024-09-15 23:04:32 -04:00
parent be35a0a7c1
commit ae3e95a0c3
20 changed files with 835 additions and 584 deletions

View file

@ -9,8 +9,10 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@types/react-router-dom": "^5.3.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0",
"react-router-dom": "^6.26.2"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.27", "@types/react": "^18.0.27",

File diff suppressed because it is too large Load diff

BIN
public/server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

15
public/three-dots.svg Normal file
View file

@ -0,0 +1,15 @@
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
<svg width="30" height="10" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" fill="#fa5f00">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"/>
<animate attributeName="fill-opacity" from="1" to="1" begin="0s" dur="0.8s" values="1;.5;1" calcMode="linear" repeatCount="indefinite"/>
</circle>
<circle cx="60" cy="15" r="9" fill-opacity="0.3">
<animate attributeName="r" from="9" to="9" begin="0s" dur="0.8s" values="9;15;9" calcMode="linear" repeatCount="indefinite"/>
<animate attributeName="fill-opacity" from="0.5" to="0.5" begin="0s" dur="0.8s" values=".5;1;.5" calcMode="linear" repeatCount="indefinite"/>
</circle>
<circle cx="105" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"/>
<animate attributeName="fill-opacity" from="1" to="1" begin="0s" dur="0.8s" values="1;.5;1" calcMode="linear" repeatCount="indefinite"/>
</circle>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,11 +1,27 @@
import { Main } from "./pages"; import React from 'react'
import { Route, Routes } from 'react-router-dom'
import Main from './pages/Main'
import About from './pages/About'
import SelfHosting from './pages/SelfHosting'
function App() { function App() {
let final: JSX.Element = <p>None</p>; return (
<Routes>
document.location.pathname === "/" && (final = <Main />); <Route
path="/"
return final; index
element={<Main />}
/>
<Route
path="/about"
element={<About />}
/>
<Route
path="/about/selfhosting"
element={<SelfHosting />}
/>
</Routes>
)
} }
export default App; export default App

View file

@ -1,16 +1,30 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Link } from "react-router-dom";
import "../keyframes.css"; import "../keyframes.css";
import { GenericProps } from "../types";
export interface Buttons { export interface Buttons {
name: string; name: string;
link: string; link: string;
} }
export interface ButtonRowProps { export interface ButtonRowProps extends GenericProps {
buttons: Buttons[]; buttons: Buttons[];
} }
export const ButtonRow = ({ buttons }: ButtonRowProps) => { export const ButtonRow = ({ fadeIn, zoomIn, buttons }: ButtonRowProps) => {
const determineClasses = () => {
if (fadeIn) {
return "social-link-area fade-in";
}
if (zoomIn) {
return "social-link-area zoom-in";
}
return "social-link-area";
};
const [shown, changeShown] = useState(false); const [shown, changeShown] = useState(false);
const showItems = () => { const showItems = () => {
@ -23,20 +37,20 @@ export const ButtonRow = ({ buttons }: ButtonRowProps) => {
return ( return (
<div <div
className="social-link-area" className={determineClasses()}
onMouseOver={showItems} onMouseOver={showItems}
onMouseOut={hideItems} onMouseOut={hideItems}
> >
{buttons.map((button, index) => { {buttons.map((button, index) => {
return ( return (
<a <Link
key={button.name} key={button.name}
className="social-link-button" className="social-link-button"
href={button.link} to={button.link}
target="_blank" target={button.link.startsWith("/") ? "_self" : "_blank"}
> >
{button.name} {button.name}
</a> </Link>
); );
})} })}
</div> </div>

View file

@ -1,9 +1,19 @@
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import { TextProps } from "../types";
export interface DescriptionProps { export interface DescriptionProps extends TextProps {
children: ReactNode; children: ReactNode;
main?: boolean;
noBackground?: boolean;
} }
export const Description = ({ children }: DescriptionProps) => { export const Description = ({ noBackground, main, children }: DescriptionProps) => {
return <div className="description">{children}</div>; return (
<div
className={noBackground ? "description no-background" : "description"}
style={main ? {margin: "14px"} : {}}
>
{children}
</div>
);
}; };

View file

@ -0,0 +1,8 @@
export function DoubleSpace() {
return (
<>
<br />
<br />
</>
);
}

View file

@ -1,4 +1,6 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom";
import { GenericProps } from "../types";
export interface TextLink { export interface TextLink {
text: string; text: string;
@ -6,23 +8,23 @@ export interface TextLink {
target?: React.HTMLAttributeAnchorTarget; target?: React.HTMLAttributeAnchorTarget;
} }
export interface TextLinks { export interface TextLinks extends GenericProps {
links: TextLink[]; links: TextLink[];
} }
export const Header = ({ links }: TextLinks) => { export const Header = ({ fadeIn, links }: TextLinks) => {
return ( return (
<div className="header"> <div className={fadeIn ? "header fade-in" : "header"}>
{links.map((link) => { {links && links.map((link) => {
return ( return (
<a <Link
className="header-link" className="header-link"
key={link.text} key={link.text}
href={link.link} to={link.link}
target={link.target ?? "_self"} target={link.target ?? "_self"}
> >
{link.text} {link.text}
</a> </Link>
); );
})} })}
</div> </div>

View file

@ -2,8 +2,15 @@ import React, { ReactNode } from "react";
export interface MessageProps { export interface MessageProps {
children: ReactNode; children: ReactNode;
className?: string;
} }
export const Message = ({ children }: MessageProps) => { export const Message = ({ className, children }: MessageProps) => {
return <div className="message">{children}</div>; return (
<div
className={"message zoom-in" + " " + className}
>
{children}
</div>
);
}; };

View file

@ -1,14 +1,31 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { TextProps } from "../types";
export interface TitleProps { export interface TitleProps extends TextProps {
children: ReactNode; children: ReactNode;
noBackground?: boolean;
glow?: boolean; glow?: boolean;
} }
export const Title = ({ children, glow }: TitleProps) => { export const Title = ({ children, noBackground, glow }: TitleProps) => {
const determineClasses = () => {
let result = "title";
if (noBackground) {
result += " no-background"
}
if (glow) {
result += " glow";
}
return result;
}
return ( return (
<div> <div>
<p className={glow ? "title glow" : "title"}>{children}</p> <p className={determineClasses()}>{children}</p>
</div> </div>
); );
}; };

View file

@ -3,3 +3,4 @@ export * from "./Title";
export * from "./Description"; export * from "./Description";
export * from "./ButtonRow"; export * from "./ButtonRow";
export * from "./Message"; export * from "./Message";
export * from "./DoubleSpace";

View file

@ -24,6 +24,17 @@ body {
animation: zoom-in 0.3s; animation: zoom-in 0.3s;
} }
.at:link, .at:visited {
color: var(--main);
font-weight: bold;
text-decoration: none;
transition: opacity 0.15s, transform 0.15s;
}
.at:hover {
opacity: 0.6;
}
.header { .header {
margin: 10px auto; margin: 10px auto;
text-align: center; text-align: center;
@ -107,6 +118,10 @@ body {
color 0.6s, color 0.6s,
text-shadow 0.6s, text-shadow 0.6s,
transform 0.6s; transform 0.6s;
padding: 42px 96px;
background-color: var(--primary);
border-radius: 17px;
box-shadow: var(--shadow);
} }
.glow:hover { .glow:hover {
@ -116,12 +131,17 @@ body {
} }
.description { .description {
margin: 15px 0; display: inline-block;
max-width: 50%;
font-size: 15px; font-size: 15px;
padding: 24px 48px;
background-color: var(--primary);
border-radius: 17px;
box-shadow: var(--shadow);
} }
.social-link-area { .social-link-area {
margin: 25px 0; margin: 10px 0;
text-align: center; text-align: center;
} }
@ -154,12 +174,20 @@ body {
.message { .message {
width: 75%; width: 75%;
margin: 25px auto; margin: auto;
text-align: center; text-align: center;
padding: 17px; align-content: center;
background-color: var(--primary); }
border-radius: 17px;
box-shadow: var(--shadow); .no-background {
padding: 0;
background-color: transparent;
box-shadow: none;
text-shadow: var(--shadow);
}
.small-font {
font-size: 12px;
} }
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {

View file

@ -85,3 +85,11 @@
opacity: 0; opacity: 0;
} }
} }
.zoom-in {
animation: zoom-in 0.3s;
}
.fade-in {
animation: fade-in 0.3s;
}

View file

@ -1,10 +1,13 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App"; import App from "./App";
import "./index.css"; import "./index.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<BrowserRouter>
<App /> <App />
</BrowserRouter>
</React.StrictMode>, </React.StrictMode>,
); );

45
src/pages/About.tsx Normal file
View file

@ -0,0 +1,45 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { Description, Header, Message, Title, DoubleSpace, ButtonRow, Buttons, TextLink } from '../components'
function About() {
const links: TextLink[] = [
{
text: "Back",
link: "/"
}
]
const buttons: Buttons[] = [
{
name: "Self-Hosting",
link: "/about/selfhosting",
}
];
return (
<>
<Header links={links} />
<Message>
<Title>About</Title>
<Description>
<p>
You've probably met me online and checked out my website, I know you IRL, or you're interested in me.
</p>
<p>
In the wise words of Lester Crest, "don't dawdle!" and read on.
</p>
</Description>
</Message>
<DoubleSpace />
<ButtonRow buttons={buttons} fadeIn />
</>
)
}
export default About;

View file

@ -1,4 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { Link } from "react-router-dom";
import { import {
Buttons, Buttons,
ButtonRow, ButtonRow,
@ -9,7 +10,7 @@ import {
Description, Description,
} from "../components"; } from "../components";
export function Main() { function Main() {
const links: TextLink[] = [ const links: TextLink[] = [
{ {
text: "pHosting", text: "pHosting",
@ -27,6 +28,10 @@ export function Main() {
text: "FreeTube Web", text: "FreeTube Web",
link: "https://tube.povario.com", link: "https://tube.povario.com",
}, },
{
text: "About",
link: "/about"
}
]; ];
const buttons: Buttons[] = [ const buttons: Buttons[] = [
@ -53,6 +58,8 @@ export function Main() {
]; ];
const [timesClicked, changeTimesClicked] = useState(0); const [timesClicked, changeTimesClicked] = useState(0);
const [displayed, changeDisplay] = useState("");
const handlePictureClick = () => { const handlePictureClick = () => {
changeTimesClicked((timesClicked) => timesClicked + 1); changeTimesClicked((timesClicked) => timesClicked + 1);
}; };
@ -62,7 +69,12 @@ export function Main() {
return ( return (
<> <>
<Header links={links} /> <div id="loader" style={{display: displayed === "" ? "none" : "flex"}}>
<img src="/three-dots.svg" height={48} style={{display: "block", marginLeft: "auto", marginRight: "auto", marginTop: "20%", width: "50%"}}/>
</div>
<div id="main" style={{display: displayed}}>
<Header links={links} fadeIn />
<div className="easter-egg-box"> <div className="easter-egg-box">
<img <img
@ -80,19 +92,17 @@ export function Main() {
</p> </p>
</div> </div>
<div className="head"> <div className="head zoom-in">
<Title glow>@powermaker450</Title> <Title noBackground glow>@powermaker450</Title>
<Description>Professional Linux Enjoyer + Self Hosts a Lot</Description> <Description noBackground main>Professional Linux Enjoyer + Self Hosts a Lot</Description>
</div> </div>
<ButtonRow buttons={buttons} /> <ButtonRow buttons={buttons} fadeIn />
<Message> <Message>
<Title>Welcome!</Title> <Title>Welcome!</Title>
</Message>
<Message>
<Description> <Description>
<p>You've reached my homepage.</p> <p>You've reached my homepage.</p>
<p> <p>
@ -101,6 +111,9 @@ export function Main() {
</p> </p>
</Description> </Description>
</Message> </Message>
</div>
</> </>
); );
} }
export default Main;

76
src/pages/SelfHosting.tsx Normal file
View file

@ -0,0 +1,76 @@
import { Link } from 'react-router-dom'
import { Description, DoubleSpace, Header, Message, TextLink, Title } from '../components'
function SelfHosting() {
const links: TextLink[] = [
{
text: "Back",
link: "/about"
}
]
return (
<>
<Header links={links} />
<Message>
<Title>Self-Hosting</Title>
<Description>
<p>
Getting the more obvious stuff out of the way, I like self-hosting. I started my self-hosting journey back in
2021, when all I was was fed up with Netflix not having all the shows I wanted to watch.
</p>
<p>
Eventually, that grew into something much bigger, and suddenly this machine I got from a dumpster was my key
to a privacy suite.
</p>
<img style={{display: "block", margin: "auto", width: "350px"}} src="/public/server.png" />
<p>
Yep. Did I say it was from a dumpster? It's beautiful in it's own way.
</p>
</Description>
<DoubleSpace />
<Description>
<p>
Managing your own server is a learning experience in itself, and with time I learned the
cold, hard, basic skills of being a Linux sysadmin.
Not to say it hasn't paid off.
</p>
<p>
I've learned my way around the terminal, and can comfortably surf through it without worry,
and if I do come across something I don't know, I'll figure out how to do it.
Not to mention the money saved not paying for cloud services. Money's not exactly something that get's thrown at me in my life.
</p>
<p>
Instead of the Google Suite, I use <Link className="at" to="https://nextcloud.com" target="_blank">Nextcloud</Link>.
</p>
<p>
Instead of Spotify, I use <Link className="at" to="https://navidrome.org" target="_blank">Navidrome</Link>.
</p>
<p>
Instead of paying for game hosting, I use <Link className="at" to="https://www.pufferpanel.com">Pufferpanel</Link>.
</p>
<p>
And so on. Between becoming comfortable with the ways of Linux, and saving my family money in the long run, this server
has been a worthwhile investment of my time and effort.
</p>
</Description>
</Message>
<DoubleSpace />
</>
)
}
export default SelfHosting;

View file

@ -1 +0,0 @@
export * from "./Main";

12
src/types.ts Normal file
View file

@ -0,0 +1,12 @@
export interface GenericProps {
className?: string;
fadeIn?: boolean;
zoomIn?: boolean;
zoomInLarge?: boolean;
slide?: "left" | "right";
}
export interface TextProps extends GenericProps {
noBackground?: boolean;
glow?: boolean;
}