mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-15 11:14:58 -05:00
89 lines
3.1 KiB
TypeScript
89 lines
3.1 KiB
TypeScript
import { Button, Flex, Heading, Image, Link, Text } from '@invoke-ai/ui-library';
|
|
import { createSelector } from '@reduxjs/toolkit';
|
|
import { useAppSelector } from 'app/store/storeHooks';
|
|
import { useClipboard } from 'common/hooks/useClipboard';
|
|
import { selectConfigSlice } from 'features/system/store/configSlice';
|
|
import { toast } from 'features/toast/toast';
|
|
import newGithubIssueUrl from 'new-github-issue-url';
|
|
import InvokeLogoYellow from 'public/assets/images/invoke-symbol-ylw-lrg.svg';
|
|
import { memo, useCallback, useMemo } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { PiArrowCounterClockwiseBold, PiArrowSquareOutBold, PiCopyBold } from 'react-icons/pi';
|
|
import { serializeError } from 'serialize-error';
|
|
|
|
type Props = {
|
|
error: Error;
|
|
resetErrorBoundary: () => void;
|
|
};
|
|
|
|
const selectIsLocal = createSelector(selectConfigSlice, (config) => config.isLocal);
|
|
|
|
const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
|
|
const { t } = useTranslation();
|
|
const isLocal = useAppSelector(selectIsLocal);
|
|
const clipboard = useClipboard();
|
|
|
|
const handleCopy = useCallback(() => {
|
|
const text = JSON.stringify(serializeError(error), null, 2);
|
|
clipboard.writeText(`\`\`\`\n${text}\n\`\`\``, () => {
|
|
toast({
|
|
id: 'ERROR_COPIED',
|
|
title: t('toast.errorCopied'),
|
|
});
|
|
});
|
|
}, [clipboard, error, t]);
|
|
|
|
const url = useMemo(() => {
|
|
if (isLocal) {
|
|
return newGithubIssueUrl({
|
|
user: 'invoke-ai',
|
|
repo: 'InvokeAI',
|
|
template: 'BUG_REPORT.yml',
|
|
title: `[bug]: ${error.name}: ${error.message}`,
|
|
});
|
|
} else {
|
|
return 'https://support.invoke.ai/support/tickets/new';
|
|
}
|
|
}, [error.message, error.name, isLocal]);
|
|
|
|
return (
|
|
<Flex layerStyle="body" w="100dvw" h="100dvh" alignItems="center" justifyContent="center" p={4}>
|
|
<Flex layerStyle="first" flexDir="column" borderRadius="base" justifyContent="center" gap={8} p={16}>
|
|
<Flex alignItems="center" gap="2">
|
|
<Image src={InvokeLogoYellow} alt="invoke-logo" w="24px" h="24px" minW="24px" minH="24px" userSelect="none" />
|
|
<Heading fontSize="2xl">{t('common.somethingWentWrong')}</Heading>
|
|
</Flex>
|
|
|
|
<Flex
|
|
layerStyle="second"
|
|
px={8}
|
|
py={4}
|
|
gap={4}
|
|
borderRadius="base"
|
|
justifyContent="space-between"
|
|
alignItems="center"
|
|
>
|
|
<Text fontWeight="semibold" color="error.400">
|
|
{error.name}: {error.message}
|
|
</Text>
|
|
</Flex>
|
|
<Flex gap={4}>
|
|
<Button leftIcon={<PiArrowCounterClockwiseBold />} onClick={resetErrorBoundary}>
|
|
{t('accessibility.resetUI')}
|
|
</Button>
|
|
<Button leftIcon={<PiCopyBold />} onClick={handleCopy}>
|
|
{t('common.copyError')}
|
|
</Button>
|
|
<Link href={url} isExternal>
|
|
<Button leftIcon={<PiArrowSquareOutBold />}>
|
|
{isLocal ? t('accessibility.createIssue') : t('accessibility.submitSupportTicket')}
|
|
</Button>
|
|
</Link>
|
|
</Flex>
|
|
</Flex>
|
|
</Flex>
|
|
);
|
|
};
|
|
|
|
export default memo(AppErrorBoundaryFallback);
|