diff --git a/app/features/vods/VodRepository.server.ts b/app/features/vods/VodRepository.server.ts new file mode 100644 index 000000000..c3f994df2 --- /dev/null +++ b/app/features/vods/VodRepository.server.ts @@ -0,0 +1,5 @@ +import { db } from "~/db/sql"; + +export function deleteById(id: number) { + return db.deleteFrom("UnvalidatedVideo").where("id", "=", id).execute(); +} diff --git a/app/features/vods/actions/vods.$id.server.ts b/app/features/vods/actions/vods.$id.server.ts new file mode 100644 index 000000000..126253eab --- /dev/null +++ b/app/features/vods/actions/vods.$id.server.ts @@ -0,0 +1,25 @@ +import { type ActionFunctionArgs, redirect } from "@remix-run/node"; +import { requireUser } from "~/features/auth/core/user.server"; +import { badRequestIfFalsy, unauthorizedIfFalsy } from "~/utils/remix"; +import { userVodsPage } from "~/utils/urls"; +import * as VodRepository from "../VodRepository.server"; +import { findVodById } from "../queries/findVodById.server"; +import { canEditVideo } from "../vods-utils"; + +export const action = async ({ request, params }: ActionFunctionArgs) => { + const user = await requireUser(request); + + const vod = badRequestIfFalsy(findVodById(Number(params.id))); + + unauthorizedIfFalsy( + canEditVideo({ + userId: user.id, + submitterUserId: vod.submitterUserId, + povUserId: typeof vod.pov === "string" ? undefined : vod.pov?.id, + }), + ); + + await VodRepository.deleteById(vod.id); + + return redirect(userVodsPage(user)); +}; diff --git a/app/features/vods/routes/vods.$id.tsx b/app/features/vods/routes/vods.$id.tsx index 2dc61deef..b05a9355e 100644 --- a/app/features/vods/routes/vods.$id.tsx +++ b/app/features/vods/routes/vods.$id.tsx @@ -8,9 +8,12 @@ import clsx from "clsx"; import * as React from "react"; import { useTranslation } from "react-i18next"; import { Button, LinkButton } from "~/components/Button"; +import { FormWithConfirm } from "~/components/FormWithConfirm"; import { Image, WeaponImage } from "~/components/Image"; import { Main } from "~/components/Main"; import { YouTubeEmbed } from "~/components/YouTubeEmbed"; +import { EditIcon } from "~/components/icons/Edit"; +import { TrashIcon } from "~/components/icons/Trash"; import { useUser } from "~/features/auth/core/user"; import { useIsMounted } from "~/hooks/useIsMounted"; import { useSearchParamState } from "~/hooks/useSearchParamState"; @@ -34,6 +37,9 @@ import { canEditVideo } from "../vods-utils"; import "../vods.css"; +import { action } from "../actions/vods.$id.server"; +export { action }; + export const handle: SendouRouteHandle = { breadcrumb: ({ match }) => { const data = match.data as SerializeFrom | undefined; @@ -79,7 +85,7 @@ export default function VodPage() { const isMounted = useIsMounted(); const [autoplay, setAutoplay] = React.useState(false); const data = useLoaderData(); - const { t } = useTranslation(["common"]); + const { t } = useTranslation(["common", "vods"]); const user = useUser(); return ( @@ -118,13 +124,30 @@ export default function VodPage() { povUserId: typeof data.vod.pov === "string" ? undefined : data.vod.pov?.id, }) ? ( - - {t("common:actions.edit")} - +
+ } + > + {t("common:actions.edit")} + + + + +
) : null} diff --git a/app/utils/remix.ts b/app/utils/remix.ts index 36b42b3a2..82c794e40 100644 --- a/app/utils/remix.ts +++ b/app/utils/remix.ts @@ -28,13 +28,18 @@ export function notFoundIfNullLike(value: T | null | undefined): T { export function badRequestIfFalsy(value: T | null | undefined): T { if (!value) { - noticeError(new Error("Value is falsy")); throw new Response(null, { status: 400 }); } return value; } +export function unauthorizedIfFalsy(value: T | null | undefined): T { + if (!value) throw new Response(null, { status: 401 }); + + return value; +} + export function parseSearchParams({ request, schema, diff --git a/locales/en/vods.json b/locales/en/vods.json index c455c3130..ce53c4f69 100644 --- a/locales/en/vods.json +++ b/locales/en/vods.json @@ -22,5 +22,6 @@ "forms.action.deleteMatch": "Delete match", "noVods": "No videos found matching this filter. Are we missing something? Message Sendou on Discord to get YouTube video submission permissions.", "addVod": "Add video", - "gainPerms": "Please post on the helpdesk of our Discord to gain permissions to upload videos." + "gainPerms": "Please post on the helpdesk of our Discord to gain permissions to upload videos.", + "deleteConfirm": "Delete video with the title '{{title}}'?" }