Files
Genarrative/src/components/platform-entry/PlatformErrorDialog.tsx
2026-05-26 14:27:18 +08:00

121 lines
3.5 KiB
TypeScript

import { Check, Copy } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { copyTextToClipboard } from '../../services/clipboard';
import { UnifiedModal } from '../common/UnifiedModal';
export type PlatformErrorDialogPayload = {
source: string;
message: string;
};
type PlatformErrorDialogProps = {
error: PlatformErrorDialogPayload | null;
onClose: () => void;
overlayClassName?: string;
panelClassName?: string;
};
function buildPlatformErrorReport(error: PlatformErrorDialogPayload) {
return [`来源:${error.source}`, `错误:${error.message}`].join('\n');
}
export function PlatformErrorDialog({
error,
onClose,
overlayClassName = 'platform-theme platform-theme--light !items-center',
panelClassName = 'platform-remap-surface rounded-[1.5rem]',
}: PlatformErrorDialogProps) {
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
'idle',
);
const resetTimerRef = useRef<number | null>(null);
const reportText = useMemo(
() => (error ? buildPlatformErrorReport(error) : ''),
[error],
);
useEffect(
() => () => {
if (resetTimerRef.current !== null) {
window.clearTimeout(resetTimerRef.current);
}
},
[],
);
useEffect(() => {
setCopyState('idle');
}, [error?.source, error?.message]);
const copyError = () => {
if (!reportText) {
return;
}
void copyTextToClipboard(reportText).then((copied) => {
setCopyState(copied ? 'copied' : 'failed');
if (resetTimerRef.current !== null) {
window.clearTimeout(resetTimerRef.current);
}
resetTimerRef.current = window.setTimeout(() => {
resetTimerRef.current = null;
setCopyState('idle');
}, 1400);
});
};
return (
<UnifiedModal
open={Boolean(error)}
title="发生错误"
onClose={onClose}
size="sm"
overlayClassName={overlayClassName}
panelClassName={panelClassName}
bodyClassName="space-y-3 px-4 py-4 sm:px-5 sm:py-5"
footerClassName="justify-end px-4 py-4 sm:px-5"
footer={
<button
type="button"
onClick={copyError}
disabled={!reportText}
className="platform-button platform-button--primary w-full justify-center gap-2 sm:w-auto"
>
{copyState === 'copied' ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
{copyState === 'copied'
? '已复制'
: copyState === 'failed'
? '复制失败'
: '复制报错'}
</button>
}
>
{error ? (
<>
<div className="rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2">
<div className="text-xs font-bold text-[var(--platform-text-soft)]">
</div>
<div className="mt-1 break-words text-sm font-semibold leading-5 text-[var(--platform-text-strong)]">
{error.source}
</div>
</div>
<div className="rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2">
<div className="text-xs font-bold text-[var(--platform-text-soft)]">
</div>
<div className="mt-1 whitespace-pre-wrap break-words text-sm leading-6 text-[var(--platform-text-strong)]">
{error.message}
</div>
</div>
</>
) : null}
</UnifiedModal>
);
}