Include actual error in snackbar

This commit is contained in:
schroda
2024-12-20 17:24:40 +01:00
parent 91a1f75de6
commit 3949363474
44 changed files with 292 additions and 91 deletions

View File

@@ -408,6 +408,7 @@
"disabled": "Disabled",
"discord": "Discord",
"display": "Display",
"error": "Error",
"filter": "Filter",
"finished": "Finished",
"footnote": "{{value}}<0>$t(global.label.asterisk)</0>",
@@ -415,6 +416,7 @@
"github": "GitHub",
"hidden": "Hidden",
"horizontal": "Horizontal",
"info": "Information",
"left": "Left",
"links": "Links",
"load_in_progress": "Still loading required data…",
@@ -435,10 +437,12 @@
"sort": "Sort",
"standard": "Standard",
"started": "Started",
"success": "Success",
"type": "Type",
"unknown": "Unknown",
"username": "Username",
"vertical": "Vertical"
"vertical": "Vertical",
"warning": "Warning"
},
"language": {
"label": {

View File

@@ -6,6 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { ApolloError } from '@apollo/client/errors';
export const jsonSaveParse = <T = any>(...args: Parameters<typeof JSON.parse>): T | null => {
try {
return JSON.parse(...args);
@@ -13,3 +15,19 @@ export const jsonSaveParse = <T = any>(...args: Parameters<typeof JSON.parse>):
return null;
}
};
export const getErrorMessage = (error: unknown): string => {
if (error instanceof ApolloError) {
return `${error.name}: ${error.message}`;
}
if (error instanceof Error) {
return `${error.name}: ${error.message}`;
}
if (error == null) {
return '';
}
return `${error}`;
};

View File

@@ -23,6 +23,7 @@ import { ABOUT_WEBUI, WEBUI_UPDATE_CHECK } from '@/lib/graphql/fragments/InfoFra
import { VersionUpdateInfoDialog } from '@/modules/app-updates/components/VersionUpdateInfoDialog.tsx';
import { useUpdateChecker } from '@/modules/app-updates/hooks/useUpdateChecker.tsx';
import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const disabledUpdateCheck = () => Promise.resolve();
@@ -136,7 +137,9 @@ export const WebUIUpdateChecker = () => {
onAction={() =>
requestManager
.updateWebUI()
.response.catch(() => makeToast(t('settings.about.webui.label.update_failure'), 'error'))
.response.catch((e) =>
makeToast(t('settings.about.webui.label.update_failure'), 'error', getErrorMessage(e)),
)
}
actionTitle={
isUpdateInProgress

View File

@@ -36,6 +36,7 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { ServerSettings } from '@/modules/settings/Settings.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type BackupSettingsType = Pick<ServerSettings, 'backupPath' | 'backupTime' | 'backupInterval' | 'backupTTL'>;
@@ -101,8 +102,8 @@ export function Backup() {
setting: Setting,
value: BackupSettingsType[Setting],
) => {
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};
@@ -152,7 +153,7 @@ export function Backup() {
return true;
} catch (e) {
makeToast(t('settings.backup.action.validate.error.label.failure'), 'error');
makeToast(t('settings.backup.action.validate.error.label.failure'), 'error', getErrorMessage(e));
resetBackupState();
}
@@ -167,7 +168,7 @@ export function Backup() {
backupRestoreId = response.data?.restoreBackup.id;
setTriggerReRender(Date.now());
} catch (e) {
makeToast(t('settings.backup.action.restore.error.label.failure'), 'error');
makeToast(t('settings.backup.action.restore.error.label.failure'), 'error', getErrorMessage(e));
} finally {
resetBackupState();
}

View File

@@ -28,6 +28,7 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { MetadataBrowseSettings } from '@/modules/browse/Browse.types.ts';
import { ServerSettings as GqlServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type ExtensionsSettings = Pick<GqlServerSettings, 'maxSourcesInParallel' | 'localSourcePath' | 'extensionRepos'>;
@@ -57,16 +58,16 @@ export const BrowseSettings = () => {
setting: Setting,
value: ExtensionsSettings[Setting],
) => {
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};
const {
settings: { hideLibraryEntries },
} = useMetadataServerSettings();
const updateMetadataServerSettings = createUpdateMetadataServerSettings<keyof MetadataBrowseSettings>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
const updateMetadataServerSettings = createUpdateMetadataServerSettings<keyof MetadataBrowseSettings>((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
if (loading) {

View File

@@ -28,6 +28,7 @@ import {
CategoryNameInfo,
CategoryUpdateInclusionInfo,
} from '@/modules/category/Category.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type CategoryType = CategoryIdInfo & CategoryNameInfo & CategoryUpdateInclusionInfo & CategoryDownloadInclusionInfo;
@@ -139,8 +140,8 @@ export const CategoriesInclusionSetting = ({
await Promise.all(categoriesToUpdate.map((category) => updateCategory(category)));
// TODO - update cache immediately
// mutate(categoriesEndpoint, [...dialogCategories], { revalidate: false });
} catch (error) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error');
} catch (e) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e));
// mutate(categoriesEndpoint, [...categories]);
}
};

View File

@@ -34,6 +34,7 @@ import {
import { GET_CATEGORIES_BASE } from '@/lib/graphql/queries/CategoryQuery.ts';
import { GET_MANGA_CATEGORIES } from '@/lib/graphql/queries/MangaQuery.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type BaseProps = {
open: boolean;
@@ -150,8 +151,8 @@ export function CategorySelect(props: CategorySelectProps) {
onClose(true, addToCategories, removeFromCategories);
if (doNotShowAddToLibraryDialogAgain) {
updateMetadataServerSettings('showAddToLibraryCategorySelectDialog', false).catch(() =>
makeToast(t('search.error.label.failed_to_save_settings'), 'error'),
updateMetadataServerSettings('showAddToLibraryCategorySelectDialog', false).catch((e) =>
makeToast(t('search.error.label.failed_to_save_settings'), 'error', getErrorMessage(e)),
);
}

View File

@@ -23,6 +23,7 @@ import { DirectionOffset, TranslationKey } from '@/Base.types.ts';
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { ReaderResumeMode } from '@/modules/reader/types/Reader.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
@@ -279,7 +280,11 @@ export class Chapters {
await fnToExecute();
makeToast(translate(actionToTranslationKey[action].success, { count: itemCount }), 'success');
} catch (e) {
makeToast(translate(actionToTranslationKey[action].error, { count: itemCount }), 'error');
makeToast(
translate(actionToTranslationKey[action].error, { count: itemCount }),
'error',
getErrorMessage(e),
);
throw e;
}
}

View File

@@ -25,6 +25,7 @@ import { dateTimeFormatter } from '@/util/DateHelper.ts';
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
import { CategoryIdInfo } from '@/modules/category/Category.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const calcProgress = (status: UpdaterSubscription['updateStatusChanged'] | undefined) => {
if (!status) {
@@ -93,7 +94,7 @@ export function UpdateChecker({
reFetchLastTimestamp().catch(defaultPromiseErrorHandler('UpdateChecker::reFetchLastTimestamp'));
} catch (e) {
lastRunningState = false;
makeToast(t('global.error.label.update_failed'), 'error');
makeToast(t('global.error.label.update_failed'), 'error', getErrorMessage(e));
}
};
@@ -101,7 +102,7 @@ export function UpdateChecker({
try {
await requestManager.resetGlobalUpdate();
} catch (e) {
makeToast(t('library.error.label.stop_global_update'), 'error');
makeToast(t('library.error.label.stop_global_update'), 'error', getErrorMessage(e));
}
};

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { CustomContentProps, SnackbarContent, VariantType } from 'notistack';
import { ForwardedRef, forwardRef, memo } from 'react';
import Alert from '@mui/material/Alert';
import AlertTitle from '@mui/material/AlertTitle';
import Button from '@mui/material/Button';
import { useTranslation } from 'react-i18next';
import { awaitConfirmation } from '@/modules/core/utils/AwaitableDialog.tsx';
import { TranslationKey } from '@/Base.types.ts';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
const MAX_DESCRIPTION_LENGTH = 255;
const SNACKBAR_VARIANT_TO_TRANSLATION_KEY: Record<VariantType, TranslationKey> = {
default: 'global.label.info',
info: 'global.label.info',
success: 'global.label.success',
warning: 'global.label.warning',
error: 'global.label.error',
};
export const SnackbarWithDescription = memo(
forwardRef(
(
{
id,
message,
description,
variant,
action,
}: CustomContentProps & {
// eslint-disable-next-line react/no-unused-prop-types
description?: string;
},
ref: ForwardedRef<HTMLDivElement>,
) => {
const { t } = useTranslation();
const severity = variant === 'default' ? undefined : variant;
const finalAction = typeof action === 'function' ? action(id) : action;
const isDescriptionTooLong = (description?.length ?? 0) > MAX_DESCRIPTION_LENGTH;
const actualDescription = isDescriptionTooLong
? description?.slice(0, MAX_DESCRIPTION_LENGTH)
: description;
return (
<SnackbarContent ref={ref}>
<Alert severity={severity} action={finalAction}>
<AlertTitle>{message}</AlertTitle>
{actualDescription}{' '}
{isDescriptionTooLong ? (
<Button
onClick={() => {
awaitConfirmation({
title:
typeof message === 'string'
? message
: t(SNACKBAR_VARIANT_TO_TRANSLATION_KEY[variant]),
message: description ?? '',
actions: {
cancel: { show: false },
confirm: { title: t('global.label.close') },
},
}).catch(
defaultPromiseErrorHandler(
`SnackbarWidthDescription: ${id} - ${message} - ${description}`,
),
);
}}
size="small"
>
{t('global.button.show_more')}
</Button>
) : (
''
)}
</Alert>
</SnackbarContent>
);
},
),
);

View File

@@ -26,6 +26,7 @@ import { useMetadataServerSettings } from '@/modules/settings/services/ServerSet
import { ReaderContextProvider } from '@/modules/reader/contexts/ReaderContextProvider.tsx';
import { DIRECTION_TO_CACHE } from '@/modules/theme/ThemeDirectionCache.ts';
import { AppHotkeysProvider } from '@/modules/hotkeys/contexts/AppHotkeysProvider.tsx';
import { SnackbarWithDescription } from '@/modules/core/components/snackbar/SnackbarWithDescription.tsx';
interface Props {
children: React.ReactNode;
@@ -84,7 +85,15 @@ export const AppContext: React.FC<Props> = ({ children }) => {
<LibraryOptionsContextProvider>
<NavBarContextProvider>
<ActiveDeviceContextProvider>
<SnackbarProvider>
<SnackbarProvider
Components={{
default: SnackbarWithDescription,
info: SnackbarWithDescription,
success: SnackbarWithDescription,
warning: SnackbarWithDescription,
error: SnackbarWithDescription,
}}
>
<ReaderContextProvider>
<AppHotkeysProvider>{children}</AppHotkeysProvider>
</ReaderContextProvider>

View File

@@ -8,12 +8,21 @@
import { enqueueSnackbar, OptionsObject, SnackbarKey } from 'notistack';
export function makeToast(message: string, severity?: OptionsObject['variant']): SnackbarKey;
export function makeToast(message: string, options?: OptionsObject): SnackbarKey;
export function makeToast(message: string, options: OptionsObject['variant'] | OptionsObject = 'default'): SnackbarKey {
if (typeof options === 'string') {
return enqueueSnackbar(message, { variant: options });
}
export function makeToast(message: string, severity?: OptionsObject['variant'], description?: string): SnackbarKey;
export function makeToast(message: string, options?: OptionsObject, description?: string): SnackbarKey;
export function makeToast(
message: string,
options: OptionsObject['variant'] | OptionsObject = 'default',
description?: string,
): SnackbarKey {
const variant = typeof options === 'string' ? options : undefined;
const snackbarOptions = typeof options === 'object' ? options : {};
return enqueueSnackbar(message, options);
return enqueueSnackbar(message, {
variant,
...snackbarOptions,
// @ts-ignore - TS2353, "notistack" is outdated and the provided way to define custom props is not working, however,
// everything in the options object gets passed to the custom snackbar component
description,
});
}

View File

@@ -26,6 +26,7 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { DEFAULT_DEVICE } from '@/modules/device/services/Device.ts';
import { MetadataServerSettingKeys, MetadataServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const DeviceSetting = () => {
const { t } = useTranslation();
@@ -63,8 +64,8 @@ export const DeviceSetting = () => {
setActiveDevice(DEFAULT_DEVICE);
}
updateMetadataServerSettings(setting, value).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
updateMetadataServerSettings(setting, value).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};

View File

@@ -17,6 +17,7 @@ import { updateMetadataServerSettings } from '@/modules/settings/services/Server
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { MetadataDownloadSettings } from '@/modules/downloads/Downloads.types.ts';
import { MetadataServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const MIN_LIMIT = 2;
const MAX_LIMIT = 10;
@@ -39,8 +40,8 @@ export const DownloadAheadSetting = ({
const updateSetting = (value: MetadataDownloadSettings['downloadAheadLimit']) => {
persistDownloadAheadLimit(value === 0 ? currentDownloadAheadLimit : value);
updateMetadataServerSettings('downloadAheadLimit', value).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
updateMetadataServerSettings('downloadAheadLimit', value).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};

View File

@@ -36,6 +36,7 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
import { ChapterDownloadStatus, ChapterIdInfo } from '@/modules/chapter/services/Chapters.ts';
import { DownloaderState, DownloadState } from '@/lib/graphql/generated/graphql.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const HeightPreservingItem = ({ children, ...props }: BoxProps) => (
// the height is necessary to prevent the item container from collapsing, which confuses Virtuoso measurements
@@ -141,7 +142,7 @@ export const DownloadQueue: React.FC = () => {
try {
await requestManager.clearDownloads().response;
} catch (e) {
makeToast(t('download.queue.error.label.failed_delete_all'), 'error');
makeToast(t('download.queue.error.label.failed_delete_all'), 'error', getErrorMessage(e));
}
};
@@ -216,7 +217,7 @@ export const DownloadQueue: React.FC = () => {
try {
await requestManager.addChapterToDownloadQueue(chapter.id).response;
} catch (e) {
makeToast(t('download.queue.error.label.failed_to_remove'), 'error');
makeToast(t('download.queue.error.label.failed_to_remove'), 'error', getErrorMessage(e));
}
};
@@ -237,7 +238,7 @@ export const DownloadQueue: React.FC = () => {
requestManager.deleteDownloadedChapter(chapter.id).response,
]);
} catch (e) {
makeToast(t('download.queue.error.label.failed_to_retry'), 'error');
makeToast(t('download.queue.error.label.failed_to_retry'), 'error', getErrorMessage(e));
}
if (!isRunning) {

View File

@@ -32,6 +32,7 @@ import { GetCategoriesSettingsQuery, GetCategoriesSettingsQueryVariables } from
import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/queries/CategoryQuery.ts';
import { MetadataDownloadSettings } from '@/modules/downloads/Downloads.types.ts';
import { ServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type DownloadSettingsType = Pick<
ServerSettings,
@@ -115,13 +116,13 @@ export const DownloadSettings = () => {
setting: Setting,
value: DownloadSettingsType[Setting],
) => {
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};
const updateMetadataSetting = createUpdateMetadataServerSettings<keyof MetadataDownloadSettings>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
const updateMetadataSetting = createUpdateMetadataServerSettings<keyof MetadataDownloadSettings>((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
return (

View File

@@ -32,6 +32,7 @@ import {
INSTALLED_STATE_TO_TRANSLATION_KEY_MAP,
} from '@/modules/extension/Extensions.constants.ts';
import { getInstalledState } from '@/modules/extension/Extensions.utils.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
interface IProps {
extension: TExtension;
@@ -84,7 +85,11 @@ export function ExtensionCard(props: IProps) {
handleUpdate();
} catch (e) {
setInstalledState(getInstalledState(isInstalled, isObsolete, hasUpdate));
makeToast(t(EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP[action], { count: 1 }), 'error');
makeToast(
t(EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP[action], { count: 1 }),
'error',
getErrorMessage(e),
);
}
};

View File

@@ -40,6 +40,7 @@ import {
import { ExtensionAction, ExtensionGroupState, ExtensionState } from '@/modules/extension/Extensions.types.ts';
import { EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP } from '@/modules/extension/Extensions.constants.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const LANGUAGE = 0;
const EXTENSIONS = 1;
@@ -123,7 +124,7 @@ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) {
handleExtensionUpdate();
makeToast(t('extension.label.installed_successfully'), 'success');
})
.catch(() => makeToast(t('extension.label.installation_failed'), 'error'));
.catch((e) => makeToast(t('extension.label.installation_failed'), 'error', getErrorMessage(e)));
} else {
makeToast(t('global.error.label.invalid_file_type'), 'error');
}
@@ -263,7 +264,7 @@ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) {
requestManager
.updateExtensions(extensionIds, { update: true })
.response.then(() => handleExtensionUpdate())
.catch(() =>
.catch((e) =>
makeToast(
t(
EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP[
@@ -271,6 +272,8 @@ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) {
],
{ count: groupedExtensions.length },
),
'error',
getErrorMessage(e),
),
)
.finally(() => setUpdatingExtensionIds([]));

View File

@@ -29,6 +29,7 @@ import { LibrarySortMode } from '@/modules/library/Library.types.ts';
import { CategoryMetadataInfo } from '@/modules/category/Category.types.ts';
import { statusToTranslationKey } from '@/modules/manga/Manga.constants.ts';
import { GridLayout } from '@/modules/core/Core.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const TITLES: { [key in 'filter' | 'sort' | 'display']: TranslationKey } = {
filter: 'global.label.filter',
@@ -61,15 +62,15 @@ export const LibraryOptionsPanel = ({
const loggedInTrackers = Trackers.getLoggedIn(trackerList.data?.trackers.nodes ?? []);
const categoryLibraryOptions = useGetCategoryMetadata(category);
const updateCategoryLibraryOptions = createUpdateCategoryMetadata(category, () =>
makeToast(t('global.error.label.failed_to_save_changes', 'error')),
const updateCategoryLibraryOptions = createUpdateCategoryMetadata(category, (e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
const {
settings: { showTabSize },
} = useMetadataServerSettings();
const setSettingValue = createUpdateMetadataServerSettings<'showTabSize'>(() =>
makeToast(t('search.error.label.failed_to_save_settings'), 'warning'),
const setSettingValue = createUpdateMetadataServerSettings<'showTabSize'>((e) =>
makeToast(t('search.error.label.failed_to_save_settings'), 'error', getErrorMessage(e)),
);
return (

View File

@@ -38,6 +38,7 @@ import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/queries/CategoryQuery.ts'
import { GET_MANGAS_BASE } from '@/lib/graphql/queries/MangaQuery.ts';
import { MetadataLibrarySettings } from '@/modules/library/Library.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const removeNonLibraryMangasFromCategories = async (): Promise<void> => {
try {
@@ -57,7 +58,7 @@ const removeNonLibraryMangasFromCategories = async (): Promise<void> => {
}
makeToast(translate('library.settings.advanced.database.cleanup.label.success'), 'success');
} catch (e) {
makeToast(translate('library.settings.advanced.database.cleanup.label.error'), 'error');
makeToast(translate('library.settings.advanced.database.cleanup.label.error'), 'error', getErrorMessage(e));
}
};
@@ -85,8 +86,8 @@ export function LibrarySettings() {
request: { error: metadataServerSettingsError, refetch: refetchMetadataServerSettings },
} = useMetadataServerSettings();
const setSettingValue = createUpdateMetadataServerSettings<keyof MetadataLibrarySettings>(() =>
makeToast(t('search.error.label.failed_to_save_settings'), 'warning'),
const setSettingValue = createUpdateMetadataServerSettings<keyof MetadataLibrarySettings>((e) =>
makeToast(t('search.error.label.failed_to_save_settings'), 'error', getErrorMessage(e)),
);
const loading = serverSettings.loading || areMetadataServerSettingsLoading || categories.loading;

View File

@@ -35,7 +35,7 @@ export const TrackMangaButton = ({ manga }: { manga: MangaTrackRecordInfo & Pick
const handleClick = (openPopup: () => void) => {
if (trackerList.error) {
makeToast(t('tracking.error.label.could_not_load_track_info'), 'error');
makeToast(t('tracking.error.label.could_not_load_track_info'), 'error', trackerList.error?.toString());
return;
}

View File

@@ -21,6 +21,7 @@ import { awaitConfirmation } from '@/modules/core/utils/AwaitableDialog.tsx';
import { GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables, MangaType } from '@/lib/graphql/generated/graphql.ts';
import { GET_CATEGORIES_BASE } from '@/lib/graphql/queries/CategoryQuery.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const useManageMangaLibraryState = (
manga: Pick<MangaType, 'id' | 'title'> & Partial<Pick<MangaType, 'inLibrary'>>,
@@ -44,8 +45,8 @@ export const useManageMangaLibraryState = (
})
.response.then(() => makeToast(t('library.info.label.added_to_library'), 'success'))
.then(() => setIsInLibrary(true))
.catch(() => {
makeToast(t('library.error.label.add_to_library'), 'error');
.catch((e) => {
makeToast(t('library.error.label.add_to_library'), 'error', getErrorMessage(e));
});
},
[manga.id],
@@ -86,7 +87,7 @@ export const useManageMangaLibraryState = (
showAddToLibraryCategorySelectDialog = (await getMetadataServerSettings())
.showAddToLibraryCategorySelectDialog;
} catch (e) {
makeToast(t('global.error.label.failed_to_load_data'), 'error');
makeToast(t('global.error.label.failed_to_load_data'), 'error', getErrorMessage(e));
return;
}
@@ -101,7 +102,7 @@ export const useManageMangaLibraryState = (
GetCategoriesBaseQueryVariables
>(GET_CATEGORIES_BASE).response;
} catch (e) {
makeToast(t('category.error.label.request_failure'), 'error');
makeToast(t('category.error.label.request_failure'), 'error', getErrorMessage(e));
return;
}
const userCreatedCategories = Categories.getUserCreated(categories.data.categories.nodes);

View File

@@ -34,6 +34,7 @@ import {
MigrateMode,
} from '@/modules/manga/Manga.types.ts';
import { actionToTranslationKey } from '@/modules/manga/Manga.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type MangaToMigrate = NonNullable<GetMangaToMigrateQuery['manga']>;
type MangaToMigrateTo = NonNullable<GetMangaToMigrateToFetchMutation['fetchManga']>['manga'];
@@ -481,7 +482,11 @@ export class Mangas {
await fnToExecute();
makeToast(translate(actionToTranslationKey[action].success, { count: itemCount }), 'success');
} catch (e) {
makeToast(translate(actionToTranslationKey[action].error, { count: itemCount }), 'error');
makeToast(
translate(actionToTranslationKey[action].error, { count: itemCount }),
'error',
getErrorMessage(e),
);
throw e;
}
}

View File

@@ -30,6 +30,7 @@ import {
} from '@/modules/settings/services/ServerSettingsMetadata.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const getMigratableSources = (
mangas: TMigratableSourcesResult | undefined,
@@ -80,8 +81,8 @@ export const Migration = ({ tabsMenuHeight }: { tabsMenuHeight: number }) => {
const {
settings: { migrateSortSettings },
} = useMetadataServerSettings();
const updateMetadataServerSettings = createUpdateMetadataServerSettings<'migrateSortSettings'>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
const updateMetadataServerSettings = createUpdateMetadataServerSettings<'migrateSortSettings'>((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
const { sortBy, sortOrder } = migrateSortSettings;

View File

@@ -46,6 +46,7 @@ import {
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { Queue } from '@/lib/Queue.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const DIRECTION_TO_INVERTED: Record<Direction, Direction> = {
ltr: 'rtl',
@@ -279,8 +280,12 @@ export class ReaderService {
}
if (commit) {
updateReaderSettings(manga, setting, value, isGlobal, profile).catch(() =>
makeToast(translate('reader.settings.error.label.failed_to_save_settings'), 'error'),
updateReaderSettings(manga, setting, value, isGlobal, profile).catch((e) =>
makeToast(
translate('reader.settings.error.label.failed_to_save_settings'),
'error',
getErrorMessage(e),
),
);
}
}
@@ -309,8 +314,8 @@ export class ReaderService {
const deleteSetting = isGlobalSetting
? () => requestManager.deleteGlobalMeta(key).response
: () => requestManager.deleteMangaMeta(manga.id, key).response;
deleteSetting().catch(() =>
makeToast(translate('reader.settings.error.label.failed_to_save_settings'), 'error'),
deleteSetting().catch((e) =>
makeToast(translate('reader.settings.error.label.failed_to_save_settings'), 'error', getErrorMessage(e)),
);
}

View File

@@ -21,6 +21,7 @@ import { GlobalUpdateSettingsInterval } from '@/modules/settings/components/glob
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { ServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type LibrarySettingsType = Pick<ServerSettings, 'updateMangas'>;
@@ -42,8 +43,8 @@ export const GlobalUpdateSettings = ({
) => {
try {
await mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
} catch (error) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error');
} catch (e) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e));
}
};

View File

@@ -22,6 +22,7 @@ import { CheckboxContainer } from '@/modules/core/components/inputs/CheckboxCont
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
import { TranslationKey } from '@/Base.types.ts';
import { ServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type GlobalUpdateSkipEntriesSettings = Pick<
ServerSettings,
@@ -90,8 +91,8 @@ export const GlobalUpdateSettingsEntries = ({ serverSettings }: { serverSettings
try {
await mutateSettings({ variables: { input: { settings: dialogSettings } } });
} catch (error) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error');
} catch (e) {
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e));
}
};

View File

@@ -17,6 +17,7 @@ import { NumberSetting } from '@/modules/core/components/settings/NumberSetting.
import { getPersistedServerSetting, usePersistedValue } from '@/modules/core/hooks/usePersistedValue.tsx';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { ServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const DEFAULT_VALUE = 23;
const MIN_VALUE = 1;
@@ -45,8 +46,8 @@ export const WebUIUpdateIntervalSetting = ({
persistUpdateCheckInterval(
webUIUpdateCheckInterval === 0 ? currentUpdateCheckInterval : webUIUpdateCheckInterval,
);
mutateSettings({ variables: { input: { settings: { webUIUpdateCheckInterval } } } }).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
mutateSettings({ variables: { input: { settings: { webUIUpdateCheckInterval } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
},
[currentUpdateCheckInterval],

View File

@@ -34,6 +34,7 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const Appearance = () => {
const { t, i18n } = useTranslation();
@@ -54,8 +55,8 @@ export const Appearance = () => {
settings,
request: { loading, error, refetch },
} = useMetadataServerSettings();
const updateMetadataSetting = createUpdateMetadataServerSettings<keyof MetadataThemeSettings>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
const updateMetadataSetting = createUpdateMetadataServerSettings<keyof MetadataThemeSettings>((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
const isDarkMode =

View File

@@ -31,6 +31,7 @@ import {
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { MetadataUpdateSettings } from '@/modules/app-updates/AppUpdateChecker.types.ts';
import { ServerSettings as GqlServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type ServerSettingsType = Pick<
GqlServerSettings,
@@ -112,7 +113,7 @@ export const ServerSettings = () => {
} = useMetadataServerSettings();
const updateMetadataServerSettings = createUpdateMetadataServerSettings<
keyof Pick<MetadataUpdateSettings, 'serverInformAvailableUpdate'>
>(() => makeToast(t('global.error.label.failed_to_save_changes'), 'error'));
>((e) => makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)));
const {
data,
@@ -136,8 +137,8 @@ export const ServerSettings = () => {
setting: Setting,
value: ServerSettingsType[Setting],
) => {
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};

View File

@@ -30,6 +30,7 @@ import { NavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.t
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export function Settings() {
const { t } = useTranslation();
@@ -52,7 +53,7 @@ export function Settings() {
await triggerClearServerCache();
makeToast(t('settings.clear_cache.label.success'), 'success');
} catch (e) {
makeToast(t('settings.clear_cache.label.failure'), 'error');
makeToast(t('settings.clear_cache.label.failure'), 'error', getErrorMessage(e));
}
};

View File

@@ -32,6 +32,7 @@ import {
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { MetadataUpdateSettings } from '@/modules/app-updates/AppUpdateChecker.types.ts';
import { ServerSettings } from '@/modules/settings/Settings.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type WebUISettingsType = Pick<
ServerSettings,
@@ -138,7 +139,7 @@ export const WebUISettings = () => {
} = useMetadataServerSettings();
const updateMetadataServerSettings = createUpdateMetadataServerSettings<
keyof Pick<MetadataUpdateSettings, 'webUIInformAvailableUpdate'>
>(() => makeToast(t('global.error.label.failed_to_save_changes'), 'error'));
>((e) => makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)));
const {
data,
@@ -158,8 +159,8 @@ export const WebUISettings = () => {
requestManager.graphQLClient.client.cache.evict({ fieldName: 'checkForWebUIUpdate' });
}
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};

View File

@@ -27,6 +27,7 @@ import { GetCategoriesSettingsQueryVariables, GetSourceSettingsQuery } from '@/l
import { GET_SOURCE_SETTINGS } from '@/lib/graphql/queries/SourceQuery.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { PreferenceProps } from '@/modules/source/Source.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
function getPrefComponent(type: string) {
switch (type) {
@@ -73,7 +74,9 @@ export function SourceConfigure() {
(type, value) => {
requestManager
.setSourcePreferences(sourceId, { position, [type]: value })
.response.catch(() => makeToast(t('global.error.label.failed_to_save_changes'), 'error'));
.response.catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};
if (loading) {

View File

@@ -52,6 +52,7 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { GridLayout } from '@/modules/core/Core.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const DEFAULT_SOURCE: Pick<SourceType, 'id'> = { id: '-1' };
@@ -290,8 +291,8 @@ export function SourceMangas() {
const filters = source?.filters ?? [];
const { savedSearches = {} } = useGetSourceMetadata(source ?? DEFAULT_SOURCE);
const updateSourceMetadata = createUpdateSourceMetadata<'savedSearches'>(source ?? { id: '-1' }, () =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
const updateSourceMetadata = createUpdateSourceMetadata<'savedSearches'>(source ?? { id: '-1' }, (e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
const selectSavedSearch = useCallback(

View File

@@ -19,7 +19,7 @@ import DialogActions from '@mui/material/DialogActions';
import TextField from '@mui/material/TextField';
import Link from '@mui/material/Link';
import { jsonrepair } from 'jsonrepair';
import { jsonSaveParse } from '@/lib/HelperFunctions.ts';
import { getErrorMessage, jsonSaveParse } from '@/lib/HelperFunctions.ts';
import { AppTheme, isThemeNameUnique } from '@/modules/theme/services/AppThemes.ts';
import {
createUpdateMetadataServerSettings,
@@ -203,10 +203,11 @@ export const ThemeCreationDialog = ({
);
bindDialogProps.onClose(e);
})
.catch(() =>
.catch((updateError) =>
makeToast(
t(dialogModeToTranslationKey[mode].failure, { theme: theme.getName() }),
'error',
getErrorMessage(updateError),
),
)
.finally(() => setIsCreating(false));

View File

@@ -21,6 +21,7 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
import { CreateThemeButton } from '@/modules/theme/components/CreateThemeButton.tsx';
import { ThemePreview } from '@/modules/theme/components/ThemePreview.tsx';
import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const ThemeList = () => {
const { t } = useTranslation();
@@ -80,10 +81,11 @@ export const ThemeList = () => {
'success',
),
)
.catch(() =>
.catch((e) =>
makeToast(
t('settings.appearance.theme.delete.failure', { theme: theme.getName() }),
'error',
getErrorMessage(e),
),
);
}}

View File

@@ -25,6 +25,7 @@ import { createTheme } from '@/modules/theme/services/ThemeCreator.ts';
import { ThemeCreationDialog } from '@/modules/theme/components/CreateThemeDialog.tsx';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines.tsx';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const ThemePreviewBadge = styled(Box)(() => ({
width: '15px',
@@ -93,7 +94,13 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
makeToast(t('settings.appearance.theme.select.fonts.loading'), 'info');
loadThemeFonts(theme.muiTheme)
.then(() => setAppTheme(theme.id))
.catch(() => makeToast(t('settings.appearance.theme.select.fonts.error'), 'error'));
.catch((e) =>
makeToast(
t('settings.appearance.theme.select.fonts.error'),
'error',
getErrorMessage(e),
),
);
}}
>
<Stack sx={{ height: '100%', m: 0 }}>

View File

@@ -23,6 +23,7 @@ import { GET_TRACKERS_BIND } from '@/lib/graphql/queries/TrackerQuery.ts';
import { GET_MANGA_TRACK_RECORDS } from '@/lib/graphql/queries/MangaQuery.ts';
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const getTrackerMode = (id: number, trackersInUse: number[], searchModeForTracker?: number): TrackerMode => {
if (id === searchModeForTracker) {
@@ -78,7 +79,7 @@ export const TrackManga = ({ manga }: { manga: MangaIdInfo & Pick<MangaType, 'ti
fetchedLatestTrackDataRef.current = true;
Promise.all(
mangaTrackRecords.map((trackRecord) => requestManager.fetchTrackBind(trackRecord.id).response),
).catch(() => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error'));
).catch((e) => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error', getErrorMessage(e)));
}, [mangaTrackRecords]);
const trackerComponents = useMemo(

View File

@@ -34,6 +34,7 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const TrackerSearch = ({
manga,
@@ -90,7 +91,7 @@ export const TrackerSearch = ({
makeToast(t('manga.action.track.add.label.success'), 'success');
closeSearchMode();
})
.catch(() => makeToast(t('manga.action.track.add.label.error'), 'error'));
.catch((e) => makeToast(t('manga.action.track.add.label.error'), 'error', getErrorMessage(e)));
};
return (

View File

@@ -25,6 +25,7 @@ import { PasswordTextField } from '@/modules/core/components/inputs/PasswordText
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { Trackers, TTrackerSearch } from '@/modules/tracker/services/Trackers.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const SettingsTrackerCard = ({ tracker }: { tracker: TTrackerSearch }) => {
const { t } = useTranslation();
@@ -42,7 +43,7 @@ export const SettingsTrackerCard = ({ tracker }: { tracker: TTrackerSearch }) =>
try {
await logoutFromTracker({ variables: { trackerId: tracker.id } });
} catch (e) {
makeToast(t('tracking.action.logout.label.failure', { name: tracker.name }), 'error');
makeToast(t('tracking.action.logout.label.failure', { name: tracker.name }), 'error', getErrorMessage(e));
}
};
@@ -62,7 +63,7 @@ export const SettingsTrackerCard = ({ tracker }: { tracker: TTrackerSearch }) =>
try {
await loginTrackerCredentials({ variables: { input: { trackerId: tracker.id, username, password } } });
} catch (e) {
makeToast(t('tracking.action.login.label.failure', { name: tracker.name }), 'error');
makeToast(t('tracking.action.login.label.failure', { name: tracker.name }), 'error', getErrorMessage(e));
}
};

View File

@@ -40,6 +40,7 @@ import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines
import { SelectSetting, SelectSettingValue } from '@/modules/core/components/settings/SelectSetting.tsx';
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
import { TrackRecordType } from '@/lib/graphql/generated/graphql.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const TrackerActiveLink = ({ children, url }: { children: React.ReactNode; url: string }) => (
<Link href={url} rel="noreferrer" target="_blank" underline="none" color="inherit">
@@ -69,7 +70,7 @@ const TrackerActiveRemoveBind = ({
requestManager
.unbindTracker(trackerRecordId, removeRemoteTracking)
.response.then(() => makeToast(t('manga.action.track.remove.label.success'), 'success'))
.catch(() => makeToast(t('manga.action.track.remove.label.error'), 'error'));
.catch((e) => makeToast(t('manga.action.track.remove.label.error'), 'error', getErrorMessage(e)));
};
return (
@@ -250,7 +251,9 @@ export const TrackerActiveCard = ({
const updateTrackerBind = (patch: Parameters<typeof requestManager.updateTrackerBind>[1]) => {
requestManager
.updateTrackerBind(trackRecord.id, patch)
.response.catch(() => makeToast(t('global.error.label.failed_to_save_changes'), 'error'));
.response.catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};
return (

View File

@@ -12,6 +12,7 @@ import { useNavigate } from 'react-router-dom';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const TrackerOAuthLogin = () => {
const { t } = useTranslation();
@@ -36,7 +37,7 @@ export const TrackerOAuthLogin = () => {
},
});
} catch (e) {
makeToast(t('tracking.action.login.label.failure', { name: trackerName }), 'error');
makeToast(t('tracking.action.login.label.failure', { name: trackerName }), 'error', getErrorMessage(e));
}
navigate(AppRoutes.tracker.path, { replace: true });

View File

@@ -28,6 +28,7 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
import { GET_TRACKERS_SETTINGS } from '@/lib/graphql/queries/TrackerQuery.ts';
import { GetTrackersSettingsQuery } from '@/lib/graphql/generated/graphql.ts';
import { MetadataTrackingSettings } from '@/modules/tracker/Tracker.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
export const TrackingSettings = () => {
const { t } = useTranslation();
@@ -42,8 +43,8 @@ export const TrackingSettings = () => {
loading: areMetadataServerSettingsLoading,
request: { error: metadataServerSettingsError, refetch: refetchServerMetadataSettings },
} = useMetadataServerSettings();
const updateTrackingSettings = createUpdateMetadataServerSettings<keyof MetadataTrackingSettings>(() =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error'),
const updateTrackingSettings = createUpdateMetadataServerSettings<keyof MetadataTrackingSettings>((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
const {

View File

@@ -38,6 +38,7 @@ import { ChapterIdInfo, ChapterMangaInfo } from '@/modules/chapter/services/Chap
import { makeToast } from '@/modules/core/utils/Toast.ts';
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
const groupByDate = (updates: Pick<ChapterType, 'fetchedAt'>[]): [date: string, items: number][] => {
if (!updates.length) {
@@ -115,14 +116,16 @@ export const Updates: React.FC = () => {
try {
await requestManager.addChapterToDownloadQueue(chapter.id).response;
} catch (e) {
makeToast(t('download.queue.error.label.failed_to_remove'), 'error');
makeToast(t('download.queue.error.label.failed_to_remove'), 'error', getErrorMessage(e));
}
};
const downloadChapter = (chapter: ChapterIdInfo) => {
requestManager
.addChapterToDownloadQueue(chapter.id)
.response.catch(() => makeToast(t('global.error.label.failed_to_save_changes'), 'error'));
.response.catch((e) =>
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
);
};
const loadMore = useCallback(() => {