From cd9f8d636f2b057b4c7fb6f0846e5843d44b7956 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 28 Nov 2017 14:00:11 +0100 Subject: [PATCH] SONAR-10080 turn Issues to My Issues --- server/sonar-web/src/main/js/api/issues.ts | 3 +- .../components/nav/global/GlobalNavMenu.js | 15 +++++- .../src/main/js/app/utils/startReactApp.js | 7 +-- .../js/apps/issues/IssuesPageSelector.tsx | 44 +++++++++++++++ .../issues/{routes.ts => components/App.d.ts} | 29 +++++----- .../src/main/js/apps/issues/components/App.js | 42 ++++++++------- .../{AppContainer.js => AppContainer.tsx} | 54 +++++++++++++------ .../src/main/js/apps/organizations/routes.js | 4 +- .../sonar-web/src/main/js/helpers/issues.ts | 2 +- .../resources/org/sonar/l10n/core.properties | 1 + 10 files changed, 146 insertions(+), 55 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx rename server/sonar-web/src/main/js/apps/issues/{routes.ts => components/App.d.ts} (57%) rename server/sonar-web/src/main/js/apps/issues/components/{AppContainer.js => AppContainer.tsx} (61%) 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 ( +
  • + + {translate('my_issues')} + +
  • + ); + } + const query = this.props.currentUser.isLoggedIn && isMySet() ? { resolved: 'false', myIssues: 'true' } : { resolved: 'false' }; - const active = this.props.location.pathname === 'issues'; return (
  • 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} /> - + @@ -187,7 +188,7 @@ const startReactApp = () => { path="project/extension/:pluginKey/:extensionKey" component={ProjectPageExtension} /> - + ; +} + +const stateToProps = (state: any) => { + const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled'); + return { + currentUser: getCurrentUser(state), + onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true') + }; +}; + +export default connect(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 similarity index 57% rename from server/sonar-web/src/main/js/apps/issues/routes.ts rename to 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; + location: { pathname: string; query: RawQuery }; + myIssues?: boolean; + onBranchesChange: () => void; + onSonarCloud: boolean; + organization?: { key: string }; +} -export default routes; +export default class App extends React.Component {} 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 (
    - {currentUser.isLoggedIn && ( - - )} + {currentUser.isLoggedIn && + !onSonarCloud && ( + + )} ({ - 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) => { 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; +} + +// 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(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; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 671127dca10..5618a879e48 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -95,6 +95,7 @@ minor=Minor more=More more_x={0} more more_actions=More Actions +my_issues=My Issues my_favorite=My Favorite my_favorites=My Favorites my_projects=My Projects -- 2.39.5