diff --git a/assets/icons/fontawesome/box-archive.svg b/assets/icons/fontawesome/box-archive.svg new file mode 100644 index 00000000..b3c9fc57 --- /dev/null +++ b/assets/icons/fontawesome/box-archive.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--> + <path d="M32 32H480c17.7 0 32 14.3 32 32V96c0 17.7-14.3 32-32 32H32C14.3 128 0 113.7 0 96V64C0 46.3 14.3 32 32 32zm0 128H480V416c0 35.3-28.7 64-64 64H96c-35.3 0-64-28.7-64-64V160zm128 80c0 8.8 7.2 16 16 16H336c8.8 0 16-7.2 16-16s-7.2-16-16-16H176c-8.8 0-16 7.2-16 16z"/> +</svg> diff --git a/src/components/data/SoftwareBuilds.tsx b/src/components/data/SoftwareBuilds.tsx index 1aa52150..235e1700 100644 --- a/src/components/data/SoftwareBuilds.tsx +++ b/src/components/data/SoftwareBuilds.tsx @@ -12,12 +12,14 @@ export interface SoftwareBuildsProps { project: string; version: string; builds?: Build[]; + eol?: boolean; } const SoftwareBuilds = ({ project, version, builds, + eol, }: SoftwareBuildsProps): ReactElement => ( <div className="flex flex-col gap-1"> {builds && @@ -42,7 +44,9 @@ const SoftwareBuilds = ({ target="_blank" className={clsx( "text-gray-100 text-sm text-center font-medium rounded-full p-2 min-w-16 mr-4 inline-flex items-center gap-1", - build.channel === "default" ? "bg-gray-800" : "bg-red-500", + build.channel === "default" && !eol + ? "bg-gray-800" + : "bg-red-500", )} > <DownloadIcon className="w-4 h-4" />#{build.build} diff --git a/src/components/data/SoftwareBuildsTable.tsx b/src/components/data/SoftwareBuildsTable.tsx index 00ae693e..1ba214d3 100644 --- a/src/components/data/SoftwareBuildsTable.tsx +++ b/src/components/data/SoftwareBuildsTable.tsx @@ -11,12 +11,14 @@ export interface SoftwareBuildsTableProps { project: string; version: string; builds: Build[]; + eol?: boolean; } const SoftwareBuildsTable = ({ project, version, builds, + eol, }: SoftwareBuildsTableProps) => { return ( <table className="w-full relative"> @@ -38,7 +40,7 @@ const SoftwareBuildsTable = ({ <span className={clsx( "text-sm font-medium text-gray-100 rounded-full py-2 px-3 min-w-16", - build.channel === "experimental" + build.channel === "experimental" || eol ? "bg-red-500" : "bg-gray-800", )} @@ -59,6 +61,7 @@ const SoftwareBuildsTable = ({ build={build} stable={build.channel === "default"} compact + eol={eol} /> </td> </tr> diff --git a/src/components/data/SoftwarePreview.tsx b/src/components/data/SoftwarePreview.tsx index 9a7af7bf..7513f33e 100644 --- a/src/components/data/SoftwarePreview.tsx +++ b/src/components/data/SoftwarePreview.tsx @@ -1,6 +1,8 @@ import Link from "next/link"; import type { FunctionComponent } from "react"; +import ArchiveIcon from "@/assets/icons/fontawesome/box-archive.svg"; + export interface SoftwarePreviewProps { id: string; name: string; @@ -9,6 +11,7 @@ export interface SoftwarePreviewProps { description?: string; download?: boolean; javadocs?: string; + eol?: boolean; } const SoftwarePreview = ({ @@ -18,6 +21,7 @@ const SoftwarePreview = ({ description, download, javadocs, + eol, }: SoftwarePreviewProps) => ( <Link href={ @@ -33,7 +37,9 @@ const SoftwarePreview = ({ <div className="rounded-lg w-12 h-12 bg-gray-800 p-3"> <Icon /> </div> - <h3 className="font-medium flex-1">{name}</h3> + <h3 className="font-medium flex-1 flex gap-4 items-center"> + {name} {eol && <ArchiveIcon className="fill-current h-6" />} + </h3> </div> {description && ( diff --git a/src/components/input/SoftwareDownloadButton.tsx b/src/components/input/SoftwareDownloadButton.tsx index 16a6b1a5..beabcfeb 100644 --- a/src/components/input/SoftwareDownloadButton.tsx +++ b/src/components/input/SoftwareDownloadButton.tsx @@ -18,6 +18,7 @@ export interface SoftwareDownloadButtonProps { version: string; stable: boolean; compact?: boolean; + eol?: boolean; } const SoftwareDownloadButton = ({ @@ -27,6 +28,7 @@ const SoftwareDownloadButton = ({ version, stable, compact, + eol, }: SoftwareDownloadButtonProps) => { const [copied, setCopied] = useState(""); const [timeoutHandler, setTimeoutHandler] = useState<NodeJS.Timeout | null>( @@ -47,7 +49,7 @@ const SoftwareDownloadButton = ({ className={clsx( "rounded-lg flex flex-row ransition-shadow text-white transition-color hover:shadow-lg", !compact && "w-full md:w-100", - stable + stable && !eol ? "bg-blue-600 hover:bg-blue-500" : "bg-red-500 hover:bg-red-400", )} diff --git a/src/components/layout/DownloadsTree.tsx b/src/components/layout/DownloadsTree.tsx index a8d8b580..592d1a96 100644 --- a/src/components/layout/DownloadsTree.tsx +++ b/src/components/layout/DownloadsTree.tsx @@ -1,10 +1,12 @@ import clsx from "clsx"; +import ArchiveIcon from "@/assets/icons/fontawesome/box-archive.svg"; import { useProject } from "@/lib/service/v2"; interface ProjectSubTreeProps { id: string; name: string; + eol?: boolean; } const ProjectSubTree = ({ @@ -13,13 +15,15 @@ const ProjectSubTree = ({ selectedProject, selectedVersion, onSelect, + eol, }: ProjectSubTreeProps & DownloadsTreeProps) => { const { data: project } = useProject(id); return ( <> - <div className="pl-3 py-1 rounded-md font-bold"> - {project?.project_name ?? name} + <div className="pl-3 py-1 rounded-md font-bold flex gap-2 items-center"> + {project?.project_name ?? name}{" "} + {eol && <ArchiveIcon className="fill-current h-4" />} </div> {project?.versions ?.slice() @@ -28,10 +32,15 @@ const ProjectSubTree = ({ <button key={version} className={clsx( - "pl-6 py-1 rounded-md hover:bg-blue-100 hover:dark:bg-gray-900 transition-colors text-gray-800 dark:text-gray-200 block w-full text-left", + "pl-6 py-1 rounded-md transition-colors text-gray-800 dark:text-gray-200 block w-full text-left", + eol + ? "hover:bg-red-100 hover:dark:bg-red-900" + : "hover:bg-blue-100 hover:dark:bg-gray-900", selectedProject === id && selectedVersion === version && - "bg-blue-100 dark:bg-blue-900", + (eol + ? "bg-red-100 dark:bg-red-900" + : "bg-blue-100 dark:bg-blue-900"), )} onClick={() => onSelect(id, version)} > @@ -54,7 +63,7 @@ const DownloadsTree = (props: DownloadsTreeProps) => { <nav className="w-50 p-2 border-r border-gray-300 overflow-auto"> <ProjectSubTree id="paper" name="Paper" {...props} /> <ProjectSubTree id="velocity" name="Velocity" {...props} /> - <ProjectSubTree id="waterfall" name="Waterfall" {...props} /> + <ProjectSubTree id="waterfall" name="Waterfall" eol {...props} /> </nav> ); }; diff --git a/src/components/layout/NavBar.tsx b/src/components/layout/NavBar.tsx index a69f183b..363bd89a 100644 --- a/src/components/layout/NavBar.tsx +++ b/src/components/layout/NavBar.tsx @@ -83,7 +83,7 @@ const NavBar = ({ component }: NavBarProps) => { <NavDropDownLink href="/software/velocity"> Velocity </NavDropDownLink> - <NavDropDownLink href="/software/waterfall"> + <NavDropDownLink href="/software/waterfall" eol> Waterfall </NavDropDownLink> </NavDropDown> diff --git a/src/components/layout/NavDropDownLink.tsx b/src/components/layout/NavDropDownLink.tsx index b43b3954..a82ec89d 100644 --- a/src/components/layout/NavDropDownLink.tsx +++ b/src/components/layout/NavDropDownLink.tsx @@ -2,11 +2,14 @@ import clsx from "clsx"; import Link from "next/link"; import type { ReactElement, ReactNode } from "react"; +import ArchiveIcon from "@/assets/icons/fontawesome/box-archive.svg"; + export interface NavDropDownLinkProps { href: string; target?: string; className?: string; children: ReactNode; + eol?: boolean; } const NavDropDownLink = ({ @@ -14,20 +17,25 @@ const NavDropDownLink = ({ target, className, children, + eol, }: NavDropDownLinkProps): ReactElement => ( <li className={clsx( "color-gray-200 text-gray-800 hover:text-blue-600 text-sm transition-colors dark:(text-gray-200 hover:text-blue-400)", + eol && "hover:(text-red-600 dark:text-red-400)", className, )} > <Link href={href} - className="px-4 py-2 w-full block" + className={clsx( + "px-4 py-2 w-full", + eol ? "flex items-center gap-2" : "block", + )} role="button" target={target} > - {children} + {children} {eol && <ArchiveIcon className="fill-current h-4" />} </Link> </li> ); diff --git a/src/components/layout/SoftwareDownload.tsx b/src/components/layout/SoftwareDownload.tsx index 2fc7b57f..b85375d7 100644 --- a/src/components/layout/SoftwareDownload.tsx +++ b/src/components/layout/SoftwareDownload.tsx @@ -12,8 +12,9 @@ export interface SoftwareDownloadProps { id: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any icon?: FunctionComponent<any>; - description: string; + description: ReactElement | string; experimentalWarning?: string; + eol?: boolean; } const SoftwareDownload = ({ @@ -22,6 +23,7 @@ const SoftwareDownload = ({ icon: Icon, description, experimentalWarning, + eol, }: SoftwareDownloadProps & ProjectProps): ReactElement => { const [isStable, setStable] = useState(true); const version = isStable @@ -36,7 +38,13 @@ const SoftwareDownload = ({ return ( <> - <header className="max-w-7xl flex flex-row mx-auto px-4 pt-32 pb-16 lg:(pt-48 pb-26) gap-16"> + <header className="max-w-7xl flex flex-row flex-wrap mx-auto px-4 pt-32 pb-16 lg:(pt-48 pb-26) gap-16"> + {eol && ( + <div className="text-center px-4 py-8 -mt-16 font-bold bg-red-400 dark:bg-red-500 shadow-md rounded-lg w-full"> + {project.name} has reached end of life! It is no longer maintained + or supported. + </div> + )} <div className="flex-1"> <div className="flex flex-row mb-6 gap-4 items-center"> <div className="w-12 h-12 rounded-lg bg-gray-800 p-3"> @@ -46,7 +54,9 @@ const SoftwareDownload = ({ </div> <h2 className="font-medium leading-normal lg:(text-5xl leading-normal) text-4xl"> Get {project.name} - <span className={isStable ? "text-blue-600" : "text-red-500"}> + <span + className={isStable && !eol ? "text-blue-600" : "text-red-500"} + > {version} </span> </h2> @@ -60,6 +70,7 @@ const SoftwareDownload = ({ build={latestBuild} version={version} stable={!latestBuild || latestBuild?.channel === "default"} + eol={eol} /> {project.latestExperimentalVersion && ( <button @@ -103,6 +114,7 @@ const SoftwareDownload = ({ project={id} version={version} builds={builds?.builds} + eol={eol} /> </section> </> diff --git a/src/components/layout/SoftwareHeader.tsx b/src/components/layout/SoftwareHeader.tsx index 64c34361..623f1026 100644 --- a/src/components/layout/SoftwareHeader.tsx +++ b/src/components/layout/SoftwareHeader.tsx @@ -1,5 +1,6 @@ import type { FunctionComponent, ReactElement } from "react"; +import ArchiveIcon from "@/assets/icons/fontawesome/box-archive.svg"; import Button from "@/components/input/Button"; export interface SoftwareHeaderProps { @@ -9,8 +10,9 @@ export interface SoftwareHeaderProps { // eslint-disable-next-line @typescript-eslint/no-explicit-any icon?: FunctionComponent<any>; header: ReactElement; - description: string; + description: ReactElement | string; github?: string; + eol?: boolean; } const SoftwareHeader = ({ @@ -21,14 +23,22 @@ const SoftwareHeader = ({ header, description, github, + eol, }: SoftwareHeaderProps): ReactElement => ( - <header className="max-w-7xl flex flex-row mx-auto px-4 pt-32 pb-26 lg:(pt-48 pb-46) gap-16"> + <header className="max-w-7xl flex flex-row flex-wrap mx-auto px-4 pt-32 pb-26 lg:(pt-48 pb-46) gap-16"> + {eol && ( + <div className="text-center px-4 py-8 -mt-16 font-bold bg-red-400 dark:bg-red-500 shadow-md rounded-lg w-full"> + {name} has reached end of life! It is no longer maintained or supported. + </div> + )} <div className="flex-1"> <div className="flex flex-row mb-6 gap-4 items-center"> <div className="w-12 h-12 rounded-lg bg-gray-800 p-3"> {Icon && <Icon />} </div> - <h1 className="font-medium text-xl">{name}</h1> + <h1 className="font-medium text-xl flex gap-4 items-center"> + {name} {eol && <ArchiveIcon className="fill-current h-6" />} + </h1> </div> <h2 className="font-medium leading-normal lg:(text-5xl leading-normal) text-4xl"> {header} @@ -39,6 +49,7 @@ const SoftwareHeader = ({ variant="filled" href={github ?? `/downloads/${id}`} external={Boolean(github)} + className={eol ? "!bg-red-500 !hover:bg-red-400" : ""} > {github ? "GitHub" : "Downloads"} </Button> diff --git a/src/pages/downloads/all.tsx b/src/pages/downloads/all.tsx index 51bf362e..752a8125 100644 --- a/src/pages/downloads/all.tsx +++ b/src/pages/downloads/all.tsx @@ -33,6 +33,8 @@ const LegacyDownloads: NextPage<LegacyDownloadProps> = ({ const [selectedVersion, setSelectedVersion] = useState(initialProjectVersion); const { data: builds } = useVersionBuilds(selectedProject, selectedVersion); + const eol = selectedProject === "waterfall"; + return ( <> <SEO @@ -55,10 +57,16 @@ const LegacyDownloads: NextPage<LegacyDownloadProps> = ({ }} /> <div className="flex-1 overflow-auto"> + {eol && ( + <div className="text-center px-4 py-2 font-bold bg-yellow-400 dark:bg-yellow-500 shadow-md"> + EOL builds are not supported. Proceed at your own risk! + </div> + )} <SoftwareBuildsTable project={selectedProject} version={selectedVersion} builds={builds?.builds ?? []} + eol={eol} /> </div> </div> diff --git a/src/pages/downloads/index.tsx b/src/pages/downloads/index.tsx index df12ac82..855dcba6 100644 --- a/src/pages/downloads/index.tsx +++ b/src/pages/downloads/index.tsx @@ -65,6 +65,7 @@ const Downloads: NextPage = () => { icon={WaterfallIcon} description="Waterfall is a legacy drop-in BungeeCord replacement with some additional improvements to performance and stability." download + eol /> </div> </header> diff --git a/src/pages/downloads/waterfall.tsx b/src/pages/downloads/waterfall.tsx index 04260c2c..cb406038 100644 --- a/src/pages/downloads/waterfall.tsx +++ b/src/pages/downloads/waterfall.tsx @@ -1,3 +1,4 @@ +import Link from "next/link"; import type { ReactElement } from "react"; import WaterfallIcon from "@/assets/brand/waterfall.svg"; @@ -26,7 +27,27 @@ const WaterfallDownloads = ({ project }: ProjectProps): ReactElement => { id="waterfall" project={project} icon={WaterfallIcon} - description="Download Waterfall, our Bungee-compatible upgrade, offering better performance and full compatibility." + description={ + <> + Waterfall has reached end of life. We recommend you transition to{" "} + <Link + className="text-blue-500 hover:text-blue-400 hover:underline" + href="/software/velocity" + > + Velocity + </Link> + . For more information see the{" "} + <a + className="text-blue-500 hover:text-blue-400 hover:underline" + href="https://forums.papermc.io/threads/1088/" + > + announcement + </a> + . <br /> + Download unsupported, archived Waterfall builds below. + </> + } + eol /> </> ); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 3cf9a7a6..bdec108e 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -3,7 +3,6 @@ import Image from "next/image"; import PaperIcon from "@/assets/brand/paper.svg"; import VelocityIcon from "@/assets/brand/velocity.svg"; -import WaterfallIcon from "@/assets/brand/waterfall.svg"; import HomeImage1 from "@/assets/images/home-1.png"; import HomeImage2 from "@/assets/images/home-2.png"; import HomeImage3 from "@/assets/images/home-3.png"; @@ -73,12 +72,6 @@ const Home: NextPage<ProjectProps> = ({ project }) => { icon={VelocityIcon} description="Velocity is a high-performance, scalable Minecraft proxy server that allows players to connect to multiple Minecraft servers under the proxy." /> - <SoftwarePreview - id="waterfall" - name="Waterfall" - icon={WaterfallIcon} - description="Waterfall is a legacy drop-in BungeeCord replacement with some additional improvements to performance and stability." - /> </div> </div> </section> diff --git a/src/pages/software/waterfall/index.tsx b/src/pages/software/waterfall/index.tsx index 4aee815c..bf9f0dbc 100644 --- a/src/pages/software/waterfall/index.tsx +++ b/src/pages/software/waterfall/index.tsx @@ -1,14 +1,10 @@ import Image from "next/image"; +import Link from "next/link"; import type { ReactElement } from "react"; import WaterfallIcon from "@/assets/brand/waterfall.svg"; -import BoltIcon from "@/assets/icons/heroicons/bolt.svg"; -import ChatBubbleLeftRightIcon from "@/assets/icons/heroicons/chat-bubble-left-right.svg"; -import CodeBracketIcon from "@/assets/icons/heroicons/code-bracket.svg"; import CommunityImage from "@/assets/images/community.png"; -import HomeImage1 from "@/assets/images/home-1.png"; import VelocityImage from "@/assets/images/velocity.png"; -import FeatureCard from "@/components/data/FeatureCard"; import Button from "@/components/input/Button"; import SoftwareHeader from "@/components/layout/SoftwareHeader"; import SEO from "@/components/util/SEO"; @@ -35,36 +31,28 @@ const WaterfallHome = ({ project }: HangarProjectProps): ReactElement => { name="Waterfall" versionGroup={project.latestVersionGroup} icon={WaterfallIcon} - header={<>The Bungee-compatible upgrade</>} - description="Waterfall is an upgraded BungeeCord, offering full compatibility with improvements to performance and stability." + header={<>Waterfall has reached end of life</>} + description={ + <> + We recommend you transition to{" "} + <Link + className="text-blue-500 hover:text-blue-400 hover:underline" + href="/software/velocity" + > + Velocity + </Link> + . For more information see the{" "} + <a + className="text-blue-500 hover:text-blue-400 hover:underline" + href="https://forums.papermc.io/threads/1088/" + > + announcement + </a> + . <br /> Archived Waterfall builds and docs are available here. + </> + } + eol /> - <section - id="why" - className="w-full pt-10 pb-5 bg-primary-200 dark:bg-background-dark-80" - > - <div className="max-w-7xl mx-auto"> - <h2 className="font-semibold text-xl md:text-2xl px-6 lg:px-4"> - Why Waterfall? - </h2> - <div className="grid md:grid-cols-3 mt-6 gap-2 px-2 xl:gap-4"> - <FeatureCard - icon={BoltIcon} - label="Fast, smooth, and easy" - description="Waterfall is a simple BungeeCord fork with additional improvements to stability and performance." - /> - <FeatureCard - icon={ChatBubbleLeftRightIcon} - label="An active and growing community" - description="If you encounter any problems, you can come talk with us on Discord and get real time support." - /> - <FeatureCard - icon={CodeBracketIcon} - label="Compatible with Bungee" - description="Everything that works with BungeeCord works with Waterfall. The switch is seamless and easy: Simply swap out the relevant downloads and you’re good to go." - /> - </div> - </div> - </section> <section id="facts" className="flex flex-col max-w-7xl mx-auto px-4 py-8 gap-8 md:(gap-12 py-16)" @@ -82,13 +70,13 @@ const WaterfallHome = ({ project }: HangarProjectProps): ReactElement => { </div> <div className="flex-1"> <h2 className="font-semibold text-2xl md:text-4xl"> - Don't need BungeeCord compatibility? + Need an updated proxy? Use Velocity! </h2> <p className="md:(mt-6 text-xl) text-gray-900 dark:text-gray-100 mt-3"> - If you don’t desperately need BungeeCord plugins on your proxy, - Velocity is the best proxy software available. Designed with - performance and scalability in mind, Velocity is a lot faster and - much more stable than BungeeCord. + All the experience the PaperMC team has gained from working on + Waterfall has applied to Velocity. Designed with performance and + scalability in mind, Velocity is the best proxy software + available. </p> <div className="flex flex-row gap-4 mt-8"> <Button variant="filled" href="/software/velocity" dense> @@ -97,41 +85,6 @@ const WaterfallHome = ({ project }: HangarProjectProps): ReactElement => { </div> </div> </div> - <div className="flex flex-col gap-6 md:(flex-row-reverse gap-8) xl:gap-24 items-center"> - <div className="w-full flex-1 rounded-xl bg-gray-900 aspect-video relative overflow-clip"> - <Image - alt="" - src={HomeImage1} - placeholder="blur" - fill - sizes="(min-width: 80rem) 40rem, (min-width: 768px) 40vw, 100vw" - className="object-cover" - /> - </div> - <div className="flex-1"> - <h2 className="font-semibold text-2xl md:text-4xl"> - Getting Started - </h2> - <p className="md:(mt-6 text-xl) text-gray-900 dark:text-gray-100 mt-3"> - To get started with Waterfall, you will need to download and - install the latest version of the proxy software. Once you're - ready, take a look at our documentation. - </p> - <div className="flex flex-row gap-4 mt-8"> - <Button variant="filled" href="/downloads/waterfall" dense> - Downloads - </Button> - <Button - variant="outlined" - href="https://docs.papermc.io/waterfall/getting-started" - external - dense - > - Documentation - </Button> - </div> - </div> - </div> <div className="flex flex-col gap-6 md:(flex-row-reverse gap-8) xl:gap-24 items-center"> <div className="flex-1"> <h2 className="font-semibold text-2xl md:text-4xl">