diff --git a/package-lock.json b/package-lock.json
index 765cfd27cd..50dd570d6f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10760,6 +10760,23 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
+ "react-router": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
+ "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==",
+ "requires": {
+ "history": "^5.2.0"
+ }
+ },
+ "react-router-dom": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz",
+ "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==",
+ "requires": {
+ "history": "^5.2.0",
+ "react-router": "6.3.0"
+ }
+ },
"read-file-stdin": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/read-file-stdin/-/read-file-stdin-0.2.1.tgz",
diff --git a/package.json b/package.json
index 8c14b329f1..022483d232 100644
--- a/package.json
+++ b/package.json
@@ -96,6 +96,7 @@
"pdfjs-dist": "2.12.313",
"react": "17.0.2",
"react-dom": "17.0.2",
+ "react-router-dom": "6.3.0",
"resize-observer-polyfill": "1.5.1",
"screenfull": "6.0.0",
"sortablejs": "1.14.0",
diff --git a/src/components/HistoryRouter.tsx b/src/components/HistoryRouter.tsx
new file mode 100644
index 0000000000..d789944e03
--- /dev/null
+++ b/src/components/HistoryRouter.tsx
@@ -0,0 +1,22 @@
+import React, { useLayoutEffect } from 'react';
+import { HistoryRouterProps, Router } from 'react-router-dom';
+
+export function HistoryRouter({ basename, children, history }: HistoryRouterProps) {
+ const [state, setState] = React.useState({
+ action: history.action,
+ location: history.location
+ });
+
+ useLayoutEffect(() => history.listen(setState), [history]);
+
+ return (
+
+ );
+}
diff --git a/src/components/Page.tsx b/src/components/Page.tsx
new file mode 100644
index 0000000000..7772e495cd
--- /dev/null
+++ b/src/components/Page.tsx
@@ -0,0 +1,50 @@
+import React, { FunctionComponent, useEffect, useRef } from 'react';
+
+import viewManager from './viewManager/viewManager';
+
+type PageProps = {
+ title?: string
+};
+
+/**
+ * Page component that handles hiding active non-react views, triggering the required events for
+ * navigation and appRouter state updates, and setting the correct classes and data attributes.
+ */
+const Page: FunctionComponent = ({ children, title }) => {
+ const element = useRef(null);
+
+ useEffect(() => {
+ // hide active non-react views
+ viewManager.hideView();
+ }, []);
+
+ useEffect(() => {
+ const event = {
+ bubbles: true,
+ cancelable: false,
+ detail: {
+ isRestored: false
+ }
+ };
+ // pagebeforeshow - hides tabs on tabless pages in libraryMenu
+ element.current?.dispatchEvent(new CustomEvent('pagebeforeshow', event));
+ // viewshow - updates state of appRouter
+ element.current?.dispatchEvent(new CustomEvent('viewshow', event));
+ // pageshow - updates header/navigation in libraryMenu
+ element.current?.dispatchEvent(new CustomEvent('pageshow', event));
+ }, [ element ]);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default Page;
diff --git a/src/components/appRouter.js b/src/components/appRouter.js
index 3b5f356653..7cad70394a 100644
--- a/src/components/appRouter.js
+++ b/src/components/appRouter.js
@@ -122,7 +122,7 @@ class AppRouter {
isBack: action === Action.Pop
});
} else {
- console.warn('[appRouter] "%s" route not found', normalizedPath, location);
+ console.info('[appRouter] "%s" route not found', normalizedPath, location);
}
}
@@ -139,7 +139,7 @@ class AppRouter {
Events.on(apiClient, 'requestfail', this.onRequestFail);
});
- ServerConnections.connect().then(result => {
+ return ServerConnections.connect().then(result => {
this.firstConnectionResult = result;
// Handle the initial route
diff --git a/src/components/pages/SearchPage.tsx b/src/components/pages/SearchPage.tsx
deleted file mode 100644
index a63d450e8e..0000000000
--- a/src/components/pages/SearchPage.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React, { FunctionComponent, useState } from 'react';
-
-import SearchFields from '../search/SearchFields';
-import SearchResults from '../search/SearchResults';
-import SearchSuggestions from '../search/SearchSuggestions';
-import LiveTVSearchResults from '../search/LiveTVSearchResults';
-
-type SearchProps = {
- serverId?: string,
- parentId?: string,
- collectionType?: string
-};
-
-const SearchPage: FunctionComponent = ({ serverId, parentId, collectionType }: SearchProps) => {
- const [ query, setQuery ] = useState();
-
- return (
- <>
-
- {!query &&
-
- }
-
-
- >
- );
-};
-
-export default SearchPage;
diff --git a/src/components/search/LiveTVSearchResults.tsx b/src/components/search/LiveTVSearchResults.tsx
index 67378bc138..b79a24c3be 100644
--- a/src/components/search/LiveTVSearchResults.tsx
+++ b/src/components/search/LiveTVSearchResults.tsx
@@ -21,8 +21,8 @@ const CARD_OPTIONS = {
type LiveTVSearchResultsProps = {
serverId?: string;
- parentId?: string;
- collectionType?: string;
+ parentId?: string | null;
+ collectionType?: string | null;
query?: string;
}
diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx
index 9f6d30a708..a54e568a3c 100644
--- a/src/components/search/SearchResults.tsx
+++ b/src/components/search/SearchResults.tsx
@@ -9,8 +9,8 @@ import SearchResultsRow from './SearchResultsRow';
type SearchResultsProps = {
serverId?: string;
- parentId?: string;
- collectionType?: string;
+ parentId?: string | null;
+ collectionType?: string | null;
query?: string;
}
diff --git a/src/components/search/SearchSuggestions.tsx b/src/components/search/SearchSuggestions.tsx
index fee361a162..abc24eb7f6 100644
--- a/src/components/search/SearchSuggestions.tsx
+++ b/src/components/search/SearchSuggestions.tsx
@@ -22,7 +22,7 @@ const createSuggestionLink = ({ name, href }: { name: string, href: string }) =>
type SearchSuggestionsProps = {
serverId?: string;
- parentId?: string;
+ parentId?: string | null;
}
const SearchSuggestions: FunctionComponent = ({ serverId = window.ApiClient.serverId(), parentId }: SearchSuggestionsProps) => {
diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js
index 501bd928ae..5073cccf8c 100644
--- a/src/components/viewManager/viewManager.js
+++ b/src/components/viewManager/viewManager.js
@@ -147,6 +147,15 @@ class ViewManager {
});
}
+ hideView() {
+ if (currentView) {
+ dispatchViewEvent(currentView, null, 'viewbeforehide');
+ dispatchViewEvent(currentView, null, 'viewhide');
+ currentView.classList.add('hide');
+ currentView = null;
+ }
+ }
+
tryRestoreView(options, onViewChanging) {
if (options.cancel) {
return Promise.reject({ cancelled: true });
diff --git a/src/controllers/search.html b/src/controllers/search.html
deleted file mode 100644
index e6fa92c0fc..0000000000
--- a/src/controllers/search.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/src/index.html b/src/index.html
index f6a4a956c8..d77378d3f8 100644
--- a/src/index.html
+++ b/src/index.html
@@ -158,6 +158,7 @@
+