mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-09 20:56:02 -05:00
Add recent events to map pool templates
This commit is contained in:
@@ -14,12 +14,19 @@ import { split, startsWith } from "~/utils/strings";
|
||||
import { CrossIcon } from "./icons/Cross";
|
||||
import { ArrowLongLeftIcon } from "./icons/ArrowLongLeft";
|
||||
import * as React from "react";
|
||||
import type { CalendarEvent } from "~/db/types";
|
||||
|
||||
export type MapPoolSelectorProps = {
|
||||
mapPool: MapPool;
|
||||
handleRemoval?: () => void;
|
||||
handleMapPoolChange: (mapPool: MapPool) => void;
|
||||
handleMapPoolChange: (
|
||||
mapPool: MapPool,
|
||||
event?: Pick<CalendarEvent, "id" | "name">
|
||||
) => void;
|
||||
className?: string;
|
||||
recentEvents?: Array<
|
||||
Pick<CalendarEvent, "id" | "name"> & { serializedMapPool: string }
|
||||
>;
|
||||
};
|
||||
|
||||
export function MapPoolSelector({
|
||||
@@ -27,6 +34,7 @@ export function MapPoolSelector({
|
||||
handleMapPoolChange,
|
||||
handleRemoval,
|
||||
className,
|
||||
recentEvents,
|
||||
}: MapPoolSelectorProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -56,6 +64,17 @@ export function MapPoolSelector({
|
||||
handleMapPoolChange(MapPool[presetId]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (startsWith(template, "recent-event:")) {
|
||||
const [, eventId] = split(template, ":");
|
||||
|
||||
const event = recentEvents?.find((e) => e.id.toString() === eventId);
|
||||
|
||||
if (event) {
|
||||
handleMapPoolChange(new MapPool(event.serializedMapPool), event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -80,6 +99,7 @@ export function MapPoolSelector({
|
||||
<MapPoolTemplateSelect
|
||||
value={template}
|
||||
handleChange={handleTemplateChange}
|
||||
recentEvents={recentEvents}
|
||||
/>
|
||||
</div>
|
||||
<MapPoolStages
|
||||
@@ -256,7 +276,10 @@ type MapModePresetId = "ANARCHY" | "ALL" | ModeShort;
|
||||
|
||||
const presetIds: MapModePresetId[] = ["ANARCHY", "ALL", ...modesShort];
|
||||
|
||||
type MapPoolTemplateValue = "none" | `preset:${MapModePresetId}`;
|
||||
type MapPoolTemplateValue =
|
||||
| "none"
|
||||
| `preset:${MapModePresetId}`
|
||||
| `recent-event:${string}`;
|
||||
|
||||
function detectTemplate(mapPool: MapPool): MapPoolTemplateValue {
|
||||
for (const presetId of presetIds) {
|
||||
@@ -270,11 +293,13 @@ function detectTemplate(mapPool: MapPool): MapPoolTemplateValue {
|
||||
type MapPoolTemplateSelectProps = {
|
||||
value: MapPoolTemplateValue;
|
||||
handleChange: (newValue: MapPoolTemplateValue) => void;
|
||||
recentEvents?: Pick<CalendarEvent, "id" | "name">[];
|
||||
};
|
||||
|
||||
function MapPoolTemplateSelect({
|
||||
handleChange,
|
||||
value,
|
||||
recentEvents,
|
||||
}: MapPoolTemplateSelectProps) {
|
||||
const { t } = useTranslation(["game-misc", "common"]);
|
||||
|
||||
@@ -302,6 +327,15 @@ function MapPoolTemplateSelect({
|
||||
</option>
|
||||
))}
|
||||
</optgroup>
|
||||
{recentEvents && recentEvents.length > 0 && (
|
||||
<optgroup label={t("common:maps.template.yourRecentEvents")}>
|
||||
{recentEvents.map((event) => (
|
||||
<option key={event.id} value={`recent-event:${event.id}`}>
|
||||
{event.name}
|
||||
</option>
|
||||
))}
|
||||
</optgroup>
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
);
|
||||
|
||||
21
app/db/models/calendar/findRecentMapPoolsByAuthorId.sql
Normal file
21
app/db/models/calendar/findRecentMapPoolsByAuthorId.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
select
|
||||
"CalendarEvent"."id",
|
||||
"CalendarEvent"."name",
|
||||
json_group_array(
|
||||
json_object(
|
||||
'stageId',
|
||||
"MapPoolMap"."stageId",
|
||||
'mode',
|
||||
"MapPoolMap"."mode"
|
||||
)
|
||||
) as "mapPool"
|
||||
from
|
||||
"CalendarEvent"
|
||||
join "MapPoolMap" on "CalendarEvent"."id" = "MapPoolMap"."calendarEventId"
|
||||
where
|
||||
"CalendarEvent"."authorId" = @authorId
|
||||
group by
|
||||
"CalendarEvent"."id"
|
||||
order by
|
||||
"CalendarEvent"."id" desc
|
||||
limit 5
|
||||
@@ -36,6 +36,7 @@ import upcomingEventsSql from "./upcomingEvents.sql";
|
||||
import createMapPoolMapSql from "./createMapPoolMap.sql";
|
||||
import deleteMapPoolMapsSql from "./deleteMapPoolMaps.sql";
|
||||
import findMapPoolByEventIdSql from "./findMapPoolByEventId.sql";
|
||||
import findRecentMapPoolsByAuthorIdSql from "./findRecentMapPoolsByAuthorId.sql";
|
||||
|
||||
const createStm = sql.prepare(createSql);
|
||||
const updateStm = sql.prepare(updateSql);
|
||||
@@ -467,3 +468,22 @@ export function eventsToReport(authorId?: CalendarEvent["authorId"]) {
|
||||
}) as Array<Pick<CalendarEvent, "id" | "name">>
|
||||
).map((row) => ({ id: row.id, name: row.name }));
|
||||
}
|
||||
|
||||
const findRecentMapPoolsByAuthorIdStm = sql.prepare(
|
||||
findRecentMapPoolsByAuthorIdSql
|
||||
);
|
||||
export function findRecentMapPoolsByAuthorId(
|
||||
authorId: CalendarEvent["authorId"]
|
||||
) {
|
||||
return (
|
||||
findRecentMapPoolsByAuthorIdStm.all({ authorId }) as Array<
|
||||
Pick<CalendarEvent, "id" | "name"> & {
|
||||
mapPool: string;
|
||||
}
|
||||
>
|
||||
).map((row) => ({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
serializedMapPool: MapPool.serialize(JSON.parse(row.mapPool)),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -192,6 +192,9 @@ export const loader = async ({ request }: LoaderArgs) => {
|
||||
|
||||
return json({
|
||||
managedBadges: db.badges.managedByUserId(user.id),
|
||||
recentEventsWithMapPools: db.calendarEvents.findRecentMapPoolsByAuthorId(
|
||||
user.id
|
||||
),
|
||||
eventToEdit: canEditEvent
|
||||
? {
|
||||
...eventToEdit,
|
||||
@@ -520,7 +523,8 @@ function BadgesAdder() {
|
||||
function MapPoolSection() {
|
||||
const { t } = useTranslation(["game-misc", "common"]);
|
||||
|
||||
const { eventToEdit } = useLoaderData<typeof loader>();
|
||||
const { eventToEdit, recentEventsWithMapPools } =
|
||||
useLoaderData<typeof loader>();
|
||||
const [mapPool, setMapPool] = React.useState<MapPool>(
|
||||
eventToEdit?.mapPool ? new MapPool(eventToEdit.mapPool) : MapPool.EMPTY
|
||||
);
|
||||
@@ -539,6 +543,7 @@ function MapPoolSection() {
|
||||
mapPool={mapPool}
|
||||
handleRemoval={() => setIncludeMapPool(false)}
|
||||
handleMapPoolChange={setMapPool}
|
||||
recentEvents={recentEventsWithMapPools}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -31,6 +31,8 @@ import { type SendouRouteHandle } from "~/utils/remix";
|
||||
import { MapPoolSelector, MapPoolStages } from "~/components/MapPoolSelector";
|
||||
import { EditIcon } from "~/components/icons/Edit";
|
||||
import { useOnce } from "~/hooks/useOnce";
|
||||
import { getUser } from "~/modules/auth";
|
||||
import type { CalendarEvent } from "~/db/types";
|
||||
|
||||
const AMOUNT_OF_MAPS_IN_MAP_LIST = stageIds.length * 2;
|
||||
|
||||
@@ -61,6 +63,7 @@ export const handle: SendouRouteHandle = {
|
||||
};
|
||||
|
||||
export const loader = async ({ request }: LoaderArgs) => {
|
||||
const user = await getUser(request);
|
||||
const url = new URL(request.url);
|
||||
const calendarEventId = url.searchParams.get("eventId");
|
||||
const t = await i18next.getFixedT(request);
|
||||
@@ -79,6 +82,9 @@ export const loader = async ({ request }: LoaderArgs) => {
|
||||
mapPool: event
|
||||
? db.calendarEvents.findMapPoolByEventId(event.eventId)
|
||||
: null,
|
||||
recentEventsWithMapPools: user
|
||||
? db.calendarEvents.findRecentMapPoolsByAuthorId(user.id)
|
||||
: undefined,
|
||||
title: makeTitle([t("pages.maps")]),
|
||||
};
|
||||
};
|
||||
@@ -118,6 +124,7 @@ export default function MapListPage() {
|
||||
<MapPoolSelector
|
||||
mapPool={mapPool}
|
||||
handleMapPoolChange={handleMapPoolChange}
|
||||
recentEvents={data.recentEventsWithMapPools}
|
||||
/>
|
||||
)}
|
||||
<a
|
||||
@@ -151,12 +158,17 @@ function useSearchParamPersistedMapPool() {
|
||||
|
||||
const [mapPool, setMapPool] = React.useState(initialMapPool);
|
||||
|
||||
const handleMapPoolChange = (newMapPool: MapPool) => {
|
||||
const handleMapPoolChange = (
|
||||
newMapPool: MapPool,
|
||||
event?: Pick<CalendarEvent, "id" | "name">
|
||||
) => {
|
||||
setMapPool(newMapPool);
|
||||
setSearchParams(
|
||||
{
|
||||
pool: newMapPool.serialized,
|
||||
},
|
||||
event
|
||||
? { eventId: event.id.toString() }
|
||||
: {
|
||||
pool: newMapPool.serialized,
|
||||
},
|
||||
{ replace: true, state: { scroll: false } }
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"maps.template": "Vorlage",
|
||||
"maps.template.none": "Keine",
|
||||
"maps.template.presets": "Voreinstellungen",
|
||||
"maps.template.yourRecentEvents": "Deine Events",
|
||||
"maps.template.preset.ANARCHY": "Anarchie-Modi",
|
||||
"maps.template.preset.ALL": "Alle Modi",
|
||||
"maps.template.preset.onlyMode": "Nur {{modeName}}",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"maps.template": "Template",
|
||||
"maps.template.none": "None",
|
||||
"maps.template.presets": "Presets",
|
||||
"maps.template.yourRecentEvents": "Recent Events",
|
||||
"maps.template.preset.ANARCHY": "Anarchy Modes",
|
||||
"maps.template.preset.ALL": "All Modes",
|
||||
"maps.template.preset.onlyMode": "Only {{modeName}}",
|
||||
|
||||
Reference in New Issue
Block a user