diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-11-28 14:00:11 +0100 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-12-11 18:00:33 +0100 |
commit | cd9f8d636f2b057b4c7fb6f0846e5843d44b7956 (patch) | |
tree | c9a766c979a0bbeba63e16ed7744bcac3c7583c7 /server | |
parent | b5825706ab56c92dfae7e12d91af4891e363e03b (diff) | |
download | sonarqube-cd9f8d636f2b057b4c7fb6f0846e5843d44b7956.tar.gz sonarqube-cd9f8d636f2b057b4c7fb6f0846e5843d44b7956.zip |
SONAR-10080 turn Issues to My Issues
Diffstat (limited to 'server')
-rw-r--r-- | server/sonar-web/src/main/js/api/issues.ts | 3 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js | 15 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/app/utils/startReactApp.js | 7 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx | 44 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/issues/components/App.d.ts (renamed from server/sonar-web/src/main/js/apps/issues/routes.ts) | 29 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/issues/components/App.js | 42 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx (renamed from server/sonar-web/src/main/js/apps/issues/components/AppContainer.js) | 54 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/organizations/routes.js | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/issues.ts | 2 |
9 files changed, 145 insertions, 55 deletions
diff --git a/server/sonar-web/src/main/js/api/issues.ts b/server/sonar-web/src/main/js/api/issues.ts index 730292aead3..516fe67ce48 100644 --- a/server/sonar-web/src/main/js/api/issues.ts +++ b/server/sonar-web/src/main/js/api/issues.ts @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { getJSON, post, postJSON, RequestData } from '../helpers/request'; +import { RawIssue } from '../helpers/issues'; export interface IssueResponse { components?: Array<{}>; @@ -30,7 +31,7 @@ interface IssuesResponse { components?: Array<{}>; debtTotal?: number; facets: Array<{}>; - issues: Array<{}>; + issues: RawIssue[]; paging: { pageIndex: number; pageSize: number; diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js index 02b3ab3ce11..27702bf87f7 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js @@ -64,11 +64,24 @@ export default class GlobalNavMenu extends React.PureComponent { } renderIssuesLink() { + const active = this.props.location.pathname === 'issues'; + + if (this.props.sonarCloud) { + return ( + <li> + <Link + to={{ pathname: '/issues', query: { resolved: 'false' } }} + className={active ? 'active' : undefined}> + {translate('my_issues')} + </Link> + </li> + ); + } + const query = this.props.currentUser.isLoggedIn && isMySet() ? { resolved: 'false', myIssues: 'true' } : { resolved: 'false' }; - const active = this.props.location.pathname === 'issues'; return ( <li> <Link to={{ pathname: '/issues', query }} className={active ? 'active' : undefined}> 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 0350b525fd4..3488208f508 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -47,7 +47,8 @@ import componentRoutes from '../../apps/component/routes'; import componentMeasuresRoutes from '../../apps/component-measures/routes'; import customMeasuresRoutes from '../../apps/custom-measures/routes'; import groupsRoutes from '../../apps/groups/routes'; -import issuesRoutes from '../../apps/issues/routes'; +import Issues from '../../apps/issues/components/AppContainer'; +import IssuesPageSelector from '../../apps/issues/IssuesPageSelector'; import marketplaceRoutes from '../../apps/marketplace/routes'; import metricsRoutes from '../../apps/metrics/routes'; import overviewRoutes from '../../apps/overview/routes'; @@ -167,7 +168,7 @@ const startReactApp = () => { path="extension/:pluginKey/:extensionKey" component={GlobalPageExtension} /> - <Route path="issues" childRoutes={issuesRoutes} /> + <Route path="issues" component={IssuesPageSelector} /> <Route path="organizations" childRoutes={organizationsRoutes} /> <Route path="projects" childRoutes={projectsRoutes} /> <Route path="quality_gates" childRoutes={qualityGatesRoutes} /> @@ -187,7 +188,7 @@ const startReactApp = () => { path="project/extension/:pluginKey/:extensionKey" component={ProjectPageExtension} /> - <Route path="project/issues" childRoutes={issuesRoutes} /> + <Route path="project/issues" component={Issues} /> <Route path="project/quality_gate" childRoutes={projectQualityGateRoutes} /> <Route path="project/quality_profiles" diff --git a/server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx b/server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx new file mode 100644 index 00000000000..4ff236be004 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:contact 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 AppContainer from './components/AppContainer'; +import { CurrentUser, isLoggedIn } from '../../app/types'; +import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer'; + +interface StateProps { + currentUser: CurrentUser; + onSonarCloud: boolean; +} + +function IssuesPage({ currentUser, onSonarCloud, ...props }: StateProps) { + const myIssues = (isLoggedIn(currentUser) && onSonarCloud) || undefined; + return <AppContainer myIssues={myIssues} {...props} />; +} + +const stateToProps = (state: any) => { + const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled'); + return { + currentUser: getCurrentUser(state), + onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true') + }; +}; + +export default connect<StateProps>(stateToProps)(IssuesPage); diff --git a/server/sonar-web/src/main/js/apps/issues/routes.ts b/server/sonar-web/src/main/js/apps/issues/components/App.d.ts index e915d90da0a..d80df8ebb32 100644 --- a/server/sonar-web/src/main/js/apps/issues/routes.ts +++ b/server/sonar-web/src/main/js/apps/issues/components/App.d.ts @@ -1,7 +1,7 @@ /* * SonarQube * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com + * mailto:contact 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 @@ -17,17 +17,20 @@ * 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 { onEnter } from './redirects'; +import * as React from 'react'; +import { Component, CurrentUser } from '../../../app/types'; +import { RawQuery } from '../../../helpers/query'; -const routes = [ - { - getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { - import('./components/AppContainer').then(i => - callback(null, { component: i.default, onEnter }) - ); - } - } -]; +interface Props { + branch?: { name: string }; + component?: Component; + currentUser: CurrentUser; + fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<any>; + location: { pathname: string; query: RawQuery }; + myIssues?: boolean; + onBranchesChange: () => void; + onSonarCloud: boolean; + organization?: { key: string }; +} -export default routes; +export default class App extends React.Component<Props> {} diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js index 94b226cb89d..46cb1d23952 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.js +++ b/server/sonar-web/src/main/js/apps/issues/components/App.js @@ -22,6 +22,7 @@ import React from 'react'; import Helmet from 'react-helmet'; import key from 'keymaster'; import { keyBy, without } from 'lodash'; +import PropTypes from 'prop-types'; import PageActions from './PageActions'; import FiltersHeader from './FiltersHeader'; import MyIssuesFilter from './MyIssuesFilter'; @@ -71,12 +72,10 @@ export type Props = { currentUser: CurrentUser, fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<*>, location: { pathname: string, query: RawQuery }, + myIssues?: bool; onBranchesChange: () => void, + onSonarCloud: bool, organization?: { key: string }, - router: { - push: ({ pathname: string, query?: RawQuery }) => void, - replace: ({ pathname: string, query?: RawQuery }) => void - } }; */ @@ -114,6 +113,10 @@ export default class App extends React.PureComponent { /*:: props: Props; */ /*:: state: State; */ + static contextTypes = { + router: PropTypes.object.isRequired + }; + constructor(props /*: Props */) { super(props); this.state = { @@ -123,7 +126,7 @@ export default class App extends React.PureComponent { issues: [], loading: true, locationsNavigator: false, - myIssues: areMyIssuesSelected(props.location.query), + myIssues: props.myIssues || areMyIssuesSelected(props.location.query), openFacets: { severities: true, types: true }, openIssue: null, openPopup: null, @@ -172,7 +175,7 @@ export default class App extends React.PureComponent { } this.setState({ - myIssues: areMyIssuesSelected(nextProps.location.query), + myIssues: nextProps.myIssues || areMyIssuesSelected(nextProps.location.query), openIssue, query: parseQuery(nextProps.location.query) }); @@ -329,15 +332,15 @@ export default class App extends React.PureComponent { } }; if (this.state.openIssue) { - this.props.router.replace(path); + this.context.router.replace(path); } else { - this.props.router.push(path); + this.context.router.push(path); } }; closeIssue = () => { if (this.state.query) { - this.props.router.push({ + this.context.router.push({ pathname: this.props.location.pathname, query: { ...serializeQuery(this.state.query), @@ -575,7 +578,7 @@ export default class App extends React.PureComponent { }; handleFilterChange = (changes /*: {} */) => { - this.props.router.push({ + this.context.router.push({ pathname: this.props.location.pathname, query: { ...serializeQuery({ ...this.state.query, ...changes }), @@ -591,7 +594,7 @@ export default class App extends React.PureComponent { if (!this.props.component) { saveMyIssues(myIssues); } - this.props.router.push({ + this.context.router.push({ pathname: this.props.location.pathname, query: { ...serializeQuery({ ...this.state.query, assigned: true, assignees: [] }), @@ -618,7 +621,7 @@ export default class App extends React.PureComponent { }; handleReset = () => { - this.props.router.push({ + this.context.router.push({ pathname: this.props.location.pathname, query: { ...DEFAULT_QUERY, @@ -754,17 +757,18 @@ export default class App extends React.PureComponent { } renderFacets() { - const { component, currentUser } = this.props; + const { component, currentUser, onSonarCloud } = this.props; const { query } = this.state; return ( <div className="layout-page-filters"> - {currentUser.isLoggedIn && ( - <MyIssuesFilter - myIssues={this.state.myIssues} - onMyIssuesChange={this.handleMyIssuesChange} - /> - )} + {currentUser.isLoggedIn && + !onSonarCloud && ( + <MyIssuesFilter + myIssues={this.state.myIssues} + onMyIssuesChange={this.handleMyIssuesChange} + /> + )} <FiltersHeader displayReset={this.isFiltered()} onReset={this.handleReset} /> <Sidebar component={component} diff --git a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx index d3af96718f6..7747e8aa148 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js +++ b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx @@ -17,25 +17,37 @@ * 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 { connect } from 'react-redux'; -import { withRouter } from 'react-router'; -/*:: import type { Dispatch } from 'redux'; */ +import { Dispatch } from 'redux'; import { uniq } from 'lodash'; -import App from './App'; import throwGlobalError from '../../../app/utils/throwGlobalError'; -import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer'; +import { + getCurrentUser, + areThereCustomOrganizations, + getGlobalSettingValue +} from '../../../store/rootReducer'; import { getOrganizations } from '../../../api/organizations'; import { receiveOrganizations } from '../../../store/organizations/duck'; import { searchIssues } from '../../../api/issues'; import { parseIssueFromResponse } from '../../../helpers/issues'; -/*:: import type { RawQuery } from '../../../helpers/query'; */ +import { RawQuery } from '../../../helpers/query'; +import { CurrentUser } from '../../../app/types'; +import { lazyLoad } from '../../../components/lazyLoad'; -const mapStateToProps = state => ({ - currentUser: getCurrentUser(state) -}); +interface StateProps { + currentUser: CurrentUser; + onSonarCloud: boolean; +} -const fetchIssueOrganizations = issues => dispatch => { +const mapStateToProps = (state: any): StateProps => { + const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled'); + return { + currentUser: getCurrentUser(state), + onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true') + }; +}; + +const fetchIssueOrganizations = (issues: any[]) => (dispatch: Dispatch<any>) => { if (!issues.length) { return Promise.resolve(); } @@ -47,9 +59,10 @@ const fetchIssueOrganizations = issues => dispatch => { ); }; -const fetchIssues = (query /*: RawQuery */, requestOrganizations /*: boolean */ = true) => ( - dispatch, - getState +const fetchIssues = (query: RawQuery, requestOrganizations = true) => ( + // use `Function` to be able to do `dispatch(...).then(...)` + dispatch: Function, + getState: () => any ) => { const organizationsEnabled = areThereCustomOrganizations(getState()); return searchIssues({ ...query, additionalFields: '_all' }) @@ -67,6 +80,17 @@ const fetchIssues = (query /*: RawQuery */, requestOrganizations /*: boolean */ .catch(throwGlobalError); }; -const mapDispatchToProps = { fetchIssues }; +interface DispatchProps { + fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<void>; +} + +// have to type cast this, because of async action +const mapDispatchToProps = { fetchIssues: fetchIssues as any } as DispatchProps; + +interface OwnProps { + myIssues?: boolean; +} -export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App)); +export default connect<StateProps, DispatchProps, OwnProps>(mapStateToProps, mapDispatchToProps)( + lazyLoad(() => import('./App')) +); diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.js b/server/sonar-web/src/main/js/apps/organizations/routes.js index b0010c5e416..d4cbdaa8a9c 100644 --- a/server/sonar-web/src/main/js/apps/organizations/routes.js +++ b/server/sonar-web/src/main/js/apps/organizations/routes.js @@ -32,7 +32,7 @@ import OrganizationProjectsManagement from './components/OrganizationProjectsMan import OrganizationDelete from './components/OrganizationDelete'; import qualityGatesRoutes from '../quality-gates/routes'; import qualityProfilesRoutes from '../quality-profiles/routes'; -import issuesRoutes from '../issues/routes'; +import Issues from '../issues/components/AppContainer'; const routes = [ { @@ -55,7 +55,7 @@ const routes = [ { path: 'issues', component: OrganizationContainer, - childRoutes: issuesRoutes + childRoutes: [{ indexRoute: { component: Issues } }] }, { path: 'members', diff --git a/server/sonar-web/src/main/js/helpers/issues.ts b/server/sonar-web/src/main/js/helpers/issues.ts index 33f8792bab6..c3b6b644c08 100644 --- a/server/sonar-web/src/main/js/helpers/issues.ts +++ b/server/sonar-web/src/main/js/helpers/issues.ts @@ -50,7 +50,7 @@ interface IssueBase { [x: string]: any; } -interface RawIssue extends IssueBase { +export interface RawIssue extends IssueBase { assignee?: string; author?: string; comments?: Array<Comment>; |