Lazy load app routes
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"@emotion/react": "11.11.4",
|
"@emotion/react": "11.11.4",
|
||||||
"@emotion/styled": "11.11.5",
|
"@emotion/styled": "11.11.5",
|
||||||
"@fontsource/roboto": "5.0.13",
|
"@fontsource/roboto": "5.0.13",
|
||||||
|
"@loadable/component": "^5.16.4",
|
||||||
"@mui/icons-material": "5.15.15",
|
"@mui/icons-material": "5.15.15",
|
||||||
"@mui/material": "5.15.15",
|
"@mui/material": "5.15.15",
|
||||||
"@mui/x-date-pickers": "7.3.1",
|
"@mui/x-date-pickers": "7.3.1",
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
"react-device-detect": "2.2.3",
|
"react-device-detect": "2.2.3",
|
||||||
"react-dom": "18.3.0",
|
"react-dom": "18.3.0",
|
||||||
"react-i18next": "14.1.1",
|
"react-i18next": "14.1.1",
|
||||||
|
"react-lazily": "^0.9.2",
|
||||||
"react-router-dom": "6.23.0",
|
"react-router-dom": "6.23.0",
|
||||||
"react-virtuoso": "4.7.10",
|
"react-virtuoso": "4.7.10",
|
||||||
"stylis": "4.3.2",
|
"stylis": "4.3.2",
|
||||||
|
|||||||
56
src/App.tsx
56
src/App.tsx
@@ -11,35 +11,39 @@ import CssBaseline from '@mui/material/CssBaseline';
|
|||||||
import React, { useLayoutEffect } from 'react';
|
import React, { useLayoutEffect } from 'react';
|
||||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
|
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
|
||||||
|
import { loadable } from 'react-lazily/loadable';
|
||||||
import { AppContext } from '@/components/context/AppContext';
|
import { AppContext } from '@/components/context/AppContext';
|
||||||
import { Browse } from '@/screens/Browse';
|
|
||||||
import { DownloadQueue } from '@/screens/DownloadQueue';
|
|
||||||
import { Library } from '@/screens/Library';
|
|
||||||
import { Manga } from '@/screens/Manga';
|
|
||||||
import { Reader } from '@/screens/Reader';
|
|
||||||
import { SearchAll } from '@/screens/SearchAll';
|
|
||||||
import { Settings } from '@/screens/Settings';
|
|
||||||
import { About } from '@/screens/settings/About';
|
|
||||||
import { Backup } from '@/screens/settings/Backup';
|
|
||||||
import { Categories } from '@/screens/settings/Categories';
|
|
||||||
import { DefaultReaderSettings } from '@/screens/settings/DefaultReaderSettings';
|
|
||||||
import { SourceConfigure } from '@/screens/SourceConfigure';
|
|
||||||
import { SourceMangas } from '@/screens/SourceMangas';
|
|
||||||
import { Updates } from '@/screens/Updates';
|
|
||||||
import '@/i18n';
|
import '@/i18n';
|
||||||
import { LibrarySettings } from '@/screens/settings/LibrarySettings';
|
|
||||||
import { DefaultNavBar } from '@/components/navbar/DefaultNavBar';
|
import { DefaultNavBar } from '@/components/navbar/DefaultNavBar';
|
||||||
import { DownloadSettings } from '@/screens/settings/DownloadSettings.tsx';
|
|
||||||
import { ServerSettings } from '@/screens/settings/ServerSettings.tsx';
|
|
||||||
import { ServerUpdateChecker } from '@/components/util/ServerUpdateChecker.tsx';
|
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { BrowseSettings } from '@/screens/settings/BrowseSettings.tsx';
|
|
||||||
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
|
|
||||||
import { Migrate } from '@/screens/Migrate.tsx';
|
|
||||||
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
|
||||||
import { TrackingSettings } from '@/screens/settings/TrackingSettings.tsx';
|
|
||||||
import { TrackerOAuthLogin } from '@/screens/TrackerOAuthLogin.tsx';
|
|
||||||
import { WebUIUpdateChecker } from '@/components/util/WebUIUpdateChecker.tsx';
|
import { WebUIUpdateChecker } from '@/components/util/WebUIUpdateChecker.tsx';
|
||||||
|
import { ServerUpdateChecker } from '@/components/util/ServerUpdateChecker.tsx';
|
||||||
|
import { lazyLoadFallback } from '@/util/LazyLoad.tsx';
|
||||||
|
import { ErrorBoundary } from '@/util/ErrorBoundary.tsx';
|
||||||
|
|
||||||
|
const { Browse } = loadable(() => import('@/screens/Browse'), lazyLoadFallback);
|
||||||
|
const { DownloadQueue } = loadable(() => import('@/screens/DownloadQueue'), lazyLoadFallback);
|
||||||
|
const { Library } = loadable(() => import('@/screens/Library'), lazyLoadFallback);
|
||||||
|
const { Manga } = loadable(() => import('@/screens/Manga'), lazyLoadFallback);
|
||||||
|
const { Reader } = loadable(() => import('@/screens/Reader'), lazyLoadFallback);
|
||||||
|
const { SearchAll } = loadable(() => import('@/screens/SearchAll'), lazyLoadFallback);
|
||||||
|
const { Settings } = loadable(() => import('@/screens/Settings'), lazyLoadFallback);
|
||||||
|
const { About } = loadable(() => import('@/screens/settings/About'), lazyLoadFallback);
|
||||||
|
const { Backup } = loadable(() => import('@/screens/settings/Backup'), lazyLoadFallback);
|
||||||
|
const { Categories } = loadable(() => import('@/screens/settings/Categories'), lazyLoadFallback);
|
||||||
|
const { DefaultReaderSettings } = loadable(() => import('@/screens/settings/DefaultReaderSettings'), lazyLoadFallback);
|
||||||
|
const { SourceConfigure } = loadable(() => import('@/screens/SourceConfigure'), lazyLoadFallback);
|
||||||
|
const { SourceMangas } = loadable(() => import('@/screens/SourceMangas'), lazyLoadFallback);
|
||||||
|
const { Updates } = loadable(() => import('@/screens/Updates'), lazyLoadFallback);
|
||||||
|
const { LibrarySettings } = loadable(() => import('@/screens/settings/LibrarySettings'), lazyLoadFallback);
|
||||||
|
const { DownloadSettings } = loadable(() => import('@/screens/settings/DownloadSettings.tsx'), lazyLoadFallback);
|
||||||
|
const { ServerSettings } = loadable(() => import('@/screens/settings/ServerSettings.tsx'), lazyLoadFallback);
|
||||||
|
const { BrowseSettings } = loadable(() => import('@/screens/settings/BrowseSettings.tsx'), lazyLoadFallback);
|
||||||
|
const { WebUISettings } = loadable(() => import('@/screens/settings/WebUISettings.tsx'), lazyLoadFallback);
|
||||||
|
const { Migrate } = loadable(() => import('@/screens/Migrate.tsx'), lazyLoadFallback);
|
||||||
|
const { DeviceSetting } = loadable(() => import('@/components/settings/DeviceSetting.tsx'), lazyLoadFallback);
|
||||||
|
const { TrackingSettings } = loadable(() => import('@/screens/settings/TrackingSettings.tsx'), lazyLoadFallback);
|
||||||
|
const { TrackerOAuthLogin } = loadable(() => import('@/screens/TrackerOAuthLogin.tsx'), lazyLoadFallback);
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
// Adds messages only in a dev environment
|
// Adds messages only in a dev environment
|
||||||
@@ -91,6 +95,7 @@ export const App: React.FC = () => (
|
|||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<ErrorBoundary>
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* General Routes */}
|
{/* General Routes */}
|
||||||
<Route path="/" element={<Navigate to="/library" replace />} />
|
<Route path="/" element={<Navigate to="/library" replace />} />
|
||||||
@@ -132,10 +137,13 @@ export const App: React.FC = () => (
|
|||||||
</Route>
|
</Route>
|
||||||
<Route path="tracker/login/oauth" element={<TrackerOAuthLogin />} />
|
<Route path="tracker/login/oauth" element={<TrackerOAuthLogin />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</ErrorBoundary>
|
||||||
</Container>
|
</Container>
|
||||||
|
<ErrorBoundary>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="manga/:mangaId/chapter/:chapterIndex" element={<Reader />} />
|
<Route path="manga/:mangaId/chapter/:chapterIndex" element={<Reader />} />
|
||||||
<Route path="*" element={null} />
|
<Route path="*" element={null} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</ErrorBoundary>
|
||||||
</AppContext>
|
</AppContext>
|
||||||
);
|
);
|
||||||
|
|||||||
86
src/util/ErrorBoundary.tsx
Normal file
86
src/util/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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 { Component, ErrorInfo, ReactNode, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children?: ReactNode;
|
||||||
|
setTrackPathChange: (change: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
error: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealErrorBoundary extends Component<Props, State> {
|
||||||
|
// eslint-disable-next-line react/state-in-constructor
|
||||||
|
public state: State = { error: null };
|
||||||
|
|
||||||
|
private prevPath: string = '';
|
||||||
|
|
||||||
|
public static getDerivedStateFromError(error: any): State {
|
||||||
|
// Update state so the next render will show the fallback UI.
|
||||||
|
return { error };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.prevPath = window.location.pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate() {
|
||||||
|
if (window.location.pathname !== this.prevPath) {
|
||||||
|
this.setState({ error: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevPath = window.location.pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||||
|
console.error('Uncaught error:', error, errorInfo);
|
||||||
|
// eslint-disable-next-line react/destructuring-assignment
|
||||||
|
this.props.setTrackPathChange(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { error } = this.state;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Something went wrong.</h1>
|
||||||
|
<p>{error.message ?? JSON.stringify(error)}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { children } = this.props;
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ErrorBoundary = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const [key, setKey] = useState(0);
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const previousPathnameRef = useRef(pathname);
|
||||||
|
const [trackPathChange, setTrackPathChange] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (trackPathChange && previousPathnameRef.current !== pathname) {
|
||||||
|
previousPathnameRef.current = pathname;
|
||||||
|
setKey((currentKey) => (currentKey + 1) % 999999);
|
||||||
|
setTrackPathChange(false);
|
||||||
|
}
|
||||||
|
}, [pathname, previousPathnameRef.current, trackPathChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RealErrorBoundary key={key} setTrackPathChange={setTrackPathChange}>
|
||||||
|
{children}
|
||||||
|
</RealErrorBoundary>
|
||||||
|
);
|
||||||
|
};
|
||||||
11
src/util/LazyLoad.tsx
Normal file
11
src/util/LazyLoad.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* 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 { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
|
||||||
|
|
||||||
|
export const lazyLoadFallback = { fallback: <LoadingPlaceholder /> };
|
||||||
18
yarn.lock
18
yarn.lock
@@ -1123,7 +1123,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.8.4":
|
"@babel/runtime@^7.12.18", "@babel/runtime@^7.8.4":
|
||||||
version "7.24.4"
|
version "7.24.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
|
||||||
integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
|
integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
|
||||||
@@ -2049,6 +2049,15 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz#9d68877a489107411b953c54ea65d0658b515809"
|
resolved "https://registry.yarnpkg.com/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz#9d68877a489107411b953c54ea65d0658b515809"
|
||||||
integrity sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==
|
integrity sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==
|
||||||
|
|
||||||
|
"@loadable/component@^5.16.4":
|
||||||
|
version "5.16.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@loadable/component/-/component-5.16.4.tgz#583cb65945a5061ca3c6b90eb34835c154283ff4"
|
||||||
|
integrity sha512-fJWxx9b5WHX90QKmizo9B+es2so8DnBthI1mbflwCoOyvzEwxiZ/SVDCTtXEnHG72/kGBdzr297SSIekYtzSOQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.18"
|
||||||
|
hoist-non-react-statics "^3.3.1"
|
||||||
|
react-is "^16.12.0"
|
||||||
|
|
||||||
"@mui/base@5.0.0-beta.40", "@mui/base@^5.0.0-beta.40":
|
"@mui/base@5.0.0-beta.40", "@mui/base@^5.0.0-beta.40":
|
||||||
version "5.0.0-beta.40"
|
version "5.0.0-beta.40"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2"
|
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2"
|
||||||
@@ -5894,7 +5903,7 @@ react-i18next@14.1.1:
|
|||||||
"@babel/runtime" "^7.23.9"
|
"@babel/runtime" "^7.23.9"
|
||||||
html-parse-stringify "^3.0.1"
|
html-parse-stringify "^3.0.1"
|
||||||
|
|
||||||
react-is@^16.13.1, react-is@^16.7.0:
|
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
@@ -5909,6 +5918,11 @@ react-is@^18.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||||
|
|
||||||
|
react-lazily@^0.9.2:
|
||||||
|
version "0.9.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-lazily/-/react-lazily-0.9.2.tgz#74596dbde43c8e0f607445da5c4839cf6cc48ab5"
|
||||||
|
integrity sha512-oBVRDQ+SuMPWenBO/0Kq+iZk34lOYJEmjiTto4bYRufndf8pux3E50BT3mJZbsq0vBsAVbX3fpQjlUvsXgDVag==
|
||||||
|
|
||||||
react-property@2.0.2:
|
react-property@2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.2.tgz#d5ac9e244cef564880a610bc8d868bd6f60fdda6"
|
resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.2.tgz#d5ac9e244cef564880a610bc8d868bd6f60fdda6"
|
||||||
|
|||||||
Reference in New Issue
Block a user