ui/App.jsx (166 lines of code) (raw):

import React, { Suspense, lazy } from 'react'; import { Route, Switch } from 'react-router-dom'; import { hot } from 'react-hot-loader/root'; import { ConnectedRouter } from 'connected-react-router'; import { Provider } from 'react-redux'; import { permaLinkPrefix } from './perfherder/perf-helpers/constants'; import { configureStore, history } from './job-view/redux/configureStore'; import LoadingSpinner from './shared/LoadingSpinner'; import LoginCallback from './login-callback/LoginCallback'; import TaskclusterCallback from './taskcluster-auth-callback/TaskclusterCallback'; import UserGuideApp from './userguide/App'; import treeFavicon from './img/tree_open.png'; import logFavicon from './img/logviewerIcon.png'; import perfFavicon from './img/line_chart.png'; import healthFavicon from './img/push-health-ok.png'; const IntermittentFailuresApp = lazy(() => import('./intermittent-failures/App'), ); const PerfherderApp = lazy(() => import('./perfherder/App')); const PushHealthApp = lazy(() => import('./push-health/App')); const JobsViewApp = lazy(() => import('./job-view/App')); const LogviewerApp = lazy(() => import('./logviewer/App')); const RedocApp = lazy(() => import('./RedocApp')); // backwards compatibility for routes like this: treeherder.mozilla.org/perf.html#/alerts?id=26622&hideDwnToInv=0 const updateOldUrls = () => { const { pathname, hash, search } = history.location; const updates = {}; const urlMatch = { '/perf.html': '/perfherder', '/pushhealth.html': '/push-health', '/': '/jobs', }; if ( pathname.endsWith('.html') || (pathname === '/' && hash.length) || urlMatch[pathname] ) { updates.pathname = urlMatch[pathname] || pathname.replace(/.html|\//g, ''); } if (hash.length) { const index = hash.indexOf('?'); updates.search = hash.substring(index); const subRoute = hash.substring(1, index); // there are old subroutes such as with the logviewer we want to ignore, i.e.: // https://treeherder.mozilla.org/logviewer.html#/jobs?job_id=319893964&repo=autoland&lineNumber=2728 if (index >= 2 && updates.pathname !== subRoute && subRoute !== '/jobs') { updates.pathname += subRoute; } } else if (search.length) { updates.search = search; } if (Object.keys(updates).length === 0) { return; } history.push(updates); }; // the urls need to be update for compatibility reasons, but we need to have exceptions from this // the link created by the permalink functionality is broken by the updateOldUrls function // for more information - https://bugzilla.mozilla.org/show_bug.cgi?id=1725329 const updateUrls = () => { if (!history.location.hash.includes(permaLinkPrefix)) { updateOldUrls(); } }; const faviconPaths = { '/jobs': { title: 'Treeherder Jobs View', favicon: treeFavicon }, '/logviewer': { title: 'Treeherder Logviewer', favicon: logFavicon, }, '/perfherder': { title: 'Perfherder', favicon: perfFavicon }, '/userguide': { title: 'Treeherder User Guide', favicon: treeFavicon, }, '/intermittent-failures': { title: 'Intermittent Failures View', favicon: treeFavicon, }, '/push-health': { title: 'Push Health', favicon: healthFavicon, }, }; const withFavicon = (element, route) => { let { title } = faviconPaths[route]; const { favicon } = faviconPaths[route]; document.querySelector('link[rel="icon"]').href = favicon; const searchParams = new URLSearchParams(history.location.search); const id = searchParams.get('id'); if (history.location.pathname === '/perfherder/alerts' && id) { title = `Alert #${id.toString()}`; } document.title = title; return <React.Fragment>{element}</React.Fragment>; }; const App = () => { updateUrls(); return ( <Provider store={configureStore()}> <ConnectedRouter history={history}> <Suspense fallback={<LoadingSpinner />}> <Switch> <Route exact path="/login" render={(props) => <LoginCallback {...props} />} /> <Route exact path="/taskcluster-auth" render={(props) => <TaskclusterCallback {...props} />} /> <Route path="/jobs" render={(props) => withFavicon(<JobsViewApp {...props} />, props.location.pathname) } /> <Route path="/logviewer" render={(props) => withFavicon( <LogviewerApp {...props} />, props.location.pathname, ) } /> <Route path="/userguide" render={(props) => withFavicon( <UserGuideApp {...props} />, props.location.pathname, ) } /> <Route path="/push-health" render={(props) => withFavicon(<PushHealthApp {...props} />, '/push-health') } /> <Route path="/intermittent-failures" render={(props) => withFavicon( <IntermittentFailuresApp {...props} />, '/intermittent-failures', ) } /> <Route path="/perfherder" render={(props) => withFavicon(<PerfherderApp {...props} />, '/perfherder') } /> <Route path="/docs" render={(props) => <RedocApp {...props} />} /> </Switch> </Suspense> </ConnectedRouter> </Provider> ); }; export default hot(App);