Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ NEXT_PUBLIC_DISCORD_LINK= # Optional: URL to your community's Discord server.
NEXT_PUBLIC_OSU_SERVER_LIST_LINK= # Optional: Link to your osu! osu server list page.
NEXT_PUBLIC_KOFI_LINK= # Optional: Your Ko-fi donation page URL.
NEXT_PUBLIC_BOOSTY_LINK= # Optional: Your Boosty.to page URL.
NEXT_PUBLIC_STATUS_PAGE_LINK= # Optional: Link to your status page.
NEXT_PUBLIC_STATUS_PAGE_LINK= # Optional: Link to your status page.
NEXT_PUBLIC_PATCHER_PAGE_ENABLED= # Optional: Set to "true" to show the Sunrise Patcher page in the help section.
17 changes: 17 additions & 0 deletions app/(website)/patcher/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Metadata } from "next";

import { getT } from "@/lib/i18n/utils";

import Page from "./page";

export async function generateMetadata(): Promise<Metadata> {
const t = await getT("pages.patcher.meta");
return {
title: t("title"),
openGraph: {
title: t("title"),
},
};
}

export default Page;
156 changes: 156 additions & 0 deletions app/(website)/patcher/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"use client";

import {
Download,
Eye,
Gauge,
Puzzle,
Settings,
TriangleAlert,
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";

import PrettyHeader from "@/components/General/PrettyHeader";
import RoundedContent from "@/components/General/RoundedContent";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import { useT } from "@/lib/i18n/utils";

const PATCHER_DOWNLOAD_URL
= "https://github.com/SunriseCommunity/osu-patcher/releases/latest/download/osu-patcher-for-sunrise-based-server.rar";

const ORIGINAL_REPO_URL = "https://github.com/rushiiMachine/osu-patcher";

const featureCategories = [
{ key: "relax", icon: <Puzzle className="size-4" /> },
{ key: "pp", icon: <Gauge className="size-4" /> },
{ key: "mods", icon: <Eye className="size-4" /> },
{ key: "ui", icon: <Settings className="size-4" /> },
] as const;

const previews = [
{
key: "settings",
src: "/images/patcher/patcher-settings.png",
},
{
key: "relaxRanking",
src: "/images/patcher/patcher-relax-ranking.png",
},
{
key: "ppCounter",
src: "/images/patcher/patcher-pp-counter.png",
},
] as const;

export default function Patcher() {
const t = useT("pages.patcher");

return (
<div className="flex w-full flex-col space-y-4">
<PrettyHeader
text={t("header")}
icon={<Puzzle />}
roundBottom={true}
/>

<div>
<PrettyHeader text={t("features.title")} icon={<Puzzle />} />
<RoundedContent>
<div className="mx-auto flex w-11/12 flex-col space-y-6">
<Carousel className="w-full">
<CarouselContent className="-ml-1">
{previews.map(({ key, src }) => (
<CarouselItem
key={key}
className="pl-0 md:basis-1/2 lg:basis-1/3"
>
<div className="p-1">
<Card className="scale-95 overflow-hidden border-border/50 pt-0 transition-transform duration-300 hover:scale-100">
<div className="relative aspect-video w-full overflow-hidden bg-muted">
<Image
src={src}
alt={t(`previews.${key}.alt`)}
fill
className="object-cover"
/>
</div>
<CardContent className="p-3">
<h3 className="mb-1 text-sm font-bold">
{t(`previews.${key}.title`)}
</h3>
<p className="text-xs text-muted-foreground">
{t(`previews.${key}.description`)}
</p>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className="left-6 md:-left-12" />
<CarouselNext className="right-6 md:-right-12" />
</Carousel>

<p className="text-sm">
{t.rich("description", {
a: chunks => (
<Link
href={ORIGINAL_REPO_URL}
className="text-primary underline transition-opacity hover:opacity-80"
>
{chunks}
</Link>
),
})}
</p>

<div className="grid grid-cols-1 gap-2 md:grid-cols-2">
{featureCategories.map(({ key, icon }) => (
<Card key={key} className="border-border/50 py-3">
<CardHeader className="px-4 py-0">
<CardTitle className="flex items-center gap-1.5 text-xs font-semibold">
<span className="text-muted-foreground">{icon}</span>
{t(`featureSummary.${key}.title`)}
</CardTitle>
</CardHeader>
<CardContent className="px-4 pb-0 pt-1">
<CardDescription className="text-xs leading-snug">
{t(`featureSummary.${key}.description`)}
</CardDescription>
</CardContent>
</Card>
))}
</div>

<Button size="lg" asChild className="w-full">
<Link href={PATCHER_DOWNLOAD_URL}>
<Download className="size-4" />
{t("download")}
</Link>
</Button>

<div className="flex items-center gap-2 rounded-md bg-destructive/10 p-3 text-sm font-semibold text-destructive">
<TriangleAlert className="size-5 flex-shrink-0" />
{t("banchoWarning")}
</div>
</div>
</RoundedContent>
</div>
</div>
);
}
6 changes: 6 additions & 0 deletions components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ export default function Header() {
</DropdownMenuItem>
)}

{process.env.NEXT_PUBLIC_PATCHER_PAGE_ENABLED === "true" && (
<DropdownMenuItem asChild>
<Link href="/patcher">{t("links.patcher")}</Link>
</DropdownMenuItem>
)}

{(process.env.NEXT_PUBLIC_KOFI_LINK
|| process.env.NEXT_PUBLIC_BOOSTY_LINK) && (
<DropdownMenuItem asChild>
Expand Down
9 changes: 9 additions & 0 deletions components/Header/HeaderMobileDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
LucideHistory,
Menu,
MonitorCog,
Puzzle,
Search,
UserIcon,
Users2,
Expand Down Expand Up @@ -96,6 +97,14 @@ export default function HeaderMobileDrawer() {
},
];

if (process.env.NEXT_PUBLIC_PATCHER_PAGE_ENABLED === "true") {
list.push({
icon: <Puzzle />,
title: t("navigation.patcher"),
url: "/patcher",
});
}

if (process.env.NEXT_PUBLIC_DISCORD_LINK) {
list.push({
icon: <UsersRoundIcon />,
Expand Down
Loading
Loading