mirror of
https://github.com/sussy-code/smov.git
synced 2025-01-01 16:37:39 +01:00
Create ErrorCard component, fix being unable to select text, use ErrorCard everywhere
This commit is contained in:
parent
5ae17a6c9a
commit
a9da1dada4
3 changed files with 76 additions and 78 deletions
68
src/pages/parts/errors/ErrorCard.tsx
Normal file
68
src/pages/parts/errors/ErrorCard.tsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
|
import { Button } from "@/components/Button";
|
||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { DisplayError } from "@/components/player/display/displayInterface";
|
||||||
|
|
||||||
|
export function ErrorCard(props: { error: DisplayError | string }) {
|
||||||
|
const [showErrorCard, setShowErrorCard] = useState(true);
|
||||||
|
const [hasCopied, setHasCopied] = useState(false);
|
||||||
|
const hasCopiedUnsetDebounce = useRef<ReturnType<typeof setTimeout> | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const errorMessage =
|
||||||
|
typeof props.error === "string" ? props.error : 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showErrorCard) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
// I didn't put a <Transition> here because it'd fade out, then jump height weirdly
|
||||||
|
<div className="w-full bg-errors-card p-6 rounded-lg">
|
||||||
|
<div className="flex justify-between items-center pb-2 border-b border-errors-border">
|
||||||
|
<span className="text-white font-medium">Error details</span>
|
||||||
|
<div className="flex justify-center items-center gap-3">
|
||||||
|
<Button
|
||||||
|
theme="secondary"
|
||||||
|
padding="p-2 md:px-4"
|
||||||
|
onClick={() => copyError()}
|
||||||
|
>
|
||||||
|
{hasCopied ? (
|
||||||
|
<>
|
||||||
|
<Icon icon={Icons.CHECKMARK} className="text-xs mr-3" />
|
||||||
|
Copied
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Icon icon={Icons.COPY} className="text-2xl mr-3" />
|
||||||
|
Copy
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
theme="secondary"
|
||||||
|
padding="p-2 md:px-2"
|
||||||
|
onClick={() => setShowErrorCard(false)}
|
||||||
|
>
|
||||||
|
<Icon icon={Icons.X} className="text-2xl" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 h-60 overflow-y-auto text-left whitespace-pre pointer-events-auto select-text">
|
||||||
|
{errorMessage}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,33 +1,15 @@
|
||||||
import { useRef, useState } from "react";
|
|
||||||
|
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icons } from "@/components/Icon";
|
||||||
import { IconPill } from "@/components/layout/IconPill";
|
import { IconPill } from "@/components/layout/IconPill";
|
||||||
import { Paragraph } from "@/components/text/Paragraph";
|
import { Paragraph } from "@/components/text/Paragraph";
|
||||||
import { Title } from "@/components/text/Title";
|
import { Title } from "@/components/text/Title";
|
||||||
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout";
|
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout";
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
|
||||||
|
import { ErrorCard } from "../errors/ErrorCard";
|
||||||
|
|
||||||
export function PlaybackErrorPart() {
|
export function PlaybackErrorPart() {
|
||||||
const playbackError = usePlayerStore((s) => s.interface.error);
|
const playbackError = usePlayerStore((s) => s.interface.error);
|
||||||
const [showErrorCard, setShowErrorCard] = useState(true);
|
|
||||||
const [hasCopied, setHasCopied] = useState(false);
|
|
||||||
const hasCopiedUnsetDebounce = useRef<ReturnType<typeof setTimeout> | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
function copyError() {
|
|
||||||
if (!playbackError || !navigator.clipboard) return;
|
|
||||||
navigator.clipboard.writeText(playbackError.message);
|
|
||||||
|
|
||||||
setHasCopied(true);
|
|
||||||
|
|
||||||
// Debounce unsetting the "has copied" label
|
|
||||||
if (hasCopiedUnsetDebounce.current)
|
|
||||||
clearTimeout(hasCopiedUnsetDebounce.current);
|
|
||||||
hasCopiedUnsetDebounce.current = setTimeout(() => setHasCopied(false), 2e3);
|
|
||||||
console.log(hasCopiedUnsetDebounce);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorLayout>
|
<ErrorLayout>
|
||||||
|
@ -51,43 +33,7 @@ export function PlaybackErrorPart() {
|
||||||
</ErrorContainer>
|
</ErrorContainer>
|
||||||
<ErrorContainer maxWidth="max-w-[45rem]">
|
<ErrorContainer maxWidth="max-w-[45rem]">
|
||||||
{/* Error */}
|
{/* Error */}
|
||||||
{playbackError && showErrorCard ? (
|
{playbackError ? <ErrorCard error={playbackError} /> : null}
|
||||||
// I didn't put a <Transition> here because it'd fade out, then jump height weirdly
|
|
||||||
<div className="w-full bg-errors-card p-6 rounded-lg">
|
|
||||||
<div className="flex justify-between items-center pb-2 border-b border-errors-border">
|
|
||||||
<span className="text-white font-medium">Error details</span>
|
|
||||||
<div className="flex justify-center items-center gap-3">
|
|
||||||
<Button
|
|
||||||
theme="secondary"
|
|
||||||
padding="p-2 md:px-4"
|
|
||||||
onClick={() => copyError()}
|
|
||||||
>
|
|
||||||
{hasCopied ? (
|
|
||||||
<>
|
|
||||||
<Icon icon={Icons.CHECKMARK} className="text-xs mr-3" />
|
|
||||||
Copied
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Icon icon={Icons.COPY} className="text-2xl mr-3" />
|
|
||||||
Copy
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
theme="secondary"
|
|
||||||
padding="p-2 md:px-2"
|
|
||||||
onClick={() => setShowErrorCard(false)}
|
|
||||||
>
|
|
||||||
<Icon icon={Icons.X} className="text-2xl" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 h-60 overflow-y-auto text-left whitespace-pre pointer-events-auto">
|
|
||||||
{playbackError.message}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</ErrorContainer>
|
</ErrorContainer>
|
||||||
</ErrorLayout>
|
</ErrorLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icons } from "@/components/Icon";
|
||||||
import { IconPill } from "@/components/layout/IconPill";
|
import { IconPill } from "@/components/layout/IconPill";
|
||||||
import { Paragraph } from "@/components/text/Paragraph";
|
import { Paragraph } from "@/components/text/Paragraph";
|
||||||
import { Title } from "@/components/text/Title";
|
import { Title } from "@/components/text/Title";
|
||||||
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape";
|
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape";
|
||||||
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout";
|
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout";
|
||||||
|
|
||||||
|
import { ErrorCard } from "../errors/ErrorCard";
|
||||||
|
|
||||||
export interface ScrapeErrorPartProps {
|
export interface ScrapeErrorPartProps {
|
||||||
data: {
|
data: {
|
||||||
sources: Record<string, ScrapingSegment>;
|
sources: Record<string, ScrapingSegment>;
|
||||||
|
@ -53,25 +55,7 @@ export function ScrapeErrorPart(props: ScrapeErrorPartProps) {
|
||||||
</ErrorContainer>
|
</ErrorContainer>
|
||||||
<ErrorContainer maxWidth="max-w-[45rem]">
|
<ErrorContainer maxWidth="max-w-[45rem]">
|
||||||
{/* Error */}
|
{/* Error */}
|
||||||
{error ? (
|
{error ? <ErrorCard error={error} /> : null}
|
||||||
<div className="w-full bg-errors-card p-6 rounded-lg">
|
|
||||||
<div className="flex justify-between items-center pb-2 border-b border-errors-border">
|
|
||||||
<span className="text-white font-medium">Error details</span>
|
|
||||||
<div className="flex justify-center items-center gap-3">
|
|
||||||
<Button theme="secondary" padding="p-2 md:px-4">
|
|
||||||
<Icon icon={Icons.COPY} className="text-2xl mr-3" />
|
|
||||||
Copy
|
|
||||||
</Button>
|
|
||||||
<Button theme="secondary" padding="p-2 md:px-2">
|
|
||||||
<Icon icon={Icons.X} className="text-2xl" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 h-60 overflow-y-auto text-left whitespace-pre pointer-events-auto">
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</ErrorContainer>
|
</ErrorContainer>
|
||||||
</ErrorLayout>
|
</ErrorLayout>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue