From: Stas Vilchik Date: Wed, 18 Apr 2018 15:10:29 +0000 (+0200) Subject: SONAR-10601 refresh the page when the web app can not load a js bundle X-Git-Tag: 7.5~1226 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c8992030dd086ea86051462950bb5fd7f640ab48;p=sonarqube.git SONAR-10601 refresh the page when the web app can not load a js bundle --- diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx index 173efed4d1d..20284555423 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -39,6 +39,7 @@ import { import { translate } from '../../helpers/l10n'; import { Extension } from '../types'; import { PluginPendingResult } from '../../api/plugins'; +import handleRequiredAuthorization from '../utils/handleRequiredAuthorization'; interface Props { appState: { @@ -63,10 +64,7 @@ class AdminContainer extends React.PureComponent { componentDidMount() { if (!this.context.canAdmin) { - // workaround cyclic dependencies - import('../utils/handleRequiredAuthorization').then(handleRequredAuthorization => - handleRequredAuthorization.default() - ); + handleRequiredAuthorization(); } else { this.fetchNavigationSettings(); this.props.fetchEditions(this.props.editionsUrl, this.props.appState.version); diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js index 35ba6778ddf..1b0d9750a00 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -79,6 +79,7 @@ import documentationRoutes from '../../apps/documentation/routes'; import webhooksRoutes from '../../apps/webhooks/routes'; import { maintenanceRoutes, setupRoutes } from '../../apps/maintenance/routes'; import { globalPermissionsRoutes, projectPermissionsRoutes } from '../../apps/permissions/routes'; +import { lazyLoad } from '../../components/lazyLoad'; function handleUpdate() { const { action } = this.state.location; @@ -185,10 +186,7 @@ const startReactApp = () => { - - import('../components/ComponentContainer').then(i => i.default) - }> + import('../components/ComponentContainer'))}> diff --git a/server/sonar-web/src/main/js/apps/about/routes.ts b/server/sonar-web/src/main/js/apps/about/routes.ts index f3d76997780..6d586df600f 100644 --- a/server/sonar-web/src/main/js/apps/about/routes.ts +++ b/server/sonar-web/src/main/js/apps/about/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/AboutApp').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/AboutApp')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/account/routes.ts b/server/sonar-web/src/main/js/apps/account/routes.ts index d72da080801..2683c0988f6 100644 --- a/server/sonar-web/src/main/js/apps/account/routes.ts +++ b/server/sonar-web/src/main/js/apps/account/routes.ts @@ -17,42 +17,30 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps, RouteComponent } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/Account').then(i => callback(null, i.default)); - }, + component: lazyLoad(() => import('./components/Account')), childRoutes: [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./profile/Profile').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./profile/Profile')) } }, { path: 'security', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/Security').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/Security')) }, { path: 'projects', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./projects/ProjectsContainer').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./projects/ProjectsContainer')) }, { path: 'notifications', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./notifications/Notifications').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./notifications/Notifications')) }, { path: 'organizations', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./organizations/UserOrganizations').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./organizations/UserOrganizations')) } ] } diff --git a/server/sonar-web/src/main/js/apps/background-tasks/routes.ts b/server/sonar-web/src/main/js/apps/background-tasks/routes.ts index 6035d85f94a..44d7b94e21d 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/routes.ts +++ b/server/sonar-web/src/main/js/apps/background-tasks/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/BackgroundTasksApp').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/BackgroundTasksApp')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/code/routes.ts b/server/sonar-web/src/main/js/apps/code/routes.ts index ad7784f9906..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/code/routes.ts +++ b/server/sonar-web/src/main/js/apps/code/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/App').then(i => callback(null, { component: (i as any).default })); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/routes.ts b/server/sonar-web/src/main/js/apps/coding-rules/routes.ts index 260a9db516a..3a081ad0934 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/routes.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/routes.ts @@ -17,8 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, RouteComponent, RedirectFunction } from 'react-router'; +import { RouterState, RedirectFunction } from 'react-router'; import { parseQuery, serializeQuery } from './query'; +import { lazyLoad } from '../../components/lazyLoad'; import { RawQuery } from '../../helpers/query'; function parseHash(hash: string): RawQuery { @@ -47,9 +48,7 @@ const routes = [ replace({ pathname: nextState.location.pathname, query: normalizedQuery }); } }, - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/App').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/component-measures/routes.ts b/server/sonar-web/src/main/js/apps/component-measures/routes.ts index c5e8c511399..f0f6cc9101c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/routes.ts +++ b/server/sonar-web/src/main/js/apps/component-measures/routes.ts @@ -17,13 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps, RedirectFunction } from 'react-router'; +import { RouterState, RedirectFunction } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/AppContainer').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) } }, { path: 'domain/:domainName', diff --git a/server/sonar-web/src/main/js/apps/component/routes.ts b/server/sonar-web/src/main/js/apps/component/routes.ts index fbd6fae2b97..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/component/routes.ts +++ b/server/sonar-web/src/main/js/apps/component/routes.ts @@ -17,15 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, RouteComponent } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - indexRoute: { - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/App').then(i => callback(null, i.default)); - } - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/custom-measures/routes.ts b/server/sonar-web/src/main/js/apps/custom-measures/routes.ts index ad7784f9906..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/custom-measures/routes.ts +++ b/server/sonar-web/src/main/js/apps/custom-measures/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/App').then(i => callback(null, { component: (i as any).default })); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/custom-metrics/routes.ts b/server/sonar-web/src/main/js/apps/custom-metrics/routes.ts index 9397beea9d1..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/custom-metrics/routes.ts +++ b/server/sonar-web/src/main/js/apps/custom-metrics/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/App').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/groups/components/App.tsx b/server/sonar-web/src/main/js/apps/groups/components/App.tsx index e44998e4bde..3742549abdb 100644 --- a/server/sonar-web/src/main/js/apps/groups/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/groups/components/App.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import { Helmet } from 'react-helmet'; import Header from './Header'; import List from './List'; +import forSingleOrganization from '../../organizations/forSingleOrganization'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; import { searchUsersGroups, deleteGroup, updateGroup, createGroup } from '../../../api/user_groups'; import { Group, Paging } from '../../../app/types'; @@ -39,146 +40,148 @@ interface State { query: string; } -export default class App extends React.PureComponent { - mounted = false; - state: State = { loading: true, query: '' }; +export default forSingleOrganization( + class App extends React.PureComponent { + mounted = false; + state: State = { loading: true, query: '' }; - componentDidMount() { - this.mounted = true; - this.fetchGroups(); - } - - componentWillUnmount() { - this.mounted = false; - } + componentDidMount() { + this.mounted = true; + this.fetchGroups(); + } - get organization() { - return this.props.organization && this.props.organization.key; - } + componentWillUnmount() { + this.mounted = false; + } - makeFetchGroupsRequest = (data?: { p?: number; q?: string }) => { - this.setState({ loading: true }); - return searchUsersGroups({ - organization: this.organization, - q: this.state.query, - ...data - }); - }; - - stopLoading = () => { - if (this.mounted) { - this.setState({ loading: false }); + get organization() { + return this.props.organization && this.props.organization.key; } - }; - fetchGroups = (data?: { p?: number; q?: string }) => { - this.makeFetchGroupsRequest(data).then(({ groups, paging }) => { + makeFetchGroupsRequest = (data?: { p?: number; q?: string }) => { + this.setState({ loading: true }); + return searchUsersGroups({ + organization: this.organization, + q: this.state.query, + ...data + }); + }; + + stopLoading = () => { if (this.mounted) { - this.setState({ groups, loading: false, paging }); + this.setState({ loading: false }); } - }, this.stopLoading); - }; + }; - fetchMoreGroups = () => { - const { paging } = this.state; - if (paging && paging.total > paging.pageIndex * paging.pageSize) { - this.makeFetchGroupsRequest({ p: paging.pageIndex + 1 }).then(({ groups, paging }) => { + fetchGroups = (data?: { p?: number; q?: string }) => { + this.makeFetchGroupsRequest(data).then(({ groups, paging }) => { if (this.mounted) { - this.setState(({ groups: existingGroups = [] }) => ({ - groups: [...existingGroups, ...groups], - loading: false, - paging - })); + this.setState({ groups, loading: false, paging }); } }, this.stopLoading); - } - }; + }; + + fetchMoreGroups = () => { + const { paging } = this.state; + if (paging && paging.total > paging.pageIndex * paging.pageSize) { + this.makeFetchGroupsRequest({ p: paging.pageIndex + 1 }).then(({ groups, paging }) => { + if (this.mounted) { + this.setState(({ groups: existingGroups = [] }) => ({ + groups: [...existingGroups, ...groups], + loading: false, + paging + })); + } + }, this.stopLoading); + } + }; - search = (query: string) => { - this.fetchGroups({ q: query }); - this.setState({ query }); - }; + search = (query: string) => { + this.fetchGroups({ q: query }); + this.setState({ query }); + }; - refresh = () => { - this.fetchGroups({ q: this.state.query }); - }; + refresh = () => { + this.fetchGroups({ q: this.state.query }); + }; - handleCreate = (data: { description: string; name: string }) => { - return createGroup({ ...data, organization: this.organization }).then(group => { - if (this.mounted) { - this.setState(({ groups = [] }: State) => ({ - groups: [...groups, group] - })); - } - }); - }; + handleCreate = (data: { description: string; name: string }) => { + return createGroup({ ...data, organization: this.organization }).then(group => { + if (this.mounted) { + this.setState(({ groups = [] }: State) => ({ + groups: [...groups, group] + })); + } + }); + }; - handleDelete = (name: string) => { - return deleteGroup({ name, organization: this.organization }).then(() => { - if (this.mounted) { - this.setState(({ groups = [] }: State) => ({ - groups: groups.filter(group => group.name !== name) - })); - } - }); - }; + handleDelete = (name: string) => { + return deleteGroup({ name, organization: this.organization }).then(() => { + if (this.mounted) { + this.setState(({ groups = [] }: State) => ({ + groups: groups.filter(group => group.name !== name) + })); + } + }); + }; - handleEdit = (data: { description?: string; id: number; name?: string }) => { - return updateGroup(data).then(() => { - if (this.mounted) { - this.setState(({ groups = [] }: State) => ({ - groups: groups.map(group => (group.id === data.id ? { ...group, ...data } : group)) - })); - } - }); - }; - - render() { - const { groups, loading, paging, query } = this.state; - - const showAnyone = - this.props.organization === undefined && 'anyone'.includes(query.toLowerCase()); - - return ( - <> - - -
-
- - - - {groups !== undefined && ( - { + return updateGroup(data).then(() => { + if (this.mounted) { + this.setState(({ groups = [] }: State) => ({ + groups: groups.map(group => (group.id === data.id ? { ...group, ...data } : group)) + })); + } + }); + }; + + render() { + const { groups, loading, paging, query } = this.state; + + const showAnyone = + this.props.organization === undefined && 'anyone'.includes(query.toLowerCase()); + + return ( + <> + + +
+
+ + - )} - - {groups !== undefined && - paging !== undefined && ( - + + {groups !== undefined && ( + )} -
- - ); + + {groups !== undefined && + paging !== undefined && ( + + )} +
+ + ); + } } -} +); diff --git a/server/sonar-web/src/main/js/apps/groups/routes.ts b/server/sonar-web/src/main/js/apps/groups/routes.ts index 88584bbe9df..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/groups/routes.ts +++ b/server/sonar-web/src/main/js/apps/groups/routes.ts @@ -17,18 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - Promise.all([ - import('./components/App').then(i => i.default), - import('../organizations/forSingleOrganization').then(i => i.default) - ]).then(([App, forSingleOrganization]) => - callback(null, { component: forSingleOrganization(App) }) - ); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/marketplace/routes.ts b/server/sonar-web/src/main/js/apps/marketplace/routes.ts index 40cc5e0ded6..98bf9bf6c67 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/routes.ts +++ b/server/sonar-web/src/main/js/apps/marketplace/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./AppContainer').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./AppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.js b/server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.js deleted file mode 100644 index 013ec53c8d7..00000000000 --- a/server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router'; -import { areThereCustomOrganizations } from '../../store/rootReducer'; - -const forSingleOrganization = (ComposedComponent /*: Object */) => { - class X extends React.PureComponent { - static displayName = `forSingleOrganization(${ComposedComponent.displayName})}`; - - render() { - const { customOrganizations, router, ...other } = this.props; - - if (customOrganizations) { - router.replace('/not_found'); - return null; - } - - return ; - } - } - - const mapStateToProps = state => ({ - customOrganizations: areThereCustomOrganizations(state) - }); - - return connect(mapStateToProps)(withRouter(X)); -}; - -export default forSingleOrganization; diff --git a/server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.tsx b/server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.tsx new file mode 100644 index 00000000000..4ec2c2a2132 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.tsx @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { withRouter, WithRouterProps } from 'react-router'; +import { areThereCustomOrganizations } from '../../store/rootReducer'; + +type ReactComponent

= React.ComponentClass

| React.StatelessComponent

; + +export default function forSingleOrganization

(ComposedComponent: ReactComponent

) { + interface StateProps { + customOrganizations: boolean | undefined; + } + + class ForSingleOrganization extends React.Component { + static displayName = `forSingleOrganization(${ComposedComponent.displayName})}`; + + render() { + const { customOrganizations, router, ...other } = this.props; + + if (!other.params.organizationKey && customOrganizations) { + router.replace('/not_found'); + return null; + } + + return ; + } + } + + const mapStateToProps = (state: any) => ({ + customOrganizations: areThereCustomOrganizations(state) + }); + + return connect(mapStateToProps)(withRouter(ForSingleOrganization)); +} diff --git a/server/sonar-web/src/main/js/apps/overview/routes.ts b/server/sonar-web/src/main/js/apps/overview/routes.ts index ad7784f9906..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/overview/routes.ts +++ b/server/sonar-web/src/main/js/apps/overview/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/App').then(i => callback(null, { component: (i as any).default })); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js b/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js index f9c1134cfbd..6a8777641c8 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js @@ -19,6 +19,7 @@ */ import { connect } from 'react-redux'; import App from './App'; +import forSingleOrganization from '../../organizations/forSingleOrganization'; import { getAppState } from '../../../store/rootReducer'; import { getRootQualifiers } from '../../../store/appState/duck'; @@ -27,4 +28,4 @@ const mapStateToProps = state => ({ topQualifiers: getRootQualifiers(getAppState(state)).filter(q => q !== 'APP') }); -export default connect(mapStateToProps)(App); +export default forSingleOrganization(connect(mapStateToProps)(App)); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/routes.ts b/server/sonar-web/src/main/js/apps/permission-templates/routes.ts index 2c795d171b4..17fa912dc8e 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/routes.ts +++ b/server/sonar-web/src/main/js/apps/permission-templates/routes.ts @@ -17,18 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - Promise.all([ - import('./components/AppContainer').then(i => i.default), - import('../organizations/forSingleOrganization').then(i => i.default) - ]).then(([AppContainer, forSingleOrganization]) => - callback(null, { component: forSingleOrganization(AppContainer) }) - ); - } + indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx index d79c2bd51c8..e9b0a329a90 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx @@ -25,13 +25,14 @@ import PageError from '../../shared/components/PageError'; import Suggestions from '../../../../app/components/embed-docs-modal/Suggestions'; import { translate } from '../../../../helpers/l10n'; import { Organization } from '../../../../app/types'; +import forSingleOrganization from '../../../organizations/forSingleOrganization'; import '../../styles.css'; interface Props { organization?: Organization; } -export default function App({ organization }: Props) { +function App({ organization }: Props) { return (

@@ -42,3 +43,5 @@ export default function App({ organization }: Props) {
); } + +export default forSingleOrganization(App); diff --git a/server/sonar-web/src/main/js/apps/permissions/routes.ts b/server/sonar-web/src/main/js/apps/permissions/routes.ts index 0f8a806aa62..e7705b24cce 100644 --- a/server/sonar-web/src/main/js/apps/permissions/routes.ts +++ b/server/sonar-web/src/main/js/apps/permissions/routes.ts @@ -17,27 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; export const globalPermissionsRoutes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - Promise.all([ - import('./global/components/App').then(i => i.default), - import('../organizations/forSingleOrganization').then(i => i.default) - ]).then(([App, forSingleOrganization]) => - callback(null, { component: forSingleOrganization(App) }) - ); - } + indexRoute: { component: lazyLoad(() => import('./global/components/App')) } } ]; export const projectPermissionsRoutes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./project/components/AppContainer').then(i => - callback(null, { component: i.default }) - ); - } + indexRoute: { component: lazyLoad(() => import('./project/components/AppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/portfolio/routes.ts b/server/sonar-web/src/main/js/apps/portfolio/routes.ts index ad7784f9906..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/routes.ts +++ b/server/sonar-web/src/main/js/apps/portfolio/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/App').then(i => callback(null, { component: (i as any).default })); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/routes.ts b/server/sonar-web/src/main/js/apps/projectActivity/routes.ts index 7c12b45e3ec..af7d4dac0d0 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/routes.ts +++ b/server/sonar-web/src/main/js/apps/projectActivity/routes.ts @@ -17,14 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/ProjectActivityAppContainer').then(i => - callback(null, { component: (i as any).default }) - ); + indexRoute: { + component: lazyLoad(() => import('./components/ProjectActivityAppContainer') as any) } } ]; diff --git a/server/sonar-web/src/main/js/apps/projectBranches/routes.ts b/server/sonar-web/src/main/js/apps/projectBranches/routes.ts index e1d1d87abde..17fa912dc8e 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/routes.ts +++ b/server/sonar-web/src/main/js/apps/projectBranches/routes.ts @@ -17,15 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/AppContainer').then(i => - callback(null, { component: (i as any).default }) - ); - } + indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/projectQualityGate/routes.ts b/server/sonar-web/src/main/js/apps/projectQualityGate/routes.ts index 6df704df94c..6f1319dbb92 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityGate/routes.ts +++ b/server/sonar-web/src/main/js/apps/projectQualityGate/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./App').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts b/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts index 6df704df94c..6f1319dbb92 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./App').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx index 798b1b034f1..1ad68ed7e8a 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx @@ -20,26 +20,30 @@ import * as React from 'react'; import { connect } from 'react-redux'; import App from './App'; -import { Organization } from '../../app/types'; +import forSingleOrganization from '../organizations/forSingleOrganization'; +import { Organization, LoggedInUser } from '../../app/types'; import { onFail } from '../../store/rootActions'; import { getAppState, getOrganizationByKey, getCurrentUser } from '../../store/rootReducer'; import { receiveOrganizations } from '../../store/organizations/duck'; import { changeProjectVisibility } from '../../api/organizations'; import { fetchOrganization } from '../../apps/organizations/actions'; -interface Props { - appState: { - defaultOrganization: string; - qualifiers: string[]; - }; - currentUser: { login: string }; +interface StateProps { + appState: { defaultOrganization: string; qualifiers: string[] }; + currentUser: LoggedInUser; + organization?: Organization; +} + +interface DispatchProps { fetchOrganization: (organization: string) => void; onVisibilityChange: (organization: Organization, visibility: string) => void; +} + +interface OwnProps { onRequestFail: (error: any) => void; - organization?: Organization; } -class AppContainer extends React.PureComponent { +class AppContainer extends React.PureComponent { componentDidMount() { // if there is no organization, that means we are in the global scope // let's fetch defails for the default organization in this case @@ -75,9 +79,9 @@ class AppContainer extends React.PureComponent { } } -const mapStateToProps = (state: any, ownProps: Props) => ({ +const mapStateToProps = (state: any, ownProps: any) => ({ appState: getAppState(state), - currentUser: getCurrentUser(state), + currentUser: getCurrentUser(state) as LoggedInUser, organization: ownProps.organization || getOrganizationByKey(state, getAppState(state).defaultOrganization) }); @@ -99,4 +103,6 @@ const mapDispatchToProps = (dispatch: Function) => ({ dispatch(onVisibilityChange(organization, visibility)) }); -export default connect(mapStateToProps, mapDispatchToProps)(AppContainer); +export default forSingleOrganization( + connect(mapStateToProps, mapDispatchToProps)(AppContainer) +); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/routes.ts b/server/sonar-web/src/main/js/apps/projectsManagement/routes.ts index e2b34050856..98bf9bf6c67 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/routes.ts +++ b/server/sonar-web/src/main/js/apps/projectsManagement/routes.ts @@ -17,18 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - Promise.all([ - import('./AppContainer').then(i => i.default), - import('../organizations/forSingleOrganization').then(i => i.default) - ]).then(([AppContainer, forSingleOrganization]) => - callback(null, { component: forSingleOrganization(AppContainer) }) - ); - } + indexRoute: { component: lazyLoad(() => import('./AppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/routes.ts b/server/sonar-web/src/main/js/apps/quality-gates/routes.ts index 4d9769f5a70..83ba110b6e8 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/routes.ts +++ b/server/sonar-web/src/main/js/apps/quality-gates/routes.ts @@ -17,24 +17,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps, RouteComponent } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/QualityGatesApp').then(i => callback(null, i.default)); - }, + component: lazyLoad(() => import('./components/QualityGatesApp')), childRoutes: [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/Intro').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/Intro')) } }, { path: 'show/:id', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/DetailsApp').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/DetailsApp')) } ] } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx index 2fe1b621c97..3a7a834957f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx @@ -21,14 +21,15 @@ import * as React from 'react'; import { searchQualityProfiles, getExporters, Actions } from '../../../api/quality-profiles'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; import { sortProfiles } from '../utils'; -import { translate } from '../../../helpers/l10n'; +import { Exporter, Profile } from '../types'; import OrganizationHelmet from '../../../components/common/OrganizationHelmet'; +import { translate } from '../../../helpers/l10n'; +import { Languages } from '../../../store/languages/reducer'; import '../styles.css'; -import { Exporter, Profile } from '../types'; interface Props { children: React.ReactElement; - languages: Array<{}>; + languages: Languages; onRequestFail: (reasong: any) => void; organization: { name: string; key: string } | null; } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx index fae68d40027..1f1d6d2f4a5 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx @@ -19,11 +19,21 @@ */ import { connect } from 'react-redux'; import App from './App'; -import { getLanguages, getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer'; +import forSingleOrganization from '../../organizations/forSingleOrganization'; +import { getLanguages, getOrganizationByKey } from '../../../store/rootReducer'; import { onFail } from '../../../store/rootActions'; +import { Languages } from '../../../store/languages/reducer'; + +interface StateProps { + languages: Languages; + organization: { name: string; key: string } | null; +} + +interface DispatchProps { + onRequestFail: (reasong: any) => void; +} const mapStateToProps = (state: any, ownProps: any) => ({ - currentUser: getCurrentUser(state), languages: getLanguages(state), organization: ownProps.params.organizationKey ? getOrganizationByKey(state, ownProps.params.organizationKey) @@ -34,4 +44,6 @@ const mapDispatchToProps = (dispatch: any) => ({ onRequestFail: (error: any) => onFail(dispatch)(error) }); -export default connect(mapStateToProps, mapDispatchToProps)(App as any); +export default forSingleOrganization( + connect(mapStateToProps, mapDispatchToProps)(App) +); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/routes.ts b/server/sonar-web/src/main/js/apps/quality-profiles/routes.ts index 05f00838a0e..8935bac3555 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/routes.ts +++ b/server/sonar-web/src/main/js/apps/quality-profiles/routes.ts @@ -17,49 +17,27 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps, RouteComponent, withRouter } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getComponent(state: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/AppContainer') - .then(i => i.default) - .then(AppContainer => { - if (state.params.organizationKey) { - callback(null, AppContainer); - } else { - import('../organizations/forSingleOrganization') - .then(i => i.default) - .then(forSingleOrganization => callback(null, forSingleOrganization(AppContainer))); - } - }); - }, - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./home/HomeContainer').then(i => callback(null, { component: i.default })); - }, + component: lazyLoad(() => import('./components/AppContainer')), + indexRoute: { component: lazyLoad(() => import('./home/HomeContainer')) }, childRoutes: [ { - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/ProfileContainer').then(i => callback(null, withRouter(i.default))); - }, + component: lazyLoad(() => import('./components/ProfileContainer')), childRoutes: [ { path: 'show', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./details/ProfileDetails').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./details/ProfileDetails')) }, { path: 'changelog', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./changelog/ChangelogContainer').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./changelog/ChangelogContainer')) }, { path: 'compare', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./compare/ComparisonContainer').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./compare/ComparisonContainer')) } ] } diff --git a/server/sonar-web/src/main/js/apps/sessions/routes.ts b/server/sonar-web/src/main/js/apps/sessions/routes.ts index 31b74819be8..28945131363 100644 --- a/server/sonar-web/src/main/js/apps/sessions/routes.ts +++ b/server/sonar-web/src/main/js/apps/sessions/routes.ts @@ -17,32 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, RouteComponent } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { path: 'new', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/LoginContainer').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/LoginContainer')) }, { path: 'logout', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/Logout').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/Logout')) }, { path: 'unauthorized', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/Unauthorized').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/Unauthorized')) }, { path: 'email_already_exists', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/EmailAlreadyExists').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./components/EmailAlreadyExists')) } ]; diff --git a/server/sonar-web/src/main/js/apps/settings/routes.ts b/server/sonar-web/src/main/js/apps/settings/routes.ts index c1c9cc95210..7c54dfe0d72 100644 --- a/server/sonar-web/src/main/js/apps/settings/routes.ts +++ b/server/sonar-web/src/main/js/apps/settings/routes.ts @@ -17,19 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, RouteComponent, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/AppContainer').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) } }, { path: 'encryption', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./encryption/EncryptionAppContainer').then(i => callback(null, i.default)); - } + component: lazyLoad(() => import('./encryption/EncryptionAppContainer')) } ]; diff --git a/server/sonar-web/src/main/js/apps/system/routes.ts b/server/sonar-web/src/main/js/apps/system/routes.ts index 9397beea9d1..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/system/routes.ts +++ b/server/sonar-web/src/main/js/apps/system/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/App').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.js deleted file mode 100644 index 68747dde3f4..00000000000 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import Modal from '../../../components/controls/Modal'; -import { translate } from '../../../helpers/l10n'; - -/*:: -type Props = {| - onFinish: () => void -|}; -*/ - -/*:: -type State = { - OnboardingContainer?: Object -}; -*/ - -export default class OnboardingModal extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = {}; - - componentDidMount() { - this.mounted = true; - import('./OnboardingContainer').then(i => this.receiveComponent(i.default)); - } - - componentWillUnmount() { - this.mounted = false; - } - - receiveComponent = (OnboardingContainer /*: Object */) => { - if (this.mounted) { - this.setState({ OnboardingContainer }); - } - }; - - render() { - const { OnboardingContainer } = this.state; - - return ( - - {OnboardingContainer != null && } - - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx new file mode 100644 index 00000000000..e1013a3f750 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import Modal from '../../../components/controls/Modal'; +import { translate } from '../../../helpers/l10n'; +import { lazyLoad } from '../../../components/lazyLoad'; + +interface Props { + onFinish: () => void; +} + +const OnboardingContainer = lazyLoad(() => import('./OnboardingContainer')); + +export default function OnboardingModal({ onFinish }: Props) { + return ( + + + + ); +} diff --git a/server/sonar-web/src/main/js/apps/users/routes.ts b/server/sonar-web/src/main/js/apps/users/routes.ts index 691aab5f32f..d33b655e8e3 100644 --- a/server/sonar-web/src/main/js/apps/users/routes.ts +++ b/server/sonar-web/src/main/js/apps/users/routes.ts @@ -17,13 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./UsersAppContainer').then(i => callback(null, { component: i.default })); - } + indexRoute: { component: lazyLoad(() => import('./UsersAppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/web-api/routes.ts b/server/sonar-web/src/main/js/apps/web-api/routes.ts index 7fafd674092..f3bb72447b8 100644 --- a/server/sonar-web/src/main/js/apps/web-api/routes.ts +++ b/server/sonar-web/src/main/js/apps/web-api/routes.ts @@ -17,19 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, RouteComponent, IndexRouteProps } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/WebApiApp').then(i => callback(null, { component: (i as any).default })); - } + indexRoute: { component: lazyLoad(() => import('./components/WebApiApp')) } }, { path: '**', - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/WebApiApp').then(i => callback(null, (i as any).default)); - } + component: lazyLoad(() => import('./components/WebApiApp')) } ]; diff --git a/server/sonar-web/src/main/js/apps/webhooks/routes.ts b/server/sonar-web/src/main/js/apps/webhooks/routes.ts index fbd6fae2b97..75753a6139a 100644 --- a/server/sonar-web/src/main/js/apps/webhooks/routes.ts +++ b/server/sonar-web/src/main/js/apps/webhooks/routes.ts @@ -17,15 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RouterState, RouteComponent } from 'react-router'; +import { lazyLoad } from '../../components/lazyLoad'; const routes = [ { - indexRoute: { - getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { - import('./components/App').then(i => callback(null, i.default)); - } - } + indexRoute: { component: lazyLoad(() => import('./components/App')) } } ]; diff --git a/server/sonar-web/src/main/js/components/lazyLoad.tsx b/server/sonar-web/src/main/js/components/lazyLoad.tsx index 34f6de91d6b..7043b40f440 100644 --- a/server/sonar-web/src/main/js/components/lazyLoad.tsx +++ b/server/sonar-web/src/main/js/components/lazyLoad.tsx @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { translate } from '../helpers/l10n'; +import { get, save } from '../helpers/storage'; type ReactComponent

= React.ComponentClass

| React.StatelessComponent

; @@ -25,9 +27,16 @@ interface Loader

{ (): Promise<{ default: ReactComponent

}>; } +export const LAST_FAILED_CHUNK_STORAGE_KEY = 'sonarqube.last_failed_chunk'; + export function lazyLoad

(loader: Loader

) { + interface ImportError { + request?: string; + } + interface State { Component?: ReactComponent

; + error?: ImportError; } // use `React.Component`, not `React.PureComponent` to always re-render @@ -38,7 +47,7 @@ export function lazyLoad

(loader: Loader

) { componentDidMount() { this.mounted = true; - loader().then(i => this.receiveComponent(i.default), () => {}); + loader().then(i => this.receiveComponent(i.default), this.failToReceiveComponent); } componentWillUnmount() { @@ -47,12 +56,35 @@ export function lazyLoad

(loader: Loader

) { receiveComponent = (Component: ReactComponent

) => { if (this.mounted) { - this.setState({ Component }); + this.setState({ Component, error: undefined }); + } + }; + + failToReceiveComponent = (error?: ImportError) => { + const lastFailedChunk = get(LAST_FAILED_CHUNK_STORAGE_KEY); + if (error && error.request === lastFailedChunk) { + // BOOM! + // this is the second time we try to load the same file + // usually that means the file does not exist on the server + // so we should not try to reload the page to not fall into infinite reloading + // just show the error message + if (this.mounted) { + this.setState({ Component: undefined, error }); + } + } else { + if (error && error.request) { + save(LAST_FAILED_CHUNK_STORAGE_KEY, error.request); + } + window.location.reload(); } }; render() { - const { Component } = this.state; + const { Component, error } = this.state; + + if (error && error.request) { + return

{translate('default_error_message')}
; + } if (!Component) { return null;