diff --git a/.gitignore b/.gitignore index 5e89f8c..e8cbf5a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,7 @@ bun.lockb .vscode/ # webstorm -.idea/ \ No newline at end of file +.idea/ + +# docs +public/docs \ No newline at end of file diff --git a/README.md b/README.md index 29ae9d3..7a8c28a 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,16 @@ Just create a `.env` file with the below variables, run `docker compose -d --bui ## Environment Variables -| Variable | Required? | Description | -|----------------------|-----------|-------------------------------------------------------------------------------------| -| `LISTENBRAINZ_TOKEN` | No | Get this from your ListenBrainz [user settings](https://listenbrainz.org/settings/) | -| `LASTFM_API_KEY` | Yes | Get this from your Last.fm [API account](https://www.last.fm/api/account/create) | +| Variable | Required? | Description | +|------------------------|-----------|----------------------------------------------------------------------------------------------------------| +| `LASTFM_API_KEY` | Yes | Get this from your Last.fm [API account](https://www.last.fm/api/account/create) | +| `LISTENBRAINZ_TOKEN` | No | Get this from your ListenBrainz [user settings](https://listenbrainz.org/settings/) | +| `GITHUB_PROJECTS_USER` | No | GitHub username to display in the footer projects list (defaults to `ihatenodejs`) | +| `GITHUB_USERNAME` | No | Fallback GitHub username if `GITHUB_PROJECTS_USER` is not set | +| `GITHUB_PROJECTS_PAT` | No | GitHub personal access token used to increase API limits for the footer projects list | +| `GITHUB_PAT` | No | Fallback GitHub personal access token if `GITHUB_PROJECTS_PAT` is not set | +| `PORT` | No | Server port (defaults to `3000`) | +| `NODE_ENV` | No | Environment mode (`production` or `development`, automatically set by deployment platform) | ## MusicBrainz diff --git a/app/about/page.tsx b/app/about/page.tsx index 476d8e7..bf32410 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -1,232 +1,159 @@ -"use client" - -import Header from '@/components/Header' -import Footer from '@/components/Footer' import Link from '@/components/objects/Link' import Button from '@/components/objects/Button' import FeaturedRepos from '@/components/widgets/FeaturedRepos' -import Image from 'next/image' -import { useState } from 'react' +import GitHubStatsImage from '@/components/widgets/GitHubStatsImage' +import PageHeader from '@/components/objects/PageHeader' +import { Card } from '@/components/ui/Card' +import { CardGrid } from '@/components/ui/CardGrid' import { SiGoogle } from 'react-icons/si' import { TbUserHeart } from 'react-icons/tb' -import { useTranslation } from 'react-i18next' -import { cn } from '@/lib/utils' +import { getFeaturedReposWithMetrics } from '@/lib/github' -export default function About() { - const { t } = useTranslation() - const [imageError, setImageError] = useState(false) - const mainStrings: string[][] = [ - t('about.projects', { returnObjects: true }) as string[], - t('about.hobbies', { returnObjects: true }) as string[], - t('about.devices', { returnObjects: true }) as string[], - t('about.contributions', { returnObjects: true }) as string[], - t('about.featuredProjects', { returnObjects: true }) as string[] - ] +const getGitHubUsername = () => { + return process.env.GITHUB_PROJECTS_USER ?? process.env.GITHUB_USERNAME ?? 'ihatenodejs' +} - const mainSections = [ - t('about.sections.projects'), - t('about.sections.hobbies'), - t('about.sections.devices'), - t('about.sections.contributions'), - t('about.sections.featuredProjects') - ] - return ( -
-
-
-
-
- +interface ContentSection { + title: string + content: React.ReactElement +} + +export default async function About() { + const featuredProjects = await getFeaturedReposWithMetrics() + const githubUsername = getGitHubUsername() + + const sections: ContentSection[] = [ + { + title: "Projects", + content: ( + <> +

+ I have worked on countless projects over the past five years, for the most part. I started learning to code with Python when I was seven and my interest has only evolved from there. I got into web development due to my uncle, who taught my how to write my first lines of HTML. +

+

+ Recently, I have been involved in developing several projects, especially with TypeScript, which is my new favorite language as of a year ago. My biggest project currently is p0ntus, a free service provider for privacy-focused individuals. +

+

+ You will also come to find that I have an addiction to Docker! Almost every project I've made is able to be run in Docker. +

+

+ Me and my developer friends operate an organization called ABOCN, where we primarily maintain a Telegram bot called Kowalski. You can find it on Telegram as @KowalskiNodeBot. +

+

+ I have learned system administration from the past three years of learning Linux for practical use and fun. I currently operate four servers running in the cloud, ran out of Canada, Germany, and the United States. +

+

+ I own a channel called PontusHub on Telegram, where I post updates about my projects, along with commentary and info about my projects related to the Android rooting community. +

+ + ) + }, + { + title: "Hobbies", + content: ( + <> +

+ When I'm not programming, I can typically be found distro hopping or flashing a new ROM to my phone. I also spend a lot of time spreading Next.js and TypeScript propaganda to JavaScript developers. +

+

+ I consider maintaining my devices as a hobby as well, as I devote a lot of time to it. I genuinely enjoy installing Arch, Gentoo, and NixOS frequently, and flashing new ROMs to the phones I own. +

+

+ I am frequently active on my Forgejo server and GitHub, and aim to make daily contributions. I am a big fan of open source software and public domain software (which most of my repos are licensed under). In fact, the website you're currently on is free and open source. It's even under the public domain! +

+

+ When I touch grass, I prefer to walk on the streets, especially in Boston, Massachusetts. I also used to swim competitively, though it has turned into to a casual hobby over time. +

+

+ Editing Wikipedia has also been a good pastime for me, and I have been editing for a year and a half now. As of writing, I have made 6.1k edits to the English Wikipedia. I am also an AfC reviewer, new page reviewer, and rollbacker. You can find me on Wikipedia as OnlyNano. +

+ + ) + }, + { + title: "Devices", + content: ( + <> +

Mobile Devices

+

+ I use a Google Pixel 9 Pro XL (komodo) as my daily driver. It runs Android 16 and is proudly rooted with KernelSU-Next. +

+

+ My previous phone, the Google Pixel 7 Pro (cheetah), is still in use as my secondary WiFi-only device. It runs Android 16 and is proudly rooted with KernelSU-Next. +

+

+ I also have a Google Pixel 3a XL (bonito) which I use as a tertiary device. It runs LineageOS 22.2 and is rooted with Magisk. +

+
+ + +
-

{t('about.title')}

-
+

Laptops

+

+ I currently daily-drive with a 16-inch MacBook Pro with an M4 Max, 64GB of memory, 2TB of storage, 16 core CPU, and a 40 core GPU. +

+

+ I use a Lenovo Thinkpad T470s with macOS Sequoia (using OpenCore) as my "side piece," if you will. I've had it for about a year now, and it's been a great experience. +

+

+ I also own two MacBook Airs (2015 and 2013 base models) and an HP Chromebook, used as secondary devices. The 2013 runs unsupported macOS Sequoia Beta, the 2015 runs Xubuntu, and the Chromebook runs Arch Linux. +

+ + ) + }, + { + title: "Contributions", + content: ( + <> +

+ Most of my repositories have migrated to p0ntus git. My username is aidan. You can find me on GitHub as {githubUsername}. +

+ + + ) + }, + { + title: "Featured Projects", + content: ( + <> +

+ Here's just four of my top projects. Star and fork counts are fetched in real-time from both GitHub and Forgejo APIs. +

+ + + ) + } + ] -
- {mainStrings.map((section, index) => { - if (mainSections[index] === t('about.sections.featuredProjects')) { - return ( -
-

{mainSections[index]}

- {section.map((text, index) => ( -

- {text} -

- ))} - -
- ) - } else if (mainSections[index] === t('about.sections.contributions')) { - return ( -
-

{mainSections[index]}

- {section.map((text, index) => ( -

- {text.split(/(ihatenodejs|p0ntus git|aidan)/).map((part, i) => { - if (part === 'ihatenodejs') { - return ihatenodejs - } - if (part === 'p0ntus git') { - return p0ntus git - } - if (part === 'aidan') { - return aidan - } - return part - })} -

- ))} - {!imageError && ( -
- ihatenodejs's Stats setImageError(true)} - loading="eager" - priority - unoptimized - className="max-w-full h-auto" - /> - ihatenodejs's Top Languages setImageError(true)} - loading="eager" - priority - unoptimized - className="max-w-full h-auto" - /> -
- )} -
- ) - } else if (mainSections[index] === t('about.sections.devices')) { - return ( -
-

{mainSections[index]}

- {Object.entries(section).map(([key, value], index) => ( -
-

{key}

- {(value as unknown as string[]).map((text: string, index: number) => ( -

- {text.split(/(KernelSU-Next|LineageOS 22.2|Android 16|Xubuntu)/).map((part, i) => { - if (part === 'KernelSU-Next') { - return KernelSU-Next - } - if (part === 'LineageOS 22.2') { - return LineageOS 22.2 - } - if (part === 'Android 16') { - return Android 16 - } - if (part === 'OpenCore') { - return OpenCore - } - if (part === 'Xubuntu') { - return Xubuntu - } - return part - })} -

- ))} - {key === "Mobile Devices" && ( -
- - - -
- )} -
- ))} -
- ) - } else if (mainSections[index] === t('about.sections.hobbies')) { - return ( -
-

{mainSections[index]}

- {section.map((text, index) => ( -

- {text.split(/(my Forgejo server|my phone|AfC|OnlyNano)/).map((part, i) => { - if (part === 'my Forgejo server') { - return my Forgejo server - } - if (part === 'my phone') { - return my phone - } - if (part === 'AfC') { - return AfC - } - if (part === 'OnlyNano') { - return OnlyNano - } - return part - })} -

- ))} -
- ) - } else if (mainSections[index] === t('about.sections.projects')) { - return ( -
-

{mainSections[index]}

- {section.map((text, index) => ( -

- {text.split(/(p0ntus|PontusHub|ABOCN|Kowalski|@KowalskiNodeBot)/).map((part, i) => { - if (part === 'p0ntus') { - return p0ntus - } - if (part === 'PontusHub') { - return PontusHub - } - if (part === 'ABOCN') { - return ABOCN - } - if (part === 'Kowalski') { - return Kowalski - } - if (part === '@KowalskiNodeBot') { - return @KowalskiNodeBot - } - return part - })} -

- ))} -
- ) - } else { - return ( -
-

{mainSections[index]}

- {section.map((text, index) => ( -

- {text} -

- ))} -
- ) - } - })} -
-
-