diff --git a/app/features/calendar/loaders/calendar.new.server.ts b/app/features/calendar/loaders/calendar.new.server.ts new file mode 100644 index 000000000..7c454fa7d --- /dev/null +++ b/app/features/calendar/loaders/calendar.new.server.ts @@ -0,0 +1,87 @@ +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { json, redirect } from "@remix-run/node"; +import { requireUser } from "~/features/auth/core/user.server"; +import * as BadgeRepository from "~/features/badges/BadgeRepository.server"; +import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; +import { tournamentFromDB } from "~/features/tournament-bracket/core/Tournament.server"; +import { i18next } from "~/modules/i18n/i18next.server"; +import { canEditCalendarEvent } from "~/permissions"; +import { validate } from "~/utils/remix"; +import { makeTitle } from "~/utils/strings"; +import { tournamentBracketsPage } from "~/utils/urls"; +import { canAddNewEvent } from "../calendar-utils"; +import { canCreateTournament } from "../calendar-utils.server"; + +export const loader = async ({ request }: LoaderFunctionArgs) => { + const t = await i18next.getFixedT(request); + const user = await requireUser(request); + const url = new URL(request.url); + + validate(canAddNewEvent(user), "Not authorized", 401); + + const eventWithTournament = async (key: string) => { + const eventId = Number(url.searchParams.get(key)); + const event = Number.isNaN(eventId) + ? undefined + : await CalendarRepository.findById({ + id: eventId, + includeMapPool: true, + includeTieBreakerMapPool: true, + includeBadgePrizes: true, + }); + + if (!event) return; + + // special tags that are added automatically + const tags = event?.tags?.filter( + (tag) => tag !== "BADGE" && tag !== "FULL_TOURNAMENT", + ); + + if (!event?.tournamentId) return { ...event, tags, tournamentCtx: null }; + + return { + ...event, + tags, + tournamentCtx: ( + await tournamentFromDB({ + tournamentId: event.tournamentId, + user, + }) + ).ctx, + }; + }; + + const eventToEdit = await eventWithTournament("eventId"); + const canEditEvent = + eventToEdit && canEditCalendarEvent({ user, event: eventToEdit }); + + // no editing tournament after the start + if ( + eventToEdit && + eventToEdit.tournamentCtx?.inProgressBrackets && + eventToEdit.tournamentCtx.inProgressBrackets.length > 0 + ) { + return redirect( + tournamentBracketsPage({ tournamentId: eventToEdit.tournamentCtx.id }), + ); + } + + const userCanCreateTournament = canCreateTournament(user); + + return json({ + managedBadges: await BadgeRepository.findManagedByUserId(user.id), + recentEventsWithMapPools: + await CalendarRepository.findRecentMapPoolsByAuthorId(user.id), + eventToEdit: canEditEvent ? eventToEdit : undefined, + eventToCopy: + userCanCreateTournament && !eventToEdit + ? await eventWithTournament("copyEventId") + : undefined, + recentTournaments: + userCanCreateTournament && !eventToEdit + ? await CalendarRepository.findRecentTournamentsByAuthorId(user.id) + : undefined, + title: makeTitle([canEditEvent ? "Edit" : "New", t("pages.calendar")]), + canCreateTournament: userCanCreateTournament, + }); +}; diff --git a/app/features/calendar/routes/calendar.new.tsx b/app/features/calendar/routes/calendar.new.tsx index 444081194..724b37e8e 100644 --- a/app/features/calendar/routes/calendar.new.tsx +++ b/app/features/calendar/routes/calendar.new.tsx @@ -1,9 +1,4 @@ -import type { - LoaderFunctionArgs, - MetaFunction, - SerializeFrom, -} from "@remix-run/node"; -import { json, redirect } from "@remix-run/node"; +import type { MetaFunction, SerializeFrom } from "@remix-run/node"; import { Form, useLoaderData } from "@remix-run/react"; import clsx from "clsx"; import * as React from "react"; @@ -28,28 +23,21 @@ import { TrashIcon } from "~/components/icons/Trash"; import { CALENDAR_EVENT } from "~/constants"; import type { Tables } from "~/db/tables"; import type { Badge as BadgeType, CalendarEventTag } from "~/db/types"; -import { requireUser } from "~/features/auth/core/user.server"; -import * as BadgeRepository from "~/features/badges/BadgeRepository.server"; -import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; import { MapPool } from "~/features/map-list-generator/core/map-pool"; -import { tournamentFromDB } from "~/features/tournament-bracket/core/Tournament.server"; import { BRACKET_NAMES, type TournamentFormatShort, } from "~/features/tournament/tournament-constants"; import { useIsMounted } from "~/hooks/useIsMounted"; -import { i18next } from "~/modules/i18n/i18next.server"; import type { RankedModeShort } from "~/modules/in-game-lists"; -import { canEditCalendarEvent } from "~/permissions"; import { isDefined, nullFilledArray } from "~/utils/arrays"; import { databaseTimestampToDate, getDateAtNextFullHour, getDateWithHoursOffset, } from "~/utils/dates"; -import { validate, type SendouRouteHandle } from "~/utils/remix"; -import { makeTitle, pathnameFromPotentialURL } from "~/utils/strings"; -import { tournamentBracketsPage } from "~/utils/urls"; +import { type SendouRouteHandle } from "~/utils/remix"; +import { pathnameFromPotentialURL } from "~/utils/strings"; import { REG_CLOSES_AT_OPTIONS, type RegClosesAtOption, @@ -59,19 +47,18 @@ import { bracketProgressionToShortTournamentFormat, calendarEventMaxDate, calendarEventMinDate, - canAddNewEvent, datesToRegClosesAt, regClosesAtToDisplayName, validateFollowUpBrackets, } from "../calendar-utils"; -import { canCreateTournament } from "../calendar-utils.server"; import { Tags } from "../components/Tags"; import "~/styles/calendar-new.css"; import "~/styles/maps.css"; +import { loader } from "../loaders/calendar.new.server"; import { action } from "../actions/calendar.new.server"; -export { action }; +export { loader, action }; export const meta: MetaFunction = (args) => { const data = args.data as SerializeFrom | null; @@ -85,80 +72,6 @@ export const handle: SendouRouteHandle = { i18n: ["calendar", "game-misc"], }; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const t = await i18next.getFixedT(request); - const user = await requireUser(request); - const url = new URL(request.url); - - validate(canAddNewEvent(user), "Not authorized", 401); - - const eventWithTournament = async (key: string) => { - const eventId = Number(url.searchParams.get(key)); - const event = Number.isNaN(eventId) - ? undefined - : await CalendarRepository.findById({ - id: eventId, - includeMapPool: true, - includeTieBreakerMapPool: true, - includeBadgePrizes: true, - }); - - if (!event) return; - - // special tags that are added automatically - const tags = event?.tags?.filter( - (tag) => tag !== "BADGE" && tag !== "FULL_TOURNAMENT", - ); - - if (!event?.tournamentId) return { ...event, tags, tournamentCtx: null }; - - return { - ...event, - tags, - tournamentCtx: ( - await tournamentFromDB({ - tournamentId: event.tournamentId, - user, - }) - ).ctx, - }; - }; - - const eventToEdit = await eventWithTournament("eventId"); - const canEditEvent = - eventToEdit && canEditCalendarEvent({ user, event: eventToEdit }); - - // no editing tournament after the start - if ( - eventToEdit && - eventToEdit.tournamentCtx?.inProgressBrackets && - eventToEdit.tournamentCtx.inProgressBrackets.length > 0 - ) { - return redirect( - tournamentBracketsPage({ tournamentId: eventToEdit.tournamentCtx.id }), - ); - } - - const userCanCreateTournament = canCreateTournament(user); - - return json({ - managedBadges: await BadgeRepository.findManagedByUserId(user.id), - recentEventsWithMapPools: - await CalendarRepository.findRecentMapPoolsByAuthorId(user.id), - eventToEdit: canEditEvent ? eventToEdit : undefined, - eventToCopy: - userCanCreateTournament && !eventToEdit - ? await eventWithTournament("copyEventId") - : undefined, - recentTournaments: - userCanCreateTournament && !eventToEdit - ? await CalendarRepository.findRecentTournamentsByAuthorId(user.id) - : undefined, - title: makeTitle([canEditEvent ? "Edit" : "New", t("pages.calendar")]), - canCreateTournament: userCanCreateTournament, - }); -}; - const useBaseEvent = () => { const { eventToEdit, eventToCopy } = useLoaderData();