From 773d2b10261ecc8f248ddb72dad60872718015f6 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sat, 27 Apr 2024 22:28:55 +0200 Subject: [PATCH] Add missing error handling --- src/components/chapter/ChapterList.tsx | 34 +++++++++---- src/components/settings/DeviceSetting.tsx | 13 +++++ src/components/util/EmptyView.tsx | 14 ++++-- src/lib/metadata/metadataServerSettings.ts | 6 ++- src/lib/metadata/readerSettings.ts | 6 ++- src/lib/requests/RequestManager.ts | 50 +++++++++++++++---- src/screens/DownloadQueue.tsx | 19 ++++++- src/screens/Extensions.tsx | 16 +++++- src/screens/Migration.tsx | 4 +- src/screens/Reader.tsx | 48 +++++++++++++++--- src/screens/SourceConfigure.tsx | 16 +++++- src/screens/Sources.tsx | 21 +++++++- src/screens/settings/About.tsx | 13 ++++- src/screens/settings/Backup.tsx | 19 ++++++- src/screens/settings/BrowseSettings.tsx | 16 +++++- src/screens/settings/Categories.tsx | 14 +++++- .../settings/DefaultReaderSettings.tsx | 30 ++++++----- src/screens/settings/DownloadSettings.tsx | 16 +++++- src/screens/settings/ServerSettings.tsx | 16 +++++- src/screens/settings/WebUISettings.tsx | 16 +++++- 20 files changed, 322 insertions(+), 65 deletions(-) diff --git a/src/components/chapter/ChapterList.tsx b/src/components/chapter/ChapterList.tsx index 99fadb5d..021e44ae 100644 --- a/src/components/chapter/ChapterList.tsx +++ b/src/components/chapter/ChapterList.tsx @@ -7,7 +7,6 @@ */ import Box from '@mui/material/Box'; -import CircularProgress from '@mui/material/CircularProgress'; import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; import { styled } from '@mui/material/styles'; @@ -36,6 +35,8 @@ import { Chapters } from '@/lib/data/Chapters.ts'; import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts'; import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx'; import { ChaptersDownloadActionMenuItems } from '@/components/chapter/ChaptersDownloadActionMenuItems.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; const ChapterListHeader = styled(Stack)(({ theme }) => ({ margin: 8, @@ -77,7 +78,12 @@ export const ChapterList: React.FC = ({ manga, isRefreshing }) => { const queue = (downloaderData?.downloadStatus.queue as DownloadType[]) ?? []; const [options, dispatch] = useChapterOptions(manga.id); - const { data: chaptersData, loading: isLoading } = requestManager.useGetMangaChapters(manga.id); + const { + data: chaptersData, + loading: isLoading, + error, + refetch, + } = requestManager.useGetMangaChapters(manga.id, { notifyOnNetworkStatusChange: true }); const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]); const chapterIds = useMemo(() => chapters.map((chapter) => chapter.id), [chapters]); @@ -134,15 +140,21 @@ export const ChapterList: React.FC = ({ manga, isRefreshing }) => { if (isLoading || (noChaptersFound && isRefreshing)) { return ( -
- -
+ + + + ); + } + + if (error) { + return ( + + refetch().catch(defaultPromiseErrorHandler('ChapterList::refetch'))} + /> + ); } diff --git a/src/components/settings/DeviceSetting.tsx b/src/components/settings/DeviceSetting.tsx index a402cc1f..d30720d7 100644 --- a/src/components/settings/DeviceSetting.tsx +++ b/src/components/settings/DeviceSetting.tsx @@ -20,6 +20,8 @@ import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarC import { ActiveDevice, DEFAULT_DEVICE } from '@/util/device.ts'; import { Select } from '@/components/atoms/Select.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; export const DeviceSetting = () => { const { t } = useTranslation(); @@ -41,6 +43,7 @@ export const DeviceSetting = () => { metadata, settings: { devices }, loading, + request: { error, refetch }, } = useMetadataServerSettings(); const { activeDevice, setActiveDevice } = useContext(ActiveDevice); @@ -67,6 +70,16 @@ export const DeviceSetting = () => { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('DeviceSetting::refetch'))} + /> + ); + } + return ( void; } -export function EmptyView({ message, messageExtra }: IProps) { +export function EmptyView({ message, messageExtra, retry }: IProps) { + const { t } = useTranslation(); const theme = useTheme(); const isMobileWidth = useMediaQuery(theme.breakpoints.down('sm')); const errorFace = useMemo(() => getRandomErrorFace(), []); return ( - {errorFace} + {retry && } {message} {messageExtra} - + ); } diff --git a/src/lib/metadata/metadataServerSettings.ts b/src/lib/metadata/metadataServerSettings.ts index e06e4cee..9ac50a4e 100644 --- a/src/lib/metadata/metadataServerSettings.ts +++ b/src/lib/metadata/metadataServerSettings.ts @@ -76,12 +76,14 @@ export const useMetadataServerSettings = (): { metadata?: Metadata; settings: MetadataServerSettings; loading: boolean; + request: ReturnType; } => { - const { data, loading } = requestManager.useGetGlobalMeta(); + const request = requestManager.useGetGlobalMeta({ notifyOnNetworkStatusChange: true }); + const { data, loading } = request; const metadata = convertFromGqlMeta(data?.metas.nodes); const settings = getMetadataServerSettingsWithDefaultFallback(metadata); - return { metadata, settings, loading }; + return { metadata, settings, loading, request }; }; export const getMetadataServerSettings = async (): Promise => { diff --git a/src/lib/metadata/readerSettings.ts b/src/lib/metadata/readerSettings.ts index 6cb8338c..3e571b7c 100644 --- a/src/lib/metadata/readerSettings.ts +++ b/src/lib/metadata/readerSettings.ts @@ -54,12 +54,14 @@ export const useDefaultReaderSettings = (): { metadata?: Metadata; settings: IReaderSettings; loading: boolean; + request: ReturnType; } => { - const { data, loading } = requestManager.useGetGlobalMeta(); + const request = requestManager.useGetGlobalMeta({ notifyOnNetworkStatusChange: true }); + const { data, loading } = request; const metadata = convertFromGqlMeta(data?.metas.nodes); const settings = getReaderSettingsWithDefaultValueFallback(metadata); - return { metadata, settings, loading }; + return { metadata, settings, loading, request }; }; /** diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 2c1c9583..680b2095 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -25,7 +25,7 @@ import { useQuery, useSubscription, } from '@apollo/client'; -import { OperationVariables } from '@apollo/client/core'; +import { OperationVariables, Reference } from '@apollo/client/core'; import { useEffect, useMemo, useRef, useState } from 'react'; import { IRestClient, RestClient } from '@/lib/requests/client/RestClient.ts'; import { GraphQLClient } from '@/lib/requests/client/GraphQLClient.ts'; @@ -269,7 +269,12 @@ import { DOWNLOAD_STATUS_SUBSCRIPTION } from '@/lib/graphql/subscriptions/Downlo import { UPDATER_SUBSCRIPTION } from '@/lib/graphql/subscriptions/UpdaterSubscription.ts'; import { GET_SERVER_SETTINGS } from '@/lib/graphql/queries/SettingsQuery.ts'; import { UPDATE_SERVER_SETTINGS } from '@/lib/graphql/mutations/SettingsMutation.ts'; -import { BASE_MANGA_FIELDS, FULL_DOWNLOAD_STATUS, FULL_EXTENSION_FIELDS } from '@/lib/graphql/Fragments.ts'; +import { + BASE_MANGA_FIELDS, + FULL_DOWNLOAD_STATUS, + FULL_EXTENSION_FIELDS, + GLOBAL_METADATA, +} from '@/lib/graphql/Fragments.ts'; import { CLEAR_SERVER_CACHE } from '@/lib/graphql/mutations/ImageMutation.ts'; import { RESET_WEBUI_UPDATE_STATUS, UPDATE_WEBUI } from '@/lib/graphql/mutations/ServerInfoMutation.ts'; import { WEBUI_UPDATE_SUBSCRIPTION } from '@/lib/graphql/subscriptions/ServerInfoSubscription.ts'; @@ -1023,18 +1028,40 @@ export class RequestManager { value: any, options?: MutationOptions, ): AbortableApolloMutationResponse { - const result = this.doRequest( + return this.doRequest( GQLMethod.MUTATION, SET_GLOBAL_METADATA, { input: { meta: { key, value: `${value}` } } }, - options, + { + update(cache, { data }) { + cache.modify({ + fields: { + metas(existingMetas, { readField }) { + if (!existingMetas) { + return existingMetas; + } + + const exists = existingMetas.nodes.some( + // eslint-disable-next-line no-underscore-dangle + (meta: Reference) => readField('key', meta) === key, + ); + if (exists) { + return existingMetas; + } + + const newMetaRef = cache.writeFragment({ + data: data!.setGlobalMeta.meta, + fragment: GLOBAL_METADATA, + }); + + return [...existingMetas, newMetaRef]; + }, + }, + }); + }, + ...options, + }, ); - - result.response.then(() => { - this.graphQLClient.client.cache.evict({ fieldName: 'metas' }); - }); - - return result; } public useGetAbout( @@ -1116,7 +1143,7 @@ export class RequestManager { }, }, }, - [this.cache.getFetchTimestampFor(EXTENSION_LIST_CACHE_KEY, undefined)], + [this.cache.getFetchTimestampFor(EXTENSION_LIST_CACHE_KEY, undefined), result.loading], ); const wrappedMutate = async (mutateOptions: Parameters[0]) => { @@ -2063,6 +2090,7 @@ export class RequestManager { GQLMethod.USE_QUERY, GET_CATEGORY_MANGAS, { id }, + options as QueryHookOptions, ); return { diff --git a/src/screens/DownloadQueue.tsx b/src/screens/DownloadQueue.tsx index f661e54e..7aa657bd 100644 --- a/src/screens/DownloadQueue.tsx +++ b/src/screens/DownloadQueue.tsx @@ -103,7 +103,12 @@ export const DownloadQueue: React.FC = () => { const [reorderDownload, { reset: revertReorder }] = requestManager.useReorderChapterInDownloadQueue(); - const { data: downloadStatusData, loading: isLoading } = requestManager.useGetDownloadStatus(); + const { + data: downloadStatusData, + loading: isLoading, + error, + refetch, + } = requestManager.useGetDownloadStatus({ notifyOnNetworkStatusChange: true }); const downloaderData = downloadStatusData?.downloadStatus; const queue = (downloaderData?.queue as DownloadType[]) ?? []; @@ -203,7 +208,7 @@ export const DownloadQueue: React.FC = () => { // bug: The folder and the last image downloaded are not deleted requestManager.deleteDownloadedChapter(chapter.id).response, ]); - } catch (error) { + } catch (e) { makeToast(t('download.queue.error.label.failed_to_remove'), 'error'); } @@ -218,6 +223,16 @@ export const DownloadQueue: React.FC = () => { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('DownloadQueue::refetch'))} + /> + ); + } + if (isQueueEmpty) { return ; } diff --git a/src/screens/Extensions.tsx b/src/screens/Extensions.tsx index d123dbfa..4da401de 100644 --- a/src/screens/Extensions.tsx +++ b/src/screens/Extensions.tsx @@ -39,6 +39,8 @@ import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuoso.tsx'; import { StyledGroupHeader } from '@/components/virtuoso/StyledGroupHeader.tsx'; import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; const LANGUAGE = 0; const EXTENSIONS = 1; @@ -114,7 +116,7 @@ export function Extensions() { const [query] = useQueryParam('query', StringParam); const [refetchExtensions, setRefetchExtensions] = useState({}); - const [fetchExtensions, { data, loading: isLoading, called }] = requestManager.useExtensionListFetch(); + const [fetchExtensions, { data, loading: isLoading, error }] = requestManager.useExtensionListFetch(); const allExtensions = data?.fetchExtensions.extensions; const handleExtensionUpdate = useCallback(() => setRefetchExtensions({}), []); @@ -229,10 +231,20 @@ export function Extensions() { [], ); - if (!allExtensions && (isLoading || !called)) { + if (isLoading) { return ; } + if (error) { + return ( + fetchExtensions().catch(defaultPromiseErrorHandler('Extensions::refetch'))} + /> + ); + } + const showAddRepoInfo = !allExtensions?.length && !areReposDefined; if (showAddRepoInfo) { return ( diff --git a/src/screens/Migration.tsx b/src/screens/Migration.tsx index fedc8334..84daaa1d 100644 --- a/src/screens/Migration.tsx +++ b/src/screens/Migration.tsx @@ -43,7 +43,9 @@ const getMigratableSources = (mangas?: TMigratableSourcesResult): TMigratableSou export const Migration = () => { const { t } = useTranslation(); - const { data, loading, error } = requestManager.useGetMigratableSources(); + const { data, loading, error } = requestManager.useGetMigratableSources({ + notifyOnNetworkStatusChange: true, + }); const migratableSources = useMemo(() => getMigratableSources(data?.mangas.nodes), [data?.mangas.nodes]); if (loading) { diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index fbba8b8f..8140357b 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -32,6 +32,7 @@ import { UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { Chapters } from '@/lib/data/Chapters.ts'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; const getReaderComponent = (readerType: ReaderType) => { switch (readerType) { @@ -95,7 +96,12 @@ export function Reader() { Number(chapterIndex) === loadedChapter.current?.sourceOrder && loadedChapter.current?.pageCount !== -1; const manga = data?.manga ?? initialManga; - const { data: chapterData, loading: isChapterLoading } = requestManager.useGetMangaChapter(mangaId, chapterIndex); + const { + data: chapterData, + loading: isChapterLoading, + error: chapterError, + refetch: fetchChapter, + } = requestManager.useGetMangaChapter(mangaId, chapterIndex, { notifyOnNetworkStatusChange: true }); const arePagesUpdatedRef = useRef(false); const { @@ -124,20 +130,30 @@ export function Reader() { loadedChapter.current = getLoadedChapter(); const chapter = loadedChapter.current ?? initialChapter; - const [fetchPages] = requestManager.useGetChapterPagesFetch(chapter.id); + const [fetchPages, { loading: arePagesLoading, error: pagesError }] = requestManager.useGetChapterPagesFetch( + chapter.id, + ); - useEffect(() => { + const doFetchPages = () => { const shouldFetchPages = !isChapterLoading && !chapter.isDownloaded; if (shouldFetchPages) { - fetchPages().then(() => { - arePagesUpdatedRef.current = true; - }); + fetchPages() + .then(() => { + arePagesUpdatedRef.current = true; + }) + .catch(defaultPromiseErrorHandler('Reader::fetchPages')); } else { arePagesUpdatedRef.current = true; } + }; + + useEffect(() => { + doFetchPages(); }, [chapter.id]); - const isLoading = isChapterLoading || !arePagesUpdatedRef.current; + const isLoading = + isChapterLoading || arePagesLoading || (!arePagesUpdatedRef.current && !chapterError && !pagesError); + const error = chapterError ?? pagesError; const [wasLastPageReadSet, setWasLastPageReadSet] = useState(false); const [curPage, setCurPage] = useState(0); const isLastPage = curPage === chapter.pageCount - 1; @@ -416,6 +432,24 @@ export function Reader() { ); } + if (error) { + return ( + { + if (chapterError) { + fetchChapter().catch(defaultPromiseErrorHandler('Reader::refetchChapter')); + } + + if (pagesError) { + doFetchPages(); + } + }} + /> + ); + } + const pages = range(chapter.pageCount).map((index) => ({ index, src: requestManager.getChapterPageUrl(mangaId, chapterIndex, index), diff --git a/src/screens/SourceConfigure.tsx b/src/screens/SourceConfigure.tsx index aa937837..d3a8f0fe 100644 --- a/src/screens/SourceConfigure.tsx +++ b/src/screens/SourceConfigure.tsx @@ -19,6 +19,8 @@ import { MultiSelectListPreference } from '@/components/sourceConfiguration/Mult import { PreferenceProps } from '@/typings.ts'; import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; function getPrefComponent(type: string) { switch (type) { @@ -52,7 +54,9 @@ export function SourceConfigure() { }, [t]); const { sourceId } = useParams<{ sourceId: string }>(); - const { data, loading } = requestManager.useGetSource(sourceId); + const { data, loading, error, refetch } = requestManager.useGetSource(sourceId, { + notifyOnNetworkStatusChange: true, + }); const sourcePreferences = data?.source.preferences ?? []; const updateValue = @@ -65,6 +69,16 @@ export function SourceConfigure() { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('SourceConfigure::refetch'))} + /> + ); + } + return ( {sourcePreferences.map((it, index) => { diff --git a/src/screens/Sources.tsx b/src/screens/Sources.tsx index 292ec968..4f61eed9 100644 --- a/src/screens/Sources.tsx +++ b/src/screens/Sources.tsx @@ -22,6 +22,8 @@ import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder'; import { SourceCard } from '@/components/SourceCard'; import { LangSelect } from '@/components/navbar/action/LangSelect'; import { NavBarContext } from '@/components/context/NavbarContext.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; function sourceToLangList(sources: ISource[]) { const result: string[] = []; @@ -55,7 +57,12 @@ export function Sources() { const [shownLangs, setShownLangs] = useLocalStorage('shownSourceLangs', sourceDefualtLangs()); const [showNsfw] = useLocalStorage('showNsfw', true); - const { data, loading: isLoading } = requestManager.useGetSourceList(); + const { + data, + loading: isLoading, + error, + refetch, + } = requestManager.useGetSourceList({ notifyOnNetworkStatusChange: true }); const sources = data?.sources.nodes; const areSourcesFromDifferentRepos = useMemo(() => { @@ -109,8 +116,18 @@ export function Sources() { if (isLoading) return ; + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('Sources::refetch'))} + /> + ); + } + if (sources?.length === 0) { - return

{t('source.error.label.no_sources_found')}

; + return ; } return ( diff --git a/src/screens/settings/About.tsx b/src/screens/settings/About.tsx index 68fa6c14..bf67c653 100644 --- a/src/screens/settings/About.tsx +++ b/src/screens/settings/About.tsx @@ -28,6 +28,7 @@ import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarC import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder'; import { GetAboutQuery, UpdateState } from '@/lib/graphql/generated/graphql.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; type AboutServer = GetAboutQuery['aboutServer']; @@ -201,7 +202,7 @@ export function About() { useSetDefaultBackTo('settings'); - const { data, loading } = requestManager.useGetAbout(); + const { data, loading, error, refetch } = requestManager.useGetAbout({ notifyOnNetworkStatusChange: true }); const { aboutServer, aboutWebUI } = data ?? {}; const { @@ -239,6 +240,16 @@ export function About() { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('About::refetch'))} + /> + ); + } + return ( ; @@ -71,7 +73,12 @@ export function Backup() { useSetDefaultBackTo('settings'); - const { data: settingsData, loading } = requestManager.useGetServerSettings(); + const { + data: settingsData, + loading, + error, + refetch, + } = requestManager.useGetServerSettings({ notifyOnNetworkStatusChange: true }); const [mutateSettings] = requestManager.useUpdateServerSettings(); const backupSettings = settingsData ? extractBackupSettings(settingsData.settings) : undefined; @@ -228,6 +235,16 @@ export function Backup() { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('Backup::refetch'))} + /> + ); + } + return ( <> diff --git a/src/screens/settings/BrowseSettings.tsx b/src/screens/settings/BrowseSettings.tsx index 120f907a..d53e9808 100644 --- a/src/screens/settings/BrowseSettings.tsx +++ b/src/screens/settings/BrowseSettings.tsx @@ -24,6 +24,8 @@ import { useMetadataServerSettings, } from '@/lib/metadata/metadataServerSettings.ts'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; type ExtensionsSettings = Pick; @@ -46,7 +48,9 @@ export const BrowseSettings = () => { const [showNsfw, setShowNsfw] = useLocalStorage('showNsfw', true); - const { data, loading } = requestManager.useGetServerSettings(); + const { data, loading, error, refetch } = requestManager.useGetServerSettings({ + notifyOnNetworkStatusChange: true, + }); const serverSettings = data ? extractBrowseSettings(data.settings) : undefined; const [mutateSettings] = requestManager.useUpdateServerSettings(); @@ -66,6 +70,16 @@ export const BrowseSettings = () => { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('BrowseSettings::refetch'))} + /> + ); + } + return ( diff --git a/src/screens/settings/Categories.tsx b/src/screens/settings/Categories.tsx index 49eb08a3..e54365b8 100644 --- a/src/screens/settings/Categories.tsx +++ b/src/screens/settings/Categories.tsx @@ -35,6 +35,8 @@ import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab'; import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext'; import { TCategory } from '@/typings.ts'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; const getItemStyle = ( isDragging: boolean, @@ -63,7 +65,7 @@ export function Categories() { }; }, [t]); - const { data, loading } = requestManager.useGetCategories({ notifyOnNetworkStatusChange: true }); + const { data, loading, error, refetch } = requestManager.useGetCategories({ notifyOnNetworkStatusChange: true }); const categories = useMemo(() => { const res = [...(data?.categories.nodes ?? [])]; if (res.length > 0 && res[0].name === 'Default') { @@ -140,6 +142,16 @@ export function Categories() { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('Categories::refetch'))} + /> + ); + } + return ( <> diff --git a/src/screens/settings/DefaultReaderSettings.tsx b/src/screens/settings/DefaultReaderSettings.tsx index 46186d3f..d5234f56 100644 --- a/src/screens/settings/DefaultReaderSettings.tsx +++ b/src/screens/settings/DefaultReaderSettings.tsx @@ -7,8 +7,6 @@ */ import { useContext, useEffect } from 'react'; -import Box from '@mui/material/Box'; -import CircularProgress from '@mui/material/CircularProgress'; import { useTranslation } from 'react-i18next'; import { AllowedMetadataValueTypes, IReaderSettings } from '@/typings'; import { convertToGqlMeta, requestUpdateServerMetadata } from '@/lib/metadata/metadata.ts'; @@ -21,6 +19,8 @@ import { ReaderSettingsOptions } from '@/components/reader/ReaderSettingsOptions import { makeToast } from '@/components/util/Toast'; import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; export function DefaultReaderSettings() { const { t } = useTranslation(); @@ -35,7 +35,12 @@ export function DefaultReaderSettings() { }; }, [t]); - const { metadata, settings, loading } = useDefaultReaderSettings(); + const { + metadata, + settings, + loading, + request: { error, refetch }, + } = useDefaultReaderSettings(); useSetDefaultBackTo('settings'); @@ -48,17 +53,16 @@ export function DefaultReaderSettings() { }; if (loading) { + return ; + } + + if (error) { return ( - - - + refetch().catch(defaultPromiseErrorHandler('DefaultReaderSettings::refetch'))} + /> ); } diff --git a/src/screens/settings/DownloadSettings.tsx b/src/screens/settings/DownloadSettings.tsx index 121b8755..ab37fba0 100644 --- a/src/screens/settings/DownloadSettings.tsx +++ b/src/screens/settings/DownloadSettings.tsx @@ -27,6 +27,8 @@ import { DeleteChaptersWhileReadingSetting } from '@/components/settings/downloa import { CategoriesInclusionSetting } from '@/components/settings/CategoriesInclusionSetting.tsx'; import { NumberSetting } from '@/components/settings/NumberSetting.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; +import { EmptyView } from '@/components/util/EmptyView.tsx'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; type DownloadSettingsType = Pick< ServerSettings, @@ -63,7 +65,9 @@ export const DownloadSettings = () => { }; }, [t]); - const { data } = requestManager.useGetServerSettings(); + const { data, loading, error, refetch } = requestManager.useGetServerSettings({ + notifyOnNetworkStatusChange: true, + }); const downloadSettings = data ? extractDownloadSettings(data.settings) : undefined; const [mutateSettings] = requestManager.useUpdateServerSettings(); const { settings: metadataSettings } = useMetadataServerSettings(); @@ -85,6 +89,16 @@ export const DownloadSettings = () => { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('DownloadSettings::refetch'))} + /> + ); + } + return ( { }; }, [t]); - const { data, loading } = requestManager.useGetServerSettings(); + const { data, loading, error, refetch } = requestManager.useGetServerSettings({ + notifyOnNetworkStatusChange: true, + }); const serverSettings = data ? extractServerSettings(data.settings) : undefined; const [mutateSettings] = requestManager.useUpdateServerSettings(); @@ -107,6 +111,16 @@ export const ServerSettings = () => { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('ServerSettings::refetch'))} + /> + ); + } + return ( { }; }, [t]); - const { data, loading } = requestManager.useGetServerSettings(); + const { data, loading, error, refetch } = requestManager.useGetServerSettings({ + notifyOnNetworkStatusChange: true, + }); const webUISettings = data ? extractWebUISettings(data.settings) : undefined; const [mutateSettings] = requestManager.useUpdateServerSettings(); @@ -146,6 +150,16 @@ export const WebUISettings = () => { return ; } + if (error) { + return ( + refetch().catch(defaultPromiseErrorHandler('WebUISettings::refetch'))} + /> + ); + } + return (