aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-06-22 17:22:21 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-07-04 14:15:34 +0200
commitb41d68b7105b3d9704319183dcb7e573e6dd1386 (patch)
tree98a5b66a1cb1435460d49409536f7a884c5a1180
parentfd52ea47ca9353d13fa3f599154393a175669081 (diff)
downloadsonarqube-b41d68b7105b3d9704319183dcb7e573e6dd1386.tar.gz
sonarqube-b41d68b7105b3d9704319183dcb7e573e6dd1386.zip
SONAR-9404 Remember last selected project history graph
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js35
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/utils.js62
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.js21
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/routes.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/utils.js44
-rw-r--r--server/sonar-web/src/main/js/helpers/storage.js67
8 files changed, 142 insertions, 93 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
index 22f52f533e5..58b855b49f0 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
@@ -29,6 +29,7 @@ import { getAllTimeMachineData } from '../../../api/time-machine';
import { getMetrics } from '../../../api/metrics';
import * as api from '../../../api/projectActivity';
import * as actions from '../actions';
+import { getGraph, saveGraph } from '../../../helpers/storage';
import { GRAPHS_METRICS, parseQuery, serializeQuery, serializeUrlQuery } from '../utils';
import type { RawQuery } from '../../../helpers/query';
import type { Analysis, MeasureHistory, Metric, Paging, Query } from '../types';
@@ -36,7 +37,10 @@ import type { Analysis, MeasureHistory, Metric, Paging, Query } from '../types';
type Props = {
location: { pathname: string, query: RawQuery },
project: { configuration?: { showHistory: boolean }, key: string, leakPeriodDate: string },
- router: { push: ({ pathname: string, query?: RawQuery }) => void }
+ router: {
+ push: ({ pathname: string, query?: RawQuery }) => void,
+ replace: ({ pathname: string, query?: RawQuery }) => void
+ }
};
export type State = {
@@ -66,6 +70,13 @@ class ProjectActivityAppContainer extends React.PureComponent {
metrics: [],
query: parseQuery(props.location.query)
};
+
+ if (this.shouldRedirect()) {
+ this.props.router.replace({
+ pathname: props.location.pathname,
+ query: serializeUrlQuery({ ...this.state.query, graph: getGraph() })
+ });
+ }
}
componentDidMount() {
@@ -225,18 +236,32 @@ class ProjectActivityAppContainer extends React.PureComponent {
};
updateQuery = (newQuery: Query) => {
+ const query = serializeUrlQuery({
+ ...this.state.query,
+ ...newQuery
+ });
+ saveGraph(query.graph);
this.props.router.push({
pathname: this.props.location.pathname,
query: {
- ...serializeUrlQuery({
- ...this.state.query,
- ...newQuery
- }),
+ ...query,
id: this.props.project.key
}
});
};
+ shouldRedirect = () => {
+ const locationQuery = this.props.location.query;
+ if (locationQuery) {
+ const filtered = Object.keys(locationQuery).some(
+ key => key !== 'id' && locationQuery[key] !== ''
+ );
+
+ // if there is no filter, but there are saved preferences in the localStorage
+ return !filtered && getGraph();
+ }
+ };
+
render() {
return (
<ProjectActivityApp
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.js b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
index ae651fd891f..616633b0a25 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/utils.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
@@ -38,12 +38,42 @@ export const GRAPHS_METRICS = {
remediation: ['reliability_remediation_effort', 'sqale_index', 'security_remediation_effort']
};
+export const activityQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
+ prevQuery.category !== nextQuery.category ||
+ prevQuery.from !== nextQuery.from ||
+ prevQuery.to !== nextQuery.to;
+
+export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
+ prevQuery.from !== nextQuery.from || prevQuery.to !== nextQuery.to;
+
+export const historyQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
+ prevQuery.graph !== nextQuery.graph;
+
+export const generateCoveredLinesMetric = (
+ uncoveredLines: MeasureHistory,
+ measuresHistory: Array<MeasureHistory>,
+ style: string
+) => {
+ const linesToCover = measuresHistory.find(measure => measure.metric === 'lines_to_cover');
+ return {
+ data: linesToCover
+ ? uncoveredLines.history.map((analysis, idx) => ({
+ x: analysis.date,
+ y: Number(linesToCover.history[idx].value) - Number(analysis.value)
+ }))
+ : [],
+ name: 'covered_lines',
+ style,
+ translatedName: translate('project_activity.custom_metric.covered_lines')
+ };
+};
+
const parseGraph = (value?: string): string => {
const graph = parseAsString(value);
return GRAPH_TYPES.includes(graph) ? graph : 'overview';
};
-const serializeGraph = (value: string): string => (value === 'overview' ? '' : value);
+const serializeGraph = (value: string): ?string => (value === 'overview' ? undefined : value);
export const parseQuery = (urlQuery: RawQuery): Query => ({
category: parseAsString(urlQuery['category']),
@@ -68,33 +98,3 @@ export const serializeUrlQuery = (query: Query): RawQuery => {
to: serializeDate(query.to)
});
};
-
-export const activityQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
- prevQuery.category !== nextQuery.category ||
- prevQuery.from !== nextQuery.from ||
- prevQuery.to !== nextQuery.to;
-
-export const historyQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
- prevQuery.graph !== nextQuery.graph;
-
-export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
- prevQuery.from !== nextQuery.from || prevQuery.to !== nextQuery.to;
-
-export const generateCoveredLinesMetric = (
- uncoveredLines: MeasureHistory,
- measuresHistory: Array<MeasureHistory>,
- style: string
-) => {
- const linesToCover = measuresHistory.find(measure => measure.metric === 'lines_to_cover');
- return {
- data: linesToCover
- ? uncoveredLines.history.map((analysis, idx) => ({
- x: analysis.date,
- y: Number(linesToCover.history[idx].value) - Number(analysis.value)
- }))
- : [],
- name: 'covered_lines',
- style,
- translatedName: translate('project_activity.custom_metric.covered_lines')
- };
-};
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
index 13078f320a5..cc8ebb4af9a 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
@@ -28,6 +28,7 @@ import VisualizationsContainer from '../visualizations/VisualizationsContainer';
import { parseUrlQuery } from '../store/utils';
import { translate } from '../../../helpers/l10n';
import * as utils from '../utils';
+import * as storage from '../../../helpers/storage';
import type { RawQuery } from '../../../helpers/query';
import '../styles.css';
@@ -78,14 +79,14 @@ export default class AllProjects extends React.PureComponent {
getSavedOptions = () => {
const options = {};
- if (utils.getSort()) {
- options.sort = utils.getSort();
+ if (storage.getSort()) {
+ options.sort = storage.getSort();
}
- if (utils.getView()) {
- options.view = utils.getView();
+ if (storage.getView()) {
+ options.view = storage.getView();
}
- if (utils.getVisualization()) {
- options.visualization = utils.getVisualization();
+ if (storage.getVisualization()) {
+ options.visualization = storage.getVisualization();
}
return options;
};
@@ -108,15 +109,15 @@ export default class AllProjects extends React.PureComponent {
this.updateLocationQuery(query);
}
- utils.saveSort(query.sort);
- utils.saveView(query.view);
- utils.saveVisualization(visualization);
+ storage.saveSort(query.sort);
+ storage.saveView(query.view);
+ storage.saveVisualization(visualization);
};
handleSortChange = (sort: string, desc: boolean) => {
const asString = (desc ? '-' : '') + sort;
this.updateLocationQuery({ sort: asString });
- utils.saveSort(asString);
+ storage.saveSort(asString);
};
handleQueryChange(initialMount: boolean) {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
index bab378af29e..d306afcd9db 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
@@ -23,7 +23,7 @@ import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import AllProjectsContainer from './AllProjectsContainer';
import { getCurrentUser } from '../../../store/rootReducer';
-import { isFavoriteSet, isAllSet } from '../utils';
+import { isFavoriteSet, isAllSet } from '../../../helpers/storage';
import { searchProjects } from '../../../api/components';
import type { RawQuery } from '../../../helpers/query';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
index 77cd3f97cc8..d3eb8d65b83 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
@@ -21,7 +21,7 @@
import React from 'react';
import { IndexLink, Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
-import { saveAll, saveFavorite } from '../utils';
+import { saveAll, saveFavorite } from '../../../helpers/storage';
import type { RawQuery } from '../../../helpers/query';
type Props = {
diff --git a/server/sonar-web/src/main/js/apps/projects/routes.js b/server/sonar-web/src/main/js/apps/projects/routes.js
index e2307b12175..8c67972c076 100644
--- a/server/sonar-web/src/main/js/apps/projects/routes.js
+++ b/server/sonar-web/src/main/js/apps/projects/routes.js
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { saveAll } from './utils';
+import { saveAll } from '../../helpers/storage';
const routes = [
{
diff --git a/server/sonar-web/src/main/js/apps/projects/utils.js b/server/sonar-web/src/main/js/apps/projects/utils.js
index 68b26f1b41c..02f2ae59d59 100644
--- a/server/sonar-web/src/main/js/apps/projects/utils.js
+++ b/server/sonar-web/src/main/js/apps/projects/utils.js
@@ -20,50 +20,6 @@
// @flow
import { translate } from '../../helpers/l10n';
-const DEFAULT_FILTER = 'sonarqube.projects.default';
-const FAVORITE = 'favorite';
-const ALL = 'all';
-
-const VIEW = 'sonarqube.projects.view';
-const VISUALIZATION = 'sonarqube.projects.visualization';
-const SORT = 'sonarqube.projects.sort';
-
-export const isFavoriteSet = (): boolean => {
- const setting = window.localStorage.getItem(DEFAULT_FILTER);
- return setting === FAVORITE;
-};
-
-export const isAllSet = (): boolean => {
- const setting = window.localStorage.getItem(DEFAULT_FILTER);
- return setting === ALL;
-};
-
-const save = (key: string, value: ?string) => {
- try {
- if (value) {
- window.localStorage.setItem(key, value);
- } else {
- window.localStorage.removeItem(key);
- }
- } catch (e) {
- // usually that means the storage is full
- // just do nothing in this case
- }
-};
-
-export const saveAll = () => save(DEFAULT_FILTER, ALL);
-
-export const saveFavorite = () => save(DEFAULT_FILTER, FAVORITE);
-
-export const saveView = (view: ?string) => save(VIEW, view);
-export const getView = () => window.localStorage.getItem(VIEW);
-
-export const saveVisualization = (visualization: ?string) => save(VISUALIZATION, visualization);
-export const getVisualization = () => window.localStorage.getItem(VISUALIZATION);
-
-export const saveSort = (sort: ?string) => save(SORT, sort);
-export const getSort = () => window.localStorage.getItem(SORT);
-
export const SORTING_METRICS = [
{ value: 'name' },
{ value: 'analysis_date' },
diff --git a/server/sonar-web/src/main/js/helpers/storage.js b/server/sonar-web/src/main/js/helpers/storage.js
new file mode 100644
index 00000000000..ac6fa964f5f
--- /dev/null
+++ b/server/sonar-web/src/main/js/helpers/storage.js
@@ -0,0 +1,67 @@
+/*
+ * 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
+const PROJECTS_DEFAULT_FILTER = 'sonarqube.projects.default';
+const PROJECTS_FAVORITE = 'favorite';
+const PROJECTS_ALL = 'all';
+
+const PROJECTS_VIEW = 'sonarqube.projects.view';
+const PROJECTS_VISUALIZATION = 'sonarqube.projects.visualization';
+const PROJECTS_SORT = 'sonarqube.projects.sort';
+
+const PROJECT_ACTIVITY_GRAPH = 'sonarqube.project_activity.graph';
+
+const save = (key: string, value: ?string) => {
+ try {
+ if (value) {
+ window.localStorage.setItem(key, value);
+ } else {
+ window.localStorage.removeItem(key);
+ }
+ } catch (e) {
+ // usually that means the storage is full
+ // just do nothing in this case
+ }
+};
+
+export const saveFavorite = () => save(PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE);
+export const isFavoriteSet = (): boolean => {
+ const setting = window.localStorage.getItem(PROJECTS_DEFAULT_FILTER);
+ return setting === PROJECTS_FAVORITE;
+};
+
+export const saveAll = () => save(PROJECTS_DEFAULT_FILTER, PROJECTS_ALL);
+export const isAllSet = (): boolean => {
+ const setting = window.localStorage.getItem(PROJECTS_DEFAULT_FILTER);
+ return setting === PROJECTS_ALL;
+};
+
+export const saveView = (view: ?string) => save(PROJECTS_VIEW, view);
+export const getView = () => window.localStorage.getItem(PROJECTS_VIEW);
+
+export const saveVisualization = (visualization: ?string) =>
+ save(PROJECTS_VISUALIZATION, visualization);
+export const getVisualization = () => window.localStorage.getItem(PROJECTS_VISUALIZATION);
+
+export const saveSort = (sort: ?string) => save(PROJECTS_SORT, sort);
+export const getSort = () => window.localStorage.getItem(PROJECTS_SORT);
+
+export const saveGraph = (graph: ?string) => save(PROJECT_ACTIVITY_GRAPH, graph);
+export const getGraph = () => window.localStorage.getItem(PROJECT_ACTIVITY_GRAPH);