Handle graphql exception errors
Extract stack trace from error message and show it as an information that is collapsed by default
This commit is contained in:
@@ -8,12 +8,13 @@
|
|||||||
|
|
||||||
// adopted from: https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/widget/EmptyView.kt
|
// adopted from: https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/widget/EmptyView.kt
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { SxProps, Theme } from '@mui/material/styles';
|
import { SxProps, Theme } from '@mui/material/styles';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Collapse from '@mui/material/Collapse';
|
||||||
|
|
||||||
const ERROR_FACES = ['(・o・;)', 'Σ(ಠ_ಠ)', 'ಥ_ಥ', '(˘・_・˘)', '(; ̄Д ̄)', '(・Д・。'];
|
const ERROR_FACES = ['(・o・;)', 'Σ(ಠ_ಠ)', 'ಥ_ಥ', '(˘・_・˘)', '(; ̄Д ̄)', '(・Д・。'];
|
||||||
|
|
||||||
@@ -30,6 +31,82 @@ export interface EmptyViewProps {
|
|||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GRAPHQL_EXCEPTION_MESSAGE_REGEX = /(.*Exception while fetching data \(.*\) : .*)\r\n\r\n(.*)/s;
|
||||||
|
|
||||||
|
const extractGraphqlExceptionInfo = (
|
||||||
|
error: EmptyViewProps['messageExtra'],
|
||||||
|
): {
|
||||||
|
isGraphqlException: boolean;
|
||||||
|
graphqlError?: string;
|
||||||
|
graphqlStackTrace?: string;
|
||||||
|
} => {
|
||||||
|
if (typeof error !== 'string') {
|
||||||
|
return { isGraphqlException: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const regexMatch = error.match(GRAPHQL_EXCEPTION_MESSAGE_REGEX);
|
||||||
|
|
||||||
|
const isGraphqlException = !!regexMatch;
|
||||||
|
if (!isGraphqlException) {
|
||||||
|
return { isGraphqlException: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, message, stackTrace] = regexMatch;
|
||||||
|
return {
|
||||||
|
isGraphqlException: true,
|
||||||
|
graphqlError: message,
|
||||||
|
graphqlStackTrace: stackTrace,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ExtraMessage = ({ messageExtra }: Pick<EmptyViewProps, 'messageExtra'>) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [showFullError, setShowFullError] = useState(false);
|
||||||
|
|
||||||
|
const { isGraphqlException, graphqlError, graphqlStackTrace } = extractGraphqlExceptionInfo(messageExtra);
|
||||||
|
|
||||||
|
if (!isGraphqlException) {
|
||||||
|
return (
|
||||||
|
<Typography variant="body1" sx={{ wordBreak: 'break-word', whiteSpace: 'pre-line', pointerEvents: 'all' }}>
|
||||||
|
{messageExtra}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack
|
||||||
|
sx={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
sx={{ wordBreak: 'break-word', whiteSpace: 'pre-line', pointerEvents: 'all' }}
|
||||||
|
>
|
||||||
|
{graphqlError}…
|
||||||
|
</Typography>
|
||||||
|
<Button variant="text" onClick={() => setShowFullError(!showFullError)} sx={{ pointerEvents: 'all' }}>
|
||||||
|
{t(showFullError ? 'global.button.show_less' : 'global.button.show_more')}
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
<Collapse in={showFullError}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
sx={{ wordBreak: 'break-word', whiteSpace: 'pre-line', pointerEvents: 'all' }}
|
||||||
|
>
|
||||||
|
{graphqlStackTrace}
|
||||||
|
</Typography>
|
||||||
|
</Collapse>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export function EmptyView({ message, messageExtra, retry, noFaces, sx }: EmptyViewProps) {
|
export function EmptyView({ message, messageExtra, retry, noFaces, sx }: EmptyViewProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -57,9 +134,7 @@ export function EmptyView({ message, messageExtra, retry, noFaces, sx }: EmptyVi
|
|||||||
<Typography variant="h5" sx={{ wordBreak: 'break-word', whiteSpace: 'pre-line', pointerEvents: 'all' }}>
|
<Typography variant="h5" sx={{ wordBreak: 'break-word', whiteSpace: 'pre-line', pointerEvents: 'all' }}>
|
||||||
{message}
|
{message}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" sx={{ wordBreak: 'break-word', whiteSpace: 'pre-line', pointerEvents: 'all' }}>
|
<ExtraMessage messageExtra={messageExtra} />
|
||||||
{messageExtra}
|
|
||||||
</Typography>
|
|
||||||
{retry && (
|
{retry && (
|
||||||
<Button onClick={retry} sx={{ pointerEvents: 'all' }}>
|
<Button onClick={retry} sx={{ pointerEvents: 'all' }}>
|
||||||
{t('global.button.retry')}
|
{t('global.button.retry')}
|
||||||
|
|||||||
Reference in New Issue
Block a user