import clsx from "clsx"; import { SquarePen, Trash } from "lucide-react"; import * as React from "react"; import { useTranslation } from "react-i18next"; import type { MetaFunction } from "react-router"; import { useLoaderData } from "react-router"; import { LinkButton } from "~/components/elements/Button"; import { FormWithConfirm } from "~/components/FormWithConfirm"; import { Image, WeaponImage } from "~/components/Image"; import { Main } from "~/components/Main"; import { YouTubeEmbed } from "~/components/YouTubeEmbed"; import { useUser } from "~/features/auth/core/user"; import { useIsMounted } from "~/hooks/useIsMounted"; import { useSearchParamState } from "~/hooks/useSearchParamState"; import { useTimeFormat } from "~/hooks/useTimeFormat"; import { databaseTimestampToDate } from "~/utils/dates"; import { metaTags, type SerializeFrom } from "~/utils/remix"; import type { SendouRouteHandle } from "~/utils/remix.server"; import type { Unpacked } from "~/utils/types"; import { modeImageUrl, navIconUrl, newVodPage, stageImageUrl, VODS_PAGE, vodVideoPage, } from "~/utils/urls"; import { SendouButton } from "../../../components/elements/Button"; import { action } from "../actions/vods.$id.server"; import { PovUser } from "../components/VodPov"; import { loader } from "../loaders/vods.$id.server"; import type { Vod } from "../vods-types"; import { canEditVideo, secondsToHoursMinutesSecondString } from "../vods-utils"; import styles from "./vods.$id.module.css"; export { action, loader }; export const handle: SendouRouteHandle = { breadcrumb: ({ match }) => { const data = match.data as SerializeFrom | undefined; if (!data) return []; return [ { imgPath: navIconUrl("vods"), href: VODS_PAGE, type: "IMAGE", }, { text: data.vod.title, href: vodVideoPage(data.vod.id), type: "TEXT", }, ]; }, }; export const meta: MetaFunction = (args) => { if (!args.data) return []; return metaTags({ title: args.data.vod.title, description: "Splatoon 3 VoD with timestamps to check out specific weapons as well as map and mode combinations.", location: args.location, }); }; export default function VodPage() { const [start, setStart] = useSearchParamState({ name: "start", defaultValue: 0, revive: Number, }); const isMounted = useIsMounted(); const [autoplay, setAutoplay] = React.useState(false); const data = useLoaderData(); const { t } = useTranslation(["common", "vods"]); const user = useUser(); const { formatDate } = useTimeFormat(); return (

{data.vod.title}

{canEditVideo({ submitterUserId: data.vod.submitterUserId, userId: user?.id, povUserId: typeof data.vod.pov === "string" ? undefined : data.vod.pov?.id, }) ? (
} > {t("common:actions.edit")} } > {t("common:actions.delete")}
) : null}
{data.vod.matches.map((match) => ( { setStart(newStart); setAutoplay(true); window.scrollTo(0, 0); }} /> ))}
); } function Match({ match, setStart, }: { match: Unpacked; setStart: (start: number) => void; }) { const { t } = useTranslation(["game-misc", "weapons"]); const weapon = match.weapons.length === 1 ? match.weapons[0] : null; const weapons = match.weapons.length > 1 ? match.weapons : null; const teamSize = weapons ? weapons.length / 2 : 0; return (
{typeof weapon === "number" ? ( ) : null} {t(`game-misc:MODE_LONG_${match.mode}`)} {weapons ? (
{weapons.slice(0, teamSize).map((weapon, i) => { return ( ); })}
{weapons.slice(teamSize).map((weapon, i) => { const adjustedI = i + teamSize; return ( ); })}
) : null} setStart(match.startsAt)} variant="outlined" > {secondsToHoursMinutesSecondString(match.startsAt)}
); }