2021-03-26 04:17:02 +04:30
|
|
|
/*
|
|
|
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
2021-01-26 23:32:12 +03:30
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
2023-05-18 13:17:41 +02:00
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
2021-01-26 23:32:12 +03:30
|
|
|
|
2021-09-09 17:51:22 +04:30
|
|
|
import CssBaseline from '@mui/material/CssBaseline';
|
2026-04-27 15:38:49 +02:00
|
|
|
import type { PropsWithChildren } from 'react';
|
|
|
|
|
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
2025-09-20 19:44:27 +02:00
|
|
|
import { Navigate, Outlet, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
2023-08-27 21:39:47 +02:00
|
|
|
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
|
2024-04-29 16:35:08 +02:00
|
|
|
import { loadable } from 'react-lazily/loadable';
|
2024-07-11 17:22:52 +02:00
|
|
|
import Box from '@mui/material/Box';
|
2025-11-02 17:18:49 +01:00
|
|
|
import { AwaitableComponent } from 'awaitable-component';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { AppContext } from '@/base/contexts/AppContext.tsx';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { DefaultNavBar } from '@/features/navigation-bar/components/DefaultNavBar.tsx';
|
2024-10-06 01:27:51 +02:00
|
|
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { WebUIUpdateChecker } from '@/features/app-updates/components/WebUIUpdateChecker.tsx';
|
|
|
|
|
import { ServerUpdateChecker } from '@/features/app-updates/components/ServerUpdateChecker.tsx';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { lazyLoadFallback } from '@/base/utils/LazyLoad.tsx';
|
|
|
|
|
import { ErrorBoundary } from '@/base/components/feedback/ErrorBoundary.tsx';
|
2025-08-16 02:14:49 +02:00
|
|
|
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { MediaQuery } from '@/base/utils/MediaQuery.tsx';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { BrowseTab } from '@/features/browse/Browse.types.ts';
|
2025-07-27 00:03:51 +02:00
|
|
|
import { LoginPage } from '@/features/authentication/screens/LoginPage.tsx';
|
|
|
|
|
import { AuthGuard } from '@/features/authentication/components/AuthGuard.tsx';
|
|
|
|
|
import { SearchParam } from '@/base/Base.types.ts';
|
2025-08-25 02:05:26 +02:00
|
|
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
2025-09-20 19:44:27 +02:00
|
|
|
import { ReactRouter } from '@/lib/react-router/ReactRouter.ts';
|
2025-10-16 20:13:50 +02:00
|
|
|
import { AuthManager } from '@/features/authentication/AuthManager.ts';
|
2025-12-22 22:04:05 +01:00
|
|
|
import { ImageProcessingType } from '@/features/settings/Settings.types.ts';
|
2026-03-13 22:41:28 +01:00
|
|
|
import { MigrationFABIndicator } from '@/features/migration/components/MigrationFABIndicator.tsx';
|
2026-04-23 15:54:37 +02:00
|
|
|
import { MigrationManager } from '@/features/migration/MigrationManager.ts';
|
2026-04-27 15:38:49 +02:00
|
|
|
import { SplashScreen } from '@/features/authentication/components/SplashScreen.tsx';
|
|
|
|
|
import { d } from 'koration';
|
2024-04-29 16:35:08 +02:00
|
|
|
|
2025-08-15 22:02:58 +02:00
|
|
|
const { Browse } = loadable(() => import('@/features/browse/screens/Browse.tsx'), lazyLoadFallback);
|
|
|
|
|
const { DownloadQueue } = loadable(() => import('@/features/downloads/screens/DownloadQueue.tsx'), lazyLoadFallback);
|
|
|
|
|
const { Library } = loadable(() => import('@/features/library/screens/Library.tsx'), lazyLoadFallback);
|
|
|
|
|
const { Manga } = loadable(() => import('@/features/manga/screens/Manga.tsx'), lazyLoadFallback);
|
|
|
|
|
const { SearchAll } = loadable(() => import('@/features/global-search/screens/SearchAll.tsx'), lazyLoadFallback);
|
2026-03-13 22:41:28 +01:00
|
|
|
const { MigrationManualSearch } = loadable(
|
|
|
|
|
() => import('@/features/migration/screens/MigrationManualSearch.tsx'),
|
|
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-08-15 22:02:58 +02:00
|
|
|
const { Settings } = loadable(() => import('@/features/settings/screens/Settings.tsx'), lazyLoadFallback);
|
|
|
|
|
const { About } = loadable(() => import('@/features/settings/screens/About.tsx'), lazyLoadFallback);
|
|
|
|
|
const { Backup } = loadable(() => import('@/features/backup/screens/Backup.tsx'), lazyLoadFallback);
|
2024-10-05 21:39:58 +02:00
|
|
|
const { CategorySettings } = loadable(
|
2025-08-15 22:02:58 +02:00
|
|
|
() => import('@/features/category/screens/CategorySettings.tsx'),
|
2024-10-05 21:39:58 +02:00
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-08-16 15:48:07 +02:00
|
|
|
const { SourceConfigure } = loadable(
|
|
|
|
|
() => import('@/features/source/configuration/screens/SourceConfigure.tsx'),
|
|
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
|
|
|
|
const { SourceMangas } = loadable(() => import('@/features/source/browse/screens/SourceMangas.tsx'), lazyLoadFallback);
|
2025-08-16 16:03:32 +02:00
|
|
|
const { ExtensionInfo } = loadable(
|
|
|
|
|
() => import('@/features/extension/info/screens/ExtensionInfo.tsx'),
|
|
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-08-15 22:02:58 +02:00
|
|
|
const { Updates } = loadable(() => import('@/features/updates/screens/Updates.tsx'), lazyLoadFallback);
|
|
|
|
|
const { History } = loadable(() => import('@/features/history/screens/History.tsx'), lazyLoadFallback);
|
|
|
|
|
const { LibrarySettings } = loadable(() => import('@/features/library/screens/LibrarySettings.tsx'), lazyLoadFallback);
|
2024-10-05 22:31:21 +02:00
|
|
|
const { DownloadSettings } = loadable(
|
2025-08-15 22:02:58 +02:00
|
|
|
() => import('@/features/downloads/screens/DownloadSettings.tsx'),
|
|
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-12-22 21:11:12 +01:00
|
|
|
const { ImagesSettings } = loadable(() => import('@/features/settings/screens/ImagesSettings.tsx'), lazyLoadFallback);
|
|
|
|
|
const { ImageProcessingSetting } = loadable(
|
|
|
|
|
() => import('@/features/settings/screens/ImageProcessingSetting.tsx'),
|
2025-12-07 03:32:28 +01:00
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-08-15 22:02:58 +02:00
|
|
|
const { ServerSettings } = loadable(() => import('@/features/settings/screens/ServerSettings.tsx'), lazyLoadFallback);
|
|
|
|
|
const { BrowseSettings } = loadable(() => import('@/features/browse/screens/BrowseSettings.tsx'), lazyLoadFallback);
|
|
|
|
|
const { WebUISettings } = loadable(() => import('@/features/settings/screens/WebUISettings.tsx'), lazyLoadFallback);
|
2026-03-13 22:41:28 +01:00
|
|
|
const { Migration } = loadable(() => import('@/features/migration/screens/Migration.tsx'), lazyLoadFallback);
|
2025-08-15 22:02:58 +02:00
|
|
|
const { DeviceSetting } = loadable(() => import('@/features/device/screens/DeviceSetting.tsx'), lazyLoadFallback);
|
|
|
|
|
const { TrackingSettings } = loadable(
|
|
|
|
|
() => import('@/features/tracker/screens/TrackingSettings.tsx'),
|
2024-10-05 22:31:21 +02:00
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2024-10-05 21:32:31 +02:00
|
|
|
const { TrackerOAuthLogin } = loadable(
|
2025-08-15 22:02:58 +02:00
|
|
|
() => import('@/features/tracker/screens/TrackerOAuthLogin.tsx'),
|
2024-10-05 21:32:31 +02:00
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2024-10-05 19:33:09 +02:00
|
|
|
const { LibraryDuplicates } = loadable(
|
2025-08-15 22:02:58 +02:00
|
|
|
() => import('@/features/library/screens/LibraryDuplicates.tsx'),
|
2024-10-05 19:33:09 +02:00
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-08-15 22:02:58 +02:00
|
|
|
const { Appearance } = loadable(() => import('@/features/settings/screens/Appearance.tsx'), lazyLoadFallback);
|
2024-12-11 13:43:53 +01:00
|
|
|
const { GlobalReaderSettings } = loadable(
|
2025-08-16 02:02:13 +02:00
|
|
|
() => import('@/features/reader/settings/screens/GlobalReaderSettings.tsx'),
|
2024-11-04 18:00:35 +01:00
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2025-08-15 22:02:58 +02:00
|
|
|
const { More } = loadable(() => import('@/features/settings/screens/More.tsx'), lazyLoadFallback);
|
|
|
|
|
const { Reader } = loadable(() => import('@/features/reader/screens/Reader.tsx'), lazyLoadFallback);
|
|
|
|
|
const { HistorySettings } = loadable(() => import('@/features/history/screens/HistorySettings.tsx'), lazyLoadFallback);
|
2026-07-01 15:10:36 +02:00
|
|
|
const { ExtensionStores } = loadable(
|
|
|
|
|
() => import('@/features/extension/store/screens/ExtensionStores.tsx'),
|
|
|
|
|
lazyLoadFallback,
|
|
|
|
|
);
|
2021-09-09 17:51:22 +04:30
|
|
|
|
2025-07-27 00:03:51 +02:00
|
|
|
if (import.meta.env.DEV) {
|
2023-08-27 21:39:47 +02:00
|
|
|
// Adds messages only in a dev environment
|
|
|
|
|
loadDevMessages();
|
|
|
|
|
loadErrorMessages();
|
|
|
|
|
}
|
2023-12-10 15:58:21 +01:00
|
|
|
|
|
|
|
|
const ScrollToTop = () => {
|
|
|
|
|
const { pathname } = useLocation();
|
|
|
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
|
window.scrollTo(0, 0);
|
|
|
|
|
}, [pathname]);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-27 15:38:49 +02:00
|
|
|
const InitializeGuard = ({ children }: PropsWithChildren) => {
|
|
|
|
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
type RequestConfig = [string, () => Promise<unknown>][];
|
|
|
|
|
type InFlightRequest = [string, Promise<unknown>];
|
|
|
|
|
|
|
|
|
|
const initialRequests: RequestConfig = [
|
|
|
|
|
['globalMeta', () => requestManager.getGlobalMeta().response],
|
|
|
|
|
['serverSettings', () => requestManager.getServerSettings().response],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const executeRequests = async (requests: RequestConfig, timeout: number = d(5).seconds.inWholeMilliseconds) => {
|
|
|
|
|
const runningRequests = requests.map(([key, fn]) => [key, fn()] satisfies InFlightRequest);
|
|
|
|
|
|
|
|
|
|
const failedRequests = runningRequests.filter(async ([_, request]) => {
|
|
|
|
|
try {
|
|
|
|
|
await request;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (failedRequests.length) {
|
|
|
|
|
await new Promise((resolve) => {
|
|
|
|
|
setTimeout(resolve, timeout);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return executeRequests(
|
|
|
|
|
requests.filter(([key]) => !failedRequests.some(([k]) => k === key)),
|
|
|
|
|
(timeout * 1.5) % d(2).minutes.inWholeMilliseconds,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
executeRequests(initialRequests).catch(defaultPromiseErrorHandler('InitializeGuard'));
|
|
|
|
|
|
|
|
|
|
setIsInitialized(true);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
if (isInitialized) {
|
|
|
|
|
return children;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <SplashScreen />;
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-25 02:05:26 +02:00
|
|
|
const InitialBackgroundRequests = () => {
|
2025-10-16 21:56:54 +02:00
|
|
|
// Load the full download status once on startup to fill the cache
|
|
|
|
|
requestManager.useGetDownloadStatus({ nextFetchPolicy: 'standby' });
|
|
|
|
|
|
2025-08-25 02:05:26 +02:00
|
|
|
const [fetchExtensionList] = requestManager.useExtensionListFetch();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// Fetch extension list on startup to show up-to-date number of available extension updates in the navigation bar
|
|
|
|
|
// without having to open the extensions page.
|
|
|
|
|
fetchExtensionList().catch(defaultPromiseErrorHandler('App::InitialBackgroundRequests: extension list'));
|
2025-10-16 21:52:50 +02:00
|
|
|
}, []);
|
2025-08-25 02:05:26 +02:00
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-01 22:40:39 +01:00
|
|
|
/**
|
|
|
|
|
* Creates permanent subscriptions to always have the latest data.
|
|
|
|
|
*
|
|
|
|
|
* E.g. in case a view is open, which does not subscribe to the download updates, finished downloads are never received
|
|
|
|
|
* and thus, data of existing chapters/mangas in the cache get outdated
|
|
|
|
|
*/
|
|
|
|
|
const BackgroundSubscriptions = () => {
|
2025-11-06 21:38:00 +01:00
|
|
|
const { isAuthRequired, accessToken } = AuthManager.useSession();
|
|
|
|
|
|
|
|
|
|
const skipConnection = isAuthRequired === null || (isAuthRequired && !accessToken);
|
2025-07-27 00:03:51 +02:00
|
|
|
|
|
|
|
|
requestManager.useDownloadSubscription({ skip: skipConnection });
|
|
|
|
|
requestManager.useUpdaterSubscription({ skip: skipConnection });
|
|
|
|
|
requestManager.useWebUIUpdateSubscription({ skip: skipConnection });
|
2026-07-02 22:44:10 +02:00
|
|
|
requestManager.useSyncSubscription({ skip: skipConnection });
|
2024-01-01 22:40:39 +01:00
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-20 19:44:27 +02:00
|
|
|
const ReactRouterSetter = () => {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
ReactRouter.setNavigateFn(navigate);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-23 15:54:37 +02:00
|
|
|
const ResumeMigration = () => {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!MigrationManager.isActive()) {
|
2026-06-06 17:45:43 +02:00
|
|
|
navigator.locks
|
2026-06-07 00:34:31 +02:00
|
|
|
?.request('migration-executor', async () => {
|
2026-06-06 17:45:43 +02:00
|
|
|
const resumed = await MigrationManager.resume();
|
|
|
|
|
if (resumed) {
|
|
|
|
|
await MigrationManager.awaitCompletion();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(defaultPromiseErrorHandler('ResumeMigration'));
|
2026-04-23 15:54:37 +02:00
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-27 00:03:51 +02:00
|
|
|
const PrivateRoutes = () => {
|
2025-10-16 21:52:50 +02:00
|
|
|
const isAuthenticated = AuthManager.useIsAuthenticated();
|
2025-07-27 00:03:51 +02:00
|
|
|
|
|
|
|
|
if (!isAuthenticated) {
|
|
|
|
|
return (
|
|
|
|
|
<Navigate
|
|
|
|
|
to={{
|
2026-05-28 00:01:54 +02:00
|
|
|
pathname: AppRoutes.authentication.children.login.path,
|
2025-07-27 00:03:51 +02:00
|
|
|
search: `${SearchParam.REDIRECT}=${window.location.pathname}`,
|
|
|
|
|
}}
|
|
|
|
|
replace
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <Outlet />;
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-11 17:22:52 +02:00
|
|
|
const MainApp = () => {
|
2024-08-30 04:34:28 +02:00
|
|
|
const { navBarWidth, appBarHeight, bottomBarHeight } = useNavBarContext();
|
2025-04-05 15:23:39 +02:00
|
|
|
const isMobileWidth = MediaQuery.useIsMobileWidth();
|
2024-07-11 17:22:52 +02:00
|
|
|
|
2025-03-22 01:16:38 +01:00
|
|
|
const {
|
|
|
|
|
settings: { hideHistory },
|
|
|
|
|
} = useMetadataServerSettings();
|
|
|
|
|
|
2024-07-11 17:22:52 +02:00
|
|
|
return (
|
|
|
|
|
<Box
|
2024-03-07 01:12:35 +01:00
|
|
|
id="appMainContainer"
|
2024-07-11 17:22:52 +02:00
|
|
|
component="main"
|
2024-03-07 01:12:35 +01:00
|
|
|
sx={{
|
2024-08-30 04:34:28 +02:00
|
|
|
minHeight: `calc(100vh - ${appBarHeight + bottomBarHeight}px)`,
|
2024-07-29 17:22:49 +02:00
|
|
|
width: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`,
|
|
|
|
|
minWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`,
|
2024-07-13 13:01:33 +02:00
|
|
|
maxWidth: `calc(100vw - (100vw - 100%) - ${navBarWidth}px)`,
|
2024-07-11 17:22:52 +02:00
|
|
|
position: 'relative',
|
2024-08-30 04:34:28 +02:00
|
|
|
mt: `${appBarHeight}px`,
|
2024-12-15 22:37:11 +01:00
|
|
|
pb: `calc(${bottomBarHeight}px + ${!bottomBarHeight ? 'env(safe-area-inset-bottom)' : '0px'})`,
|
2025-01-08 23:59:26 +01:00
|
|
|
pr: 'env(safe-area-inset-right)',
|
2024-03-07 01:12:35 +01:00
|
|
|
}}
|
|
|
|
|
>
|
2024-04-29 16:35:08 +02:00
|
|
|
<ErrorBoundary>
|
|
|
|
|
<Routes>
|
2025-07-27 00:03:51 +02:00
|
|
|
<Route path={AppRoutes.authentication.match}>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.authentication.children.login.match} element={<LoginPage />} />
|
2025-07-27 00:03:51 +02:00
|
|
|
</Route>
|
|
|
|
|
|
|
|
|
|
<Route element={<PrivateRoutes />}>
|
|
|
|
|
{/* General Routes */}
|
|
|
|
|
<Route
|
|
|
|
|
path={AppRoutes.root.match}
|
|
|
|
|
element={<Navigate to={AppRoutes.library.path()} replace />}
|
|
|
|
|
/>
|
|
|
|
|
<Route
|
|
|
|
|
path={AppRoutes.matchAll.match}
|
|
|
|
|
element={<Navigate to={AppRoutes.root.path} replace />}
|
|
|
|
|
/>
|
|
|
|
|
{isMobileWidth && <Route path={AppRoutes.more.match} element={<More />} />}
|
|
|
|
|
<Route path={AppRoutes.about.match} element={<About />} />
|
|
|
|
|
<Route path={AppRoutes.settings.match}>
|
|
|
|
|
<Route index element={<Settings />} />
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.settings.children.categories.match} element={<CategorySettings />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.reader.match} element={<GlobalReaderSettings />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.library.match}>
|
2025-07-27 00:03:51 +02:00
|
|
|
<Route index element={<LibrarySettings />} />
|
|
|
|
|
<Route
|
2026-05-28 00:01:54 +02:00
|
|
|
path={AppRoutes.settings.children.library.children.duplicates.match}
|
2025-07-27 00:03:51 +02:00
|
|
|
element={<LibraryDuplicates />}
|
|
|
|
|
/>
|
|
|
|
|
</Route>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.settings.children.download.match}>
|
2025-12-07 03:32:28 +01:00
|
|
|
<Route index element={<DownloadSettings />} />
|
2025-12-22 21:11:12 +01:00
|
|
|
{/* TODO: deprecated - got moved to "settings/images/processing/downloads" */}
|
2025-12-07 03:32:28 +01:00
|
|
|
<Route
|
2026-05-28 00:01:54 +02:00
|
|
|
path={AppRoutes.settings.children.download.children.conversions.match}
|
2025-12-22 21:11:12 +01:00
|
|
|
element={
|
|
|
|
|
<Navigate
|
2026-05-28 00:01:54 +02:00
|
|
|
to={AppRoutes.settings.children.images.children.processingDownloads.path}
|
2025-12-22 21:11:12 +01:00
|
|
|
replace
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</Route>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.settings.children.images.match}>
|
2025-12-22 21:11:12 +01:00
|
|
|
<Route index element={<ImagesSettings />} />
|
|
|
|
|
<Route
|
2026-05-28 00:01:54 +02:00
|
|
|
path={AppRoutes.settings.children.images.children.processingDownloads.match}
|
2025-12-22 22:04:05 +01:00
|
|
|
element={<ImageProcessingSetting type={ImageProcessingType.DOWNLOAD} />}
|
|
|
|
|
/>
|
|
|
|
|
<Route
|
2026-05-28 00:01:54 +02:00
|
|
|
path={AppRoutes.settings.children.images.children.processingServe.match}
|
2025-12-22 22:04:05 +01:00
|
|
|
element={<ImageProcessingSetting type={ImageProcessingType.SERVE} />}
|
2025-12-07 03:32:28 +01:00
|
|
|
/>
|
|
|
|
|
</Route>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.settings.children.backup.match} element={<Backup />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.server.match} element={<ServerSettings />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.webui.match} element={<WebUISettings />} />
|
2026-07-01 15:10:36 +02:00
|
|
|
<Route path={AppRoutes.settings.children.browse.match}>
|
|
|
|
|
<Route index element={<BrowseSettings />} />
|
|
|
|
|
<Route
|
|
|
|
|
path={AppRoutes.settings.children.browse.children.extensionStores.match}
|
|
|
|
|
element={<ExtensionStores />}
|
|
|
|
|
/>
|
|
|
|
|
</Route>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.settings.children.history.match} element={<HistorySettings />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.device.match} element={<DeviceSetting />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.tracking.match} element={<TrackingSettings />} />
|
|
|
|
|
<Route path={AppRoutes.settings.children.appearance.match} element={<Appearance />} />
|
2024-05-04 15:41:21 +02:00
|
|
|
</Route>
|
2021-03-18 21:46:24 +03:30
|
|
|
|
2025-07-27 00:03:51 +02:00
|
|
|
{/* Manga Routes */}
|
2021-01-23 01:20:16 +03:30
|
|
|
|
2025-07-27 00:03:51 +02:00
|
|
|
<Route path={AppRoutes.sources.match}>
|
|
|
|
|
{/* TODO: deprecated - "source" and "extension" page got merged into "browse" */}
|
|
|
|
|
<Route index element={<Navigate to={AppRoutes.browse.path(BrowseTab.SOURCES)} replace />} />
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.sources.children.browse.match} element={<SourceMangas />} />
|
|
|
|
|
<Route path={AppRoutes.sources.children.configure.match} element={<SourceConfigure />} />
|
|
|
|
|
<Route path={AppRoutes.sources.children.searchAll.match} element={<SearchAll />} />
|
2025-07-27 00:03:51 +02:00
|
|
|
</Route>
|
|
|
|
|
<Route path={AppRoutes.extension.match}>
|
2025-12-22 21:11:12 +01:00
|
|
|
{/* TODO: deprecated - "source" and "extension" page got merged into "browse" */}
|
2025-07-27 00:03:51 +02:00
|
|
|
<Route
|
|
|
|
|
index
|
|
|
|
|
element={<Navigate to={AppRoutes.browse.path(BrowseTab.EXTENSIONS)} replace />}
|
|
|
|
|
/>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.extension.children.info.match} element={<ExtensionInfo />} />
|
2025-07-27 00:03:51 +02:00
|
|
|
</Route>
|
|
|
|
|
<Route path={AppRoutes.downloads.match} element={<DownloadQueue />} />
|
|
|
|
|
<Route path={AppRoutes.manga.match}>
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.manga.children.reader.match} element={null} />
|
2025-07-27 00:03:51 +02:00
|
|
|
<Route index element={<Manga />} />
|
|
|
|
|
</Route>
|
|
|
|
|
<Route path={AppRoutes.library.match} element={<Library />} />
|
|
|
|
|
<Route path={AppRoutes.updates.match} element={<Updates />} />
|
|
|
|
|
{!hideHistory && <Route path={AppRoutes.history.match} element={<History />} />}
|
|
|
|
|
<Route path={AppRoutes.browse.match} element={<Browse />} />
|
2026-03-13 22:41:28 +01:00
|
|
|
<Route path={AppRoutes.browse.match} element={<Browse />} />
|
2025-07-27 00:03:51 +02:00
|
|
|
<Route path={AppRoutes.migrate.match}>
|
2026-03-13 22:41:28 +01:00
|
|
|
<Route index element={<Migration />} />
|
2026-05-28 00:01:54 +02:00
|
|
|
<Route path={AppRoutes.migrate.children.singleMangaSearch.match} element={<SearchAll />} />
|
2026-03-13 22:41:28 +01:00
|
|
|
<Route
|
2026-05-28 00:01:54 +02:00
|
|
|
path={AppRoutes.migrate.children.manualSearch.match}
|
2026-03-13 22:41:28 +01:00
|
|
|
element={<MigrationManualSearch />}
|
|
|
|
|
/>
|
2025-07-27 00:03:51 +02:00
|
|
|
</Route>
|
|
|
|
|
<Route path={AppRoutes.tracker.match} element={<TrackerOAuthLogin />} />
|
2024-04-29 16:35:08 +02:00
|
|
|
</Route>
|
|
|
|
|
</Routes>
|
|
|
|
|
</ErrorBoundary>
|
2024-07-11 17:22:52 +02:00
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const ReaderApp = () => (
|
|
|
|
|
<ErrorBoundary>
|
|
|
|
|
<Routes>
|
2025-09-03 00:13:41 +02:00
|
|
|
<Route element={<PrivateRoutes />}>
|
|
|
|
|
<Route path={AppRoutes.matchAll.match} element={<Reader />} />
|
|
|
|
|
</Route>
|
2024-07-11 17:22:52 +02:00
|
|
|
</Routes>
|
|
|
|
|
</ErrorBoundary>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const App: React.FC = () => (
|
|
|
|
|
<AppContext>
|
|
|
|
|
<ScrollToTop />
|
2025-11-02 17:18:49 +01:00
|
|
|
<AwaitableComponent.Root />
|
2025-10-03 22:34:46 +02:00
|
|
|
|
2026-01-11 23:02:00 +01:00
|
|
|
<ReactRouterSetter />
|
|
|
|
|
|
|
|
|
|
<CssBaseline enableColorScheme />
|
|
|
|
|
|
2025-10-15 02:50:43 +02:00
|
|
|
<AuthGuard>
|
2026-04-27 15:38:49 +02:00
|
|
|
<InitializeGuard>
|
|
|
|
|
<ServerUpdateChecker />
|
|
|
|
|
<WebUIUpdateChecker />
|
|
|
|
|
<InitialBackgroundRequests />
|
|
|
|
|
<BackgroundSubscriptions />
|
|
|
|
|
<ResumeMigration />
|
|
|
|
|
|
|
|
|
|
<Box sx={{ display: 'flex' }}>
|
|
|
|
|
<Box sx={{ flexShrink: 0, position: 'relative', height: '100vh' }}>
|
|
|
|
|
<DefaultNavBar />
|
|
|
|
|
</Box>
|
|
|
|
|
<Routes>
|
|
|
|
|
<Route path={AppRoutes.matchAll.match} element={<MainApp />} />
|
|
|
|
|
<Route path={AppRoutes.reader.match} element={<ReaderApp />} />
|
|
|
|
|
</Routes>
|
2025-07-27 00:03:51 +02:00
|
|
|
</Box>
|
2026-04-27 15:38:49 +02:00
|
|
|
<MigrationFABIndicator />
|
|
|
|
|
</InitializeGuard>
|
2025-07-27 00:03:51 +02:00
|
|
|
</AuthGuard>
|
2022-11-26 13:51:56 +01:00
|
|
|
</AppContext>
|
|
|
|
|
);
|