diff --git a/app/components/MapPoolSelector.tsx b/app/components/MapPoolSelector.tsx index 1bc2ea46a..9fe4d955f 100644 --- a/app/components/MapPoolSelector.tsx +++ b/app/components/MapPoolSelector.tsx @@ -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 + ) => void; className?: string; + recentEvents?: Array< + Pick & { 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({ void; + recentEvents?: Pick[]; }; function MapPoolTemplateSelect({ handleChange, value, + recentEvents, }: MapPoolTemplateSelectProps) { const { t } = useTranslation(["game-misc", "common"]); @@ -302,6 +327,15 @@ function MapPoolTemplateSelect({ ))} + {recentEvents && recentEvents.length > 0 && ( + + {recentEvents.map((event) => ( + + ))} + + )} ); diff --git a/app/db/models/calendar/findRecentMapPoolsByAuthorId.sql b/app/db/models/calendar/findRecentMapPoolsByAuthorId.sql new file mode 100644 index 000000000..6ef3c3ba8 --- /dev/null +++ b/app/db/models/calendar/findRecentMapPoolsByAuthorId.sql @@ -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 \ No newline at end of file diff --git a/app/db/models/calendar/queries.server.ts b/app/db/models/calendar/queries.server.ts index 60ad44186..a8788112c 100644 --- a/app/db/models/calendar/queries.server.ts +++ b/app/db/models/calendar/queries.server.ts @@ -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> ).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 & { + mapPool: string; + } + > + ).map((row) => ({ + id: row.id, + name: row.name, + serializedMapPool: MapPool.serialize(JSON.parse(row.mapPool)), + })); +} diff --git a/app/routes/calendar/new.tsx b/app/routes/calendar/new.tsx index 5357ffadb..6d51ba6e5 100644 --- a/app/routes/calendar/new.tsx +++ b/app/routes/calendar/new.tsx @@ -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(); + const { eventToEdit, recentEventsWithMapPools } = + useLoaderData(); const [mapPool, setMapPool] = React.useState( eventToEdit?.mapPool ? new MapPool(eventToEdit.mapPool) : MapPool.EMPTY ); @@ -539,6 +543,7 @@ function MapPoolSection() { mapPool={mapPool} handleRemoval={() => setIncludeMapPool(false)} handleMapPoolChange={setMapPool} + recentEvents={recentEventsWithMapPools} /> ) : ( diff --git a/app/routes/maps.tsx b/app/routes/maps.tsx index 1c63249ea..950e01ee9 100644 --- a/app/routes/maps.tsx +++ b/app/routes/maps.tsx @@ -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() { )} { + const handleMapPoolChange = ( + newMapPool: MapPool, + event?: Pick + ) => { setMapPool(newMapPool); setSearchParams( - { - pool: newMapPool.serialized, - }, + event + ? { eventId: event.id.toString() } + : { + pool: newMapPool.serialized, + }, { replace: true, state: { scroll: false } } ); }; diff --git a/public/locales/de/common.json b/public/locales/de/common.json index bff10d8fb..b7e97835e 100644 --- a/public/locales/de/common.json +++ b/public/locales/de/common.json @@ -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}}", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c69d6a4a5..127e88e7b 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -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}}",