* 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<{}>;
components?: Array<{}>;
debtTotal?: number;
facets: Array<{}>;
- issues: Array<{}>;
+ issues: RawIssue[];
paging: {
pageIndex: number;
pageSize: number;
}
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}>
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';
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} />
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"
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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 { Component, CurrentUser } from '../../../app/types';
+import { RawQuery } from '../../../helpers/query';
+
+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 class App extends React.Component<Props> {}
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';
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
- }
};
*/
/*:: props: Props; */
/*:: state: State; */
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
+
constructor(props /*: Props */) {
super(props);
this.state = {
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,
}
this.setState({
- myIssues: areMyIssuesSelected(nextProps.location.query),
+ myIssues: nextProps.myIssues || areMyIssuesSelected(nextProps.location.query),
openIssue,
query: parseQuery(nextProps.location.query)
});
}
};
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),
};
handleFilterChange = (changes /*: {} */) => {
- this.props.router.push({
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...serializeQuery({ ...this.state.query, ...changes }),
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: [] }),
};
handleReset = () => {
- this.props.router.push({
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...DEFAULT_QUERY,
}
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}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import { withRouter } from 'react-router';
-/*:: import type { Dispatch } from 'redux'; */
-import { uniq } from 'lodash';
-import App from './App';
-import throwGlobalError from '../../../app/utils/throwGlobalError';
-import { getCurrentUser, areThereCustomOrganizations } 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'; */
-
-const mapStateToProps = state => ({
- currentUser: getCurrentUser(state)
-});
-
-const fetchIssueOrganizations = issues => dispatch => {
- if (!issues.length) {
- return Promise.resolve();
- }
-
- const organizationKeys = uniq(issues.map(issue => issue.organization));
- return getOrganizations({ organizations: organizationKeys.join() }).then(
- response => dispatch(receiveOrganizations(response.organizations)),
- throwGlobalError
- );
-};
-
-const fetchIssues = (query /*: RawQuery */, requestOrganizations /*: boolean */ = true) => (
- dispatch,
- getState
-) => {
- const organizationsEnabled = areThereCustomOrganizations(getState());
- return searchIssues({ ...query, additionalFields: '_all' })
- .then(response => {
- const parsedIssues = response.issues.map(issue =>
- parseIssueFromResponse(issue, response.components, response.users, response.rules)
- );
- return { ...response, issues: parsedIssues };
- })
- .then(response => {
- return organizationsEnabled && requestOrganizations
- ? dispatch(fetchIssueOrganizations(response.issues)).then(() => response)
- : response;
- })
- .catch(throwGlobalError);
-};
-
-const mapDispatchToProps = { fetchIssues };
-
-export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { uniq } from 'lodash';
+import throwGlobalError from '../../../app/utils/throwGlobalError';
+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 { RawQuery } from '../../../helpers/query';
+import { CurrentUser } from '../../../app/types';
+import { lazyLoad } from '../../../components/lazyLoad';
+
+interface StateProps {
+ currentUser: CurrentUser;
+ onSonarCloud: boolean;
+}
+
+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();
+ }
+
+ const organizationKeys = uniq(issues.map(issue => issue.organization));
+ return getOrganizations({ organizations: organizationKeys.join() }).then(
+ response => dispatch(receiveOrganizations(response.organizations)),
+ throwGlobalError
+ );
+};
+
+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' })
+ .then(response => {
+ const parsedIssues = response.issues.map(issue =>
+ parseIssueFromResponse(issue, response.components, response.users, response.rules)
+ );
+ return { ...response, issues: parsedIssues };
+ })
+ .then(response => {
+ return organizationsEnabled && requestOrganizations
+ ? dispatch(fetchIssueOrganizations(response.issues)).then(() => response)
+ : response;
+ })
+ .catch(throwGlobalError);
+};
+
+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<StateProps, DispatchProps, OwnProps>(mapStateToProps, mapDispatchToProps)(
+ lazyLoad(() => import('./App'))
+);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { RouterState, IndexRouteProps } from 'react-router';
-import { onEnter } from './redirects';
-
-const routes = [
- {
- getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) {
- import('./components/AppContainer').then(i =>
- callback(null, { component: i.default, onEnter })
- );
- }
- }
-];
-
-export default routes;
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 = [
{
{
path: 'issues',
component: OrganizationContainer,
- childRoutes: issuesRoutes
+ childRoutes: [{ indexRoute: { component: Issues } }]
},
{
path: 'members',
[x: string]: any;
}
-interface RawIssue extends IssueBase {
+export interface RawIssue extends IssueBase {
assignee?: string;
author?: string;
comments?: Array<Comment>;
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