A small web app for watching movies and shows easily
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

107 lines
3.4 KiB

import { useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/buttons/Button";
import { Icon, Icons } from "@/components/Icon";
import { Modal } from "@/components/overlays/Modal";
import { DisplayError } from "@/components/player/display/displayInterface";
export function ErrorCard(props: {
error: DisplayError | string;
onClose: () => void;
}) {
const [hasCopied, setHasCopied] = useState(false);
const hasCopiedUnsetDebounce = useRef<ReturnType<typeof setTimeout> | null>(
null
);
const { t } = useTranslation();
let errorMessage: string | null = null;
if (typeof props.error === "string") errorMessage = props.error;
else if (props.error.key)
errorMessage = `${props.error.type}: ${t(props.error.key)}`;
else if (props.error.message)
errorMessage = `${props.error.type}: ${t(props.error.message)}`;
function copyError() {
if (!props.error || !navigator.clipboard) return;
navigator.clipboard.writeText(`\`\`\`${errorMessage}\`\`\``);
setHasCopied(true);
// Debounce unsetting the "has copied" label
if (hasCopiedUnsetDebounce.current)
clearTimeout(hasCopiedUnsetDebounce.current);
hasCopiedUnsetDebounce.current = setTimeout(() => setHasCopied(false), 2e3);
}
return (
// I didn't put a <Transition> here because it'd fade out, then jump height weirdly
<div className="bg-errors-card w-full rounded-lg p-6">
<div className="border-errors-border flex items-center justify-between border-b pb-2">
<span className="font-medium text-white">{t("errors.details")}</span>
<div className="flex items-center justify-center gap-3">
<Button
theme="secondary"
padding="p-2 md:px-4"
onClick={() => copyError()}
>
{hasCopied ? (
<>
<Icon icon={Icons.CHECKMARK} className="mr-3 text-xs" />
{t("actions.copied")}
</>
) : (
<>
<Icon icon={Icons.COPY} className="mr-3 text-2xl" />
{t("actions.copy")}
</>
)}
</Button>
<Button
theme="secondary"
padding="p-2 md:px-2"
onClick={props.onClose}
>
<Icon icon={Icons.X} className="text-2xl" />
</Button>
</div>
</div>
<div className="pointer-events-auto mt-4 h-60 select-text overflow-y-auto whitespace-pre text-left">
{errorMessage}
</div>
</div>
);
}
// use plain modal version if there is no access to history api (like in error boundary)
export function ErrorCardInPlainModal(props: {
error?: DisplayError | string;
onClose: () => void;
show?: boolean;
}) {
if (!props.show || !props.error) return null;
return (
<div className="fixed inset-0 flex h-full w-full items-center justify-center bg-black bg-opacity-30 p-12">
<div className="w-full max-w-2xl">
<ErrorCard error={props.error} onClose={props.onClose} />
</div>
</div>
);
}
export function ErrorCardInModal(props: {
error?: DisplayError | string;
id: string;
onClose: () => void;
}) {
if (!props.error) return null;
return (
<Modal id={props.id}>
<div className="pointer-events-auto w-11/12 max-w-2xl">
<ErrorCard error={props.error} onClose={props.onClose} />
</div>
</Modal>
);
}