diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 963f3b9..cf23657 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,6 +26,7 @@ "proj4": "^2.7.5", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-helmet": "^6.1.0", "react-hook-form": "^6.15.8", "react-map-gl": "^6.1.17", "react-markdown": "^5.0.3", @@ -7315,6 +7316,20 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, "node_modules/react-hook-form": { "version": "6.15.8", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.8.tgz", @@ -7503,6 +7518,14 @@ "react": ">=15" } }, + "node_modules/react-side-effect": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", + "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -14572,6 +14595,17 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + } + }, "react-hook-form": { "version": "6.15.8", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.8.tgz", @@ -14723,6 +14757,12 @@ "tiny-warning": "^1.0.0" } }, + "react-side-effect": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", + "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", + "requires": {} + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5a0452f..7e36059 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "proj4": "^2.7.5", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-helmet": "^6.1.0", "react-hook-form": "^6.15.8", "react-map-gl": "^6.1.17", "react-markdown": "^5.0.3", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index bd6131f..8dd3d57 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,6 +6,7 @@ import {BrowserRouter as Router, Switch, Route, Link} from 'react-router-dom' import {useObservable} from 'rxjs-hooks' import {from} from 'rxjs' import {pluck} from 'rxjs/operators' +import {Helmet} from "react-helmet"; import {useConfig} from 'config' import styles from './App.module.less' @@ -68,6 +69,10 @@ const App = connect((state) => ({login: state.login}))(function App({login}) { return config ? ( + + + OpenBikeSensor Portal + {config?.banner && } diff --git a/frontend/src/components/Page/index.tsx b/frontend/src/components/Page/index.tsx index b5ce0d4..c881f14 100644 --- a/frontend/src/components/Page/index.tsx +++ b/frontend/src/components/Page/index.tsx @@ -1,31 +1,41 @@ -import React from 'react' -import classnames from 'classnames' -import {Container} from 'semantic-ui-react' +import React from "react"; +import classnames from "classnames"; +import { Container } from "semantic-ui-react"; +import { Helmet } from "react-helmet"; -import styles from './Page.module.less' +import styles from "./Page.module.less"; export default function Page({ small, children, fullScreen, stage, + title, }: { - small?: boolean - children: ReactNode - fullScreen?: boolean - stage?: ReactNode + small?: boolean; + children: ReactNode; + fullScreen?: boolean; + stage?: ReactNode; + title?: string; }) { return ( -
+ {title && ( + + {title} - OpenBikeSensor Portal + )} - > - {stage} - {fullScreen ? children : {children}} -
- ) +
+ {stage} + {fullScreen ? children : {children}} +
+ + ); } diff --git a/frontend/src/pages/ExportPage/index.tsx b/frontend/src/pages/ExportPage/index.tsx index 987c7d2..718c5cd 100644 --- a/frontend/src/pages/ExportPage/index.tsx +++ b/frontend/src/pages/ExportPage/index.tsx @@ -117,7 +117,7 @@ export default function ExportPage() { const config = useConfig(); const exportUrl = `${config?.apiUrl}/export/events?bbox=${bbox}&fmt=${fmt}`; return ( - +
Export
diff --git a/frontend/src/pages/LoginRedirectPage.tsx b/frontend/src/pages/LoginRedirectPage.tsx index 1953025..6e4ff13 100644 --- a/frontend/src/pages/LoginRedirectPage.tsx +++ b/frontend/src/pages/LoginRedirectPage.tsx @@ -35,7 +35,7 @@ const LoginRedirectPage = connect((state) => ({loggedIn: Boolean(state.login)})) if (error) { return ( - + @@ -91,7 +91,7 @@ function ExchangeAuthCode({code}) { ) } - return {content} + return {content} } export default LoginRedirectPage diff --git a/frontend/src/pages/MapPage/index.tsx b/frontend/src/pages/MapPage/index.tsx index af1c3fd..8ca454c 100644 --- a/frontend/src/pages/MapPage/index.tsx +++ b/frontend/src/pages/MapPage/index.tsx @@ -148,7 +148,7 @@ export default function MapPage() { } return ( - +
{layerSidebar && (
diff --git a/frontend/src/pages/NotFoundPage.js b/frontend/src/pages/NotFoundPage.js index 076db5a..71fa133 100644 --- a/frontend/src/pages/NotFoundPage.js +++ b/frontend/src/pages/NotFoundPage.js @@ -7,7 +7,7 @@ import {Page} from '../components' export default function NotFoundPage() { const history = useHistory() return ( - +
Page not found

You know what that means...

diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index abb0b48..8399ff1 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -44,7 +44,7 @@ const SettingsPage = connect((state) => ({login: state.login}), {setLogin})(func }, [setLoading, setLogin, setErrors]) return ( - + diff --git a/frontend/src/pages/TrackEditor.tsx b/frontend/src/pages/TrackEditor.tsx index a4de4a7..74516f9 100644 --- a/frontend/src/pages/TrackEditor.tsx +++ b/frontend/src/pages/TrackEditor.tsx @@ -106,12 +106,13 @@ const TrackEditor = connect((state) => ({login: state.login}))(function TrackEdi } }, [setBusy, setConfirmDelete, slug, history]) + const title = `Edit ${track ? track.title || 'Unnamed track' : 'track'}` return ( - + -
Edit {track ? track.title || 'Unnamed track' : 'track'}
+
{title}
diff --git a/frontend/src/pages/TrackPage/index.tsx b/frontend/src/pages/TrackPage/index.tsx index a962d10..ba2a2ed 100644 --- a/frontend/src/pages/TrackPage/index.tsx +++ b/frontend/src/pages/TrackPage/index.tsx @@ -176,8 +176,10 @@ const TrackPage = connect((state) => ({login: state.login}))(function TrackPage( const [pointsMode, setPointsMode] = React.useState('overtakingEvents') // none|overtakingEvents|measurements const [side, setSide] = React.useState('overtaker') // overtaker|stationary + const title = track ? track.title || 'Unnamed track' : null return ( @@ -204,7 +206,7 @@ const TrackPage = connect((state) => ({login: state.login}))(function TrackPage( {track && ( <> -
{track.title || 'Unnamed track'}
+
{title}
diff --git a/frontend/src/pages/TracksPage.tsx b/frontend/src/pages/TracksPage.tsx index 1cf68da..8bebc8d 100644 --- a/frontend/src/pages/TracksPage.tsx +++ b/frontend/src/pages/TracksPage.tsx @@ -136,9 +136,10 @@ function UploadButton({navigate, ...props}) { } const TracksPage = connect((state) => ({login: (state as any).login}))(function TracksPage({login, privateTracks}) { + const title = privateTracks ? 'My tracks' : 'Public tracks' return ( - -
{privateTracks ? 'My tracks' : 'Public tracks'}
+ +
{title}
{privateTracks && }
diff --git a/frontend/src/pages/UploadPage.tsx b/frontend/src/pages/UploadPage.tsx index 10783a5..04ba7a8 100644 --- a/frontend/src/pages/UploadPage.tsx +++ b/frontend/src/pages/UploadPage.tsx @@ -135,7 +135,7 @@ export default function UploadPage() { } return ( - + {files.length ? (