From 0660afe142642c4b92b6d5aeb4d0d1d4abbc4dcb Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Sat, 21 Dec 2024 01:55:19 +0000 Subject: [PATCH] feat: add external nav links, new game, better contrast (#63) ### Explanation _No response_ ### Issue _No response_ ### Attestations - [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [ ] I have run the linter and resolved any errors. - [ ] My pull request uses an appropriate title, matching the conventional commit standards. - [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Reviewed-on: https://codeberg.org/nhcarrigan/portfolio/pulls/63 Co-authored-by: Naomi Carrigan Co-committed-by: Naomi Carrigan --- src/app/activity/page.tsx | 69 -------------------- src/app/contribute/page.tsx | 117 +++++++++++++++++++--------------- src/app/page.tsx | 37 +++++------ src/components/navigation.tsx | 12 ++++ src/config/Games.ts | 6 ++ src/config/NavItems.ts | 5 +- test/config/NavItems.spec.ts | 7 -- 7 files changed, 102 insertions(+), 151 deletions(-) delete mode 100644 src/app/activity/page.tsx diff --git a/src/app/activity/page.tsx b/src/app/activity/page.tsx deleted file mode 100644 index 9453e75..0000000 --- a/src/app/activity/page.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @copyright nhcarrigan - * @license Naomi's Public License - * @author Naomi Carrigan - */ -"use client"; -import { type JSX, useEffect, useState } from "react"; -import { Activity } from "../../components/activity"; -import { Rule } from "../../components/rule"; - -/** - * Renders the /activity page. - * @returns A React Component. - */ -const ActivityComponent = (): JSX.Element => { - const [ activity, setActivity ] = useState< - Array<{ - type: string; - date: Date; - repo: string; - repoName: string; - }> - >([]); - - useEffect(() => { - void fetch("/api/activity"). - then(async(data) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return (await data.json()) as Array<{ - type: string; - date: Date; - repo: string; - repoName: string; - }>; - }). - then((data) => { - setActivity(data); - }); - }, []); - - return ( -
-

{`Recent Activity`}

-
-

{`See what Naomi has been up to lately.`}

- -
    - {activity.map((act, index) => { - return ( - - ); - })} -
-
-
- ); -}; - -export default ActivityComponent; diff --git a/src/app/contribute/page.tsx b/src/app/contribute/page.tsx index f5e64fc..4437cc3 100644 --- a/src/app/contribute/page.tsx +++ b/src/app/contribute/page.tsx @@ -4,72 +4,83 @@ * @author Naomi Carrigan */ "use client"; -import { type JSX, useEffect, useState } from "react"; -import { Issue } from "../../components/issue"; -import { Rule } from "../../components/rule"; +import type { JSX } from "react"; /** * Renders the /contribute page. * @returns A React Component. */ const ContributeComponent = (): JSX.Element => { - const [ issues, setIssues ] = useState< - Array<{ - labels: Array; - number: number; - title: string; - url: string; - body: string; - }> - >([]); - - useEffect(() => { - void fetch("/api/contribute"). - then(async(data) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return (await data.json()) as Array<{ - labels: Array; - number: number; - title: string; - url: string; - body: string; - }>; - }). - then((data) => { - setIssues(data); - }); - }, []); - - if (issues.length === 0) { - return ( -
-

{`Open for Contribution~!`}

-
-

{`Loading...`}

-
-
- ); - } - return (
+ max-w-4xl m-auto mt-16 mb-16 rounded-lg">

{`Open for Contribution~!`}

-

{`Heya! This page lists issues across all of our projects that are currently open for contribution. - We'd love to have you work on one!`}

- -
    - {issues.map((act) => { - return ( - - ); - })} -
+

{`Our issue tracker is currently unavailable while we work with the Codeberg team to address rate limit issues.`}

); + + /* + *To be restored when rate-limit is lifted. + *const [ issues, setIssues ] = useState< + *Array<{ + * labels: Array; + * number: number; + * title: string; + * url: string; + * body: string; + *}> + *>([]); + * + *useEffect(() => { + *void fetch("/api/contribute"). + * then(async(data) => { + * // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + * return (await data.json()) as Array<{ + * labels: Array; + * number: number; + * title: string; + * url: string; + * body: string; + * }>; + * }). + * then((data) => { + * setIssues(data); + * }); + *}, []); + * + *if (issues.length === 0) { + *return ( + *
+ *

{`Open for Contribution~!`}

+ *
+ *

{`Loading...`}

+ *
+ *
+ *); + *} + * + *return ( + *
+ *

{`Open for Contribution~!`}

+ *
+ *

{`Heya! This page lists issues across all of our projects that are currently open for contribution. + * We'd love to have you work on one!`}

+ * + *
    + * {issues.map((act) => { + * return ( + * + * ); + * })} + *
+ *
+ *
+ *); + */ }; export default ContributeComponent; diff --git a/src/app/page.tsx b/src/app/page.tsx index a8716ae..bb13c54 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,6 +3,8 @@ * @license Naomi's Public License * @author Naomi Carrigan */ +import { faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Image from "next/image"; import React, { type JSX } from "react"; import { NavItems } from "../config/NavItems"; @@ -32,31 +34,13 @@ const getLuminance = (hexColor: string): number => { return rl + gl + bl; }; -const adjustColorLuminosity = ( - hexColor: string, - luminosityChange: number, -): string => { - let r = Number.parseInt(hexColor.slice(1, 3), 16); - let g = Number.parseInt(hexColor.slice(3, 5), 16); - let b = Number.parseInt(hexColor.slice(5, 7), 16); - r = Math.round(r * luminosityChange); - g = Math.round(g * luminosityChange); - b = Math.round(b * luminosityChange); - r = Math.min(255, Math.max(0, r)); - g = Math.min(255, Math.max(0, g)); - b = Math.min(255, Math.max(0, b)); - return `#${r.toString(16).padStart(2, "0")}${g. - toString(16). - padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; -}; - const generateColorPair = (): { background: string; color: string } => { const backgroundColor = generateRandomColor(); const backgroundLuminance = getLuminance(backgroundColor); const textColor = backgroundLuminance > 0.5 - ? adjustColorLuminosity(backgroundColor, 0) - : adjustColorLuminosity(backgroundColor, 5); + ? "#00000099" + : "#FFFFFF99"; return { background: backgroundColor, @@ -88,15 +72,26 @@ const Home = (): JSX.Element => { items-center border-solid border-2 rounded-3xl h-14 p-8 my-4" href={item.href} key={item.href} + rel={item.href.startsWith("http") + ? "noreferrer" + : ""} style={{ background: background, borderColor: color, color: color, }} + target={item.href.startsWith("http") + ? "_blank" + : "_self"} > {index % 2 === 1 ? "🩷" - : "🩵"} {item.text} + : "🩵"} {item.text}{" "} + {item.href.startsWith("http") + ? + : null} ); })} diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index 930e689..69465c0 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -7,6 +7,7 @@ import { faBars, faTimes, + faUpRightFromSquare, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Image from "next/image"; @@ -60,10 +61,21 @@ export const Navigation = (): JSX.Element | null => { href={item.href} key={item.href} onClick={toggleMenu} + rel={item.href.startsWith("http") + ? "noreferrer" + : ""} + target={item.href.startsWith("http") + ? "_blank" + : "_self"} > {index % 2 === 1 ? "🩷" : "🩵"} {item.text} + {item.href.startsWith("http") + ? + : null} ); })} diff --git a/src/config/Games.ts b/src/config/Games.ts index 2b85d39..2f2bf67 100644 --- a/src/config/Games.ts +++ b/src/config/Games.ts @@ -187,4 +187,10 @@ export const Games: Array<{ name: "Angel Legion", url: "https://store.steampowered.com/app/1333350/Angel_Legion/", }, + { + alt: "A detailed anime-style illustration of an angelic warrior character against a sunset cityscape background. The character has long blonde hair and wears elaborate white and gold armor with flowing white robes accented by blue and orange fabrics. She has multiple large white wings spread dramatically behind her and wears a golden crown-like headpiece. Golden chains float decoratively around her dress and armor. She's depicted in a dynamic pose with futuristic city towers and a reddish-orange sky visible in the background. The artwork combines elements of fantasy and sci-fi, with ornate details in the character's costume and architectural elements of the setting.", + img: "idle-angels.jpg", + name: "Idle Angels", + url: "https://play.google.com/store/apps/details?id=com.mujoysg.hxbb&hl=en-US&pli=1", + }, ]; diff --git a/src/config/NavItems.ts b/src/config/NavItems.ts index 4806392..a830d6a 100644 --- a/src/config/NavItems.ts +++ b/src/config/NavItems.ts @@ -18,7 +18,6 @@ export const NavItems = [ { href: "/games", text: "Game Screenshots" }, { href: "/team", text: "The NHCarrigan Team" }, { href: "/polycule", text: "Polycule" }, - { href: "/activity", text: "Activity" }, { href: "/art", text: "Art of Naomi" }, { href: "/manifesto", text: "Transfemme Manifesto" }, { href: "/ask", text: "Ask Me Anything!" }, @@ -31,6 +30,10 @@ export const NavItems = [ { href: "/sales", text: "Sales Inquiries" }, { href: "/newsletter", text: "Newsletter" }, { href: "/appeal", text: "Sanction Appeals" }, + { href: "https://games.nhcarrigan.com", text: "Game Dev" }, + { href: "https://merch.nhcarrigan.link", text: "Merchandise" }, + { href: "https://docs.nhcarrigan.com", text: "Documentation" }, + { href: "https://chat.nhcarrigan.com", text: "Support" }, ].sort((a, b) => { return a.text.localeCompare(b.text); }); diff --git a/test/config/NavItems.spec.ts b/test/config/NavItems.spec.ts index 2aaa6fd..7329826 100644 --- a/test/config/NavItems.spec.ts +++ b/test/config/NavItems.spec.ts @@ -20,11 +20,4 @@ describe("nav items", () => { expect(href, "links are not unique").toHaveLength(NavItems.length); expect(text, "names are not unique").toHaveLength(NavItems.length); }); - - it("should be internal links", () => { - expect.hasAssertions(); - for (const nav of NavItems) { - expect(nav.href, `${nav.href} is not internal`).toMatch(/^\/[\da-z-]+$/); - } - }); });