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-]+$/); - } - }); });