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 @@ -6,4 +6,5 @@ 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_PATCHER_PAGE_ENABLED= # Optional: Set to "true" to show the Sunrise Patcher page in the help section.
NEXT_PUBLIC_UNOPTIMIZED_IMAGES= # Optional: Set to "true" to disable Next.js image optimization.
NEXT_PUBLIC_UNOPTIMIZED_IMAGES= # Optional: Set to "true" to disable Next.js image optimization.
NEXT_PUBLIC_ALLOW_LOCAL_IP= # Optional: Set to "true" to allow the image optimizer to fetch loopback/private addresses. Only needed locally.
1 change: 1 addition & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ docker compose -f docker-compose.yml up -d # Creates the container with app and
> ```bash
> NODE_TLS_REJECT_UNAUTHORIZED=0
> NEXT_PUBLIC_UNOPTIMIZED_IMAGES=true
> NEXT_PUBLIC_ALLOW_LOCAL_IP=true
> ```
> to you `.env.local` file.

Expand Down
19 changes: 3 additions & 16 deletions app/(admin)/admin/users/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { FileText, Trophy, User } from "lucide-react";
import { usePathname, useSearchParams } from "next/navigation";
import { use, useCallback, useEffect, useState } from "react";
import { use, useEffect, useState } from "react";

import AdminUserEditEvent from "@/app/(admin)/admin/users/[id]/edit/components/Tabs/AdminUserEditEvents";
import AdminUserEditGeneral from "@/app/(admin)/admin/users/[id]/edit/components/Tabs/AdminUserEditGeneral";
Expand All @@ -21,6 +21,7 @@ import {
} from "@/components/ui/alert-dialog";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useAdminUserSensitive } from "@/lib/hooks/api/user/useAdminUserEdit";
import { createQueryString } from "@/lib/utils/buildQuery";

export default function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = use(params);
Expand All @@ -37,27 +38,13 @@ export default function Page({ params }: { params: Promise<{ id: string }> }) {

const { data: user, isLoading } = useAdminUserSensitive(userId);

const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString());
if (value) {
params.set(name, value);
}
else {
params.delete(name);
}
return params.toString();
},
[searchParams],
);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("tab", activeTab)}`,
);
}, [activeTab, pathname, createQueryString]);
}, [activeTab, pathname]);

const handleTabChange = (value: string) => {
if (value === "events" && !hasAcceptedEventsWarning) {
Expand Down
26 changes: 6 additions & 20 deletions app/(admin)/admin/users/components/UsersSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { PaginationState } from "@tanstack/react-table";
import { Search, Settings } from "lucide-react";
import { usePathname, useSearchParams } from "next/navigation";
import { createContext, useCallback, useEffect, useState } from "react";
import { createContext, useEffect, useState } from "react";

import { adminUserColumns } from "@/app/(admin)/admin/users/components/AdminUserColumns";
import { AdminUserDataTable } from "@/app/(admin)/admin/users/components/AdminUserDataTable";
Expand All @@ -15,6 +15,7 @@ import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { useUserSearchList } from "@/lib/hooks/api/user/useUserSearchList";
import useDebounce from "@/lib/hooks/useDebounce";
import { createQueryString } from "@/lib/utils/buildQuery";
import { tryParseNumber } from "@/lib/utils/type.util";

export const PersonalInfoVisibilityContext = createContext<boolean>(false);
Expand All @@ -38,45 +39,30 @@ export default function UsersSearch() {
const [showPersonalInfo, setShowPersonalInfo] = useState(false);
const [showSettings, setShowSettings] = useState(false);

const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString());
if (value) {
params.set(name, value);
}
else {
params.delete(name);
}
return params.toString();
},
[searchParams],
);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("query", searchValue)}`,
);
}, [searchValue, pathname, createQueryString]);
}, [searchValue, pathname]);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname
}?${
`${pathname}?${
createQueryString("page", pagination.pageIndex.toString())}`,
);
}, [pagination.pageIndex, pathname, createQueryString]);
}, [pagination.pageIndex, pathname]);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("size", pagination.pageSize.toString())}`,
);
}, [pagination.pageSize, pathname, createQueryString]);
}, [pagination.pageSize, pathname]);

const { data, isLoading } = useUserSearchList(
searchValue || undefined,
Expand Down
2 changes: 2 additions & 0 deletions app/(website)/(site)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export default function Home() {
alt="frontpage image"
width={1150}
height={1150}
loading="eager"
className="size-full rounded-lg md:min-h-96 md:min-w-96"
/>
</div>
Expand Down Expand Up @@ -194,6 +195,7 @@ export default function Home() {
src={card.imageUrl || "/placeholder.svg"}
alt={card.titleKey}
fill
sizes="(min-width: 1024px) 33vw, (min-width: 768px) 50vw, 100vw"
className="rounded-t-lg object-cover "
/>
</div>
Expand Down
4 changes: 4 additions & 0 deletions app/(website)/api/getVideo/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ async function getVideoStream(request: Request, videoId: number) {
videoStream.on("end", () => controller.close());
videoStream.on("error", err => controller.error(err));
},
cancel() {
// Browser aborted the response (seek, unmount, navigation)
videoStream.destroy();
},
});

const response = new Response(stream, { status: 206, headers });
Expand Down
19 changes: 5 additions & 14 deletions app/(website)/beatmapsets/[...ids]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Book, Clapperboard, Music2 } from "lucide-react";
import Image from "next/image";
import { usePathname, useSearchParams } from "next/navigation";
import { use, useCallback, useEffect, useState } from "react";
import { use, useEffect, useState } from "react";

import { BeatmapDropdown } from "@/app/(website)/beatmapsets/components/BeatmapDropdown";
import { BeatmapInfoAccordion } from "@/app/(website)/beatmapsets/components/BeatmapInfoAccordion";
Expand All @@ -25,6 +25,7 @@ import { useBeatmapSet } from "@/lib/hooks/api/beatmap/useBeatmapSet";
import { useT } from "@/lib/i18n/utils";
import type { BeatmapResponse } from "@/lib/types/api";
import { BeatmapStatusWeb, GameMode } from "@/lib/types/api";
import { createQueryString } from "@/lib/utils/buildQuery";
import { gameModeToVanilla } from "@/lib/utils/gameMode.util";
import { isInstance, tryParseNumber } from "@/lib/utils/type.util";

Expand Down Expand Up @@ -56,16 +57,6 @@ export default function Beatmapset(props: BeatmapsetProps) {
);
const beatmapSet = beatmapsetQuery.data;

const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set(name, value);

return params.toString();
},
[searchParams],
);

useEffect(() => {
if (!beatmapSet)
return;
Expand Down Expand Up @@ -110,7 +101,7 @@ export default function Beatmapset(props: BeatmapsetProps) {
"",
`${pathname}?${createQueryString("mode", activeMode.toString())}`,
);
}, [activeMode, createQueryString, pathname]);
}, [activeMode, pathname]);

useEffect(() => {
if (!activeBeatmap)
Expand All @@ -121,10 +112,10 @@ export default function Beatmapset(props: BeatmapsetProps) {
null,
"",
`/beatmapsets/${beatmapSetId}/${activeBeatmap.id}?${
searchParams.toString()}`,
new URLSearchParams(window.location.search)}`,
);
}
}, [activeBeatmap, beatmapId, beatmapSetId, searchParams]);
}, [activeBeatmap, beatmapId, beatmapSetId]);

if (beatmapsetQuery.isLoading || !activeMode) {
return (
Expand Down
35 changes: 19 additions & 16 deletions app/(website)/leaderboard/components/UserDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,29 +130,32 @@ export function UserDataTable<TData, TValue>({
data-state={row.getIsSelected() && "selected"}
className="smooth-transition group relative isolate overflow-hidden hover:translate-x-2"
>
{row.getVisibleCells().map(cell => (
{row.getVisibleCells().map((cell, index) => (
<TableCell key={cell.id}>
{/* Row background. Has to live inside a <td> - a <div> as a direct child of <tr> is invalid HTML, the parser relocates it and hydration mismatches. The cell is position: static, so inset-0 still resolves against the relative <tr>. */}
{index === 0 && (
<>
<div className="absolute inset-0 -z-10 overflow-hidden">
<ImageWithFallback
src={`${(row.original as { user: UserResponse }).user.banner_url}&default=false`}
alt="user bg"
fill={true}
objectFit="cover"
className="relative -z-20 -translate-x-2/4"
fallBackSrc="/images/placeholder.png"
fallBackClassName="opacity-0 group-hover:opacity-30"
/>
</div>
<div className="smooth-transition absolute inset-0 -z-10 -mx-1 bg-gradient-to-l from-accent via-accent to-accent/75 group-hover:to-accent/50" />
</>
)}

{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}

<>
<div className="absolute inset-0 -z-10 overflow-hidden">
<ImageWithFallback
src={`${(row.original as { user: UserResponse }).user.banner_url}&default=false`}
alt="user bg"
fill={true}
objectFit="cover"
className="relative -z-20 -translate-x-2/4"
fallBackSrc="/images/placeholder.png"
fallBackClassName="opacity-0 group-hover:opacity-30"
/>
</div>
<div className="smooth-transition absolute inset-0 -z-10 -mx-1 bg-gradient-to-l from-accent via-accent to-accent/75 group-hover:to-accent/50" />
</>
</TableRow>
))
) : (
Expand Down
24 changes: 7 additions & 17 deletions app/(website)/leaderboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { ChartColumnIncreasing } from "lucide-react";
import { usePathname, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";

import { useUserColumns } from "@/app/(website)/leaderboard/components/UserColumns";
import { UserDataTable } from "@/app/(website)/leaderboard/components/UserDataTable";
Expand All @@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import { useUsersLeaderboard } from "@/lib/hooks/api/user/useUsersLeaderboard";
import { useT } from "@/lib/i18n/utils";
import { GameMode, LeaderboardSortType } from "@/lib/types/api";
import { createQueryString } from "@/lib/utils/buildQuery";
import { isInstance, tryParseNumber } from "@/lib/utils/type.util";

export default function Leaderboard() {
Expand Down Expand Up @@ -41,49 +42,38 @@ export default function Leaderboard() {
pageSize: size,
});

const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set(name, value);

return params.toString();
},
[searchParams],
);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("type", leaderboardType.toString())}`,
);
}, [leaderboardType, pathname, createQueryString]);
}, [leaderboardType, pathname]);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("mode", activeMode.toString())}`,
);
}, [activeMode, pathname, createQueryString]);
}, [activeMode, pathname]);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("size", pagination.pageSize.toString())}`,
);
}, [pagination.pageSize, pathname, createQueryString]);
}, [pagination.pageSize, pathname]);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname
}?${
`${pathname}?${
createQueryString("page", pagination.pageIndex.toString())}`,
);
}, [pagination.pageIndex, pathname, createQueryString]);
}, [pagination.pageIndex, pathname]);

const comboboxValues = useMemo(
() => [
Expand Down
13 changes: 2 additions & 11 deletions app/(website)/topplays/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
import { useTopScores } from "@/lib/hooks/api/score/useTopScores";
import { useT } from "@/lib/i18n/utils";
import { GameMode } from "@/lib/types/api";
import { createQueryString } from "@/lib/utils/buildQuery";
import { isInstance } from "@/lib/utils/type.util";

import UserScoreMinimal from "./components/UserScoreMinimal";
Expand Down Expand Up @@ -39,23 +40,13 @@ export default function Topplays() {
const totalCountScores
= data?.find(item => item.total_count !== undefined)?.total_count ?? 0;

const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set(name, value);

return params.toString();
},
[searchParams],
);

useEffect(() => {
window.history.replaceState(
null,
"",
`${pathname}?${createQueryString("mode", activeMode.toString())}`,
);
}, [activeMode, pathname, createQueryString]);
}, [activeMode, pathname]);

return (
<div className="flex w-full flex-col space-y-4">
Expand Down
Loading
Loading