* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
-const STORAGE_KEY = 'sonar_recent_history';
+import { get, remove, save } from '../../helpers/storage';
+
+const RECENT_HISTORY = 'sonar_recent_history';
const HISTORY_LIMIT = 10;
/*::
export default class RecentHistory {
static get() /*: History */ {
- if (!window.localStorage) {
- return [];
- }
- let history = window.localStorage.getItem(STORAGE_KEY);
+ const history = get(RECENT_HISTORY);
if (history == null) {
- history = [];
+ return [];
} else {
try {
- history = JSON.parse(history);
+ return JSON.parse(history);
} catch (e) {
- RecentHistory.clear();
- history = [];
+ remove(RECENT_HISTORY);
+ return [];
}
}
- return history;
}
static set(newHistory /*: History */) /*: void */ {
- if (window.localStorage) {
- window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newHistory));
- }
+ save(RECENT_HISTORY, JSON.stringify(newHistory));
}
static clear() /*: void */ {
- if (window.localStorage) {
- window.localStorage.removeItem(STORAGE_KEY);
- }
+ remove(RECENT_HISTORY);
}
static add(
import { searchUsers } from '../../api/users';
import { Issue } from '../../app/types';
import { formatMeasure } from '../../helpers/measures';
+import { get, save } from '../../helpers/storage';
import {
queriesEqual,
cleanQuery,
// allow sorting by CREATION_DATE only
const parseAsSort = (sort: string) => (sort === 'CREATION_DATE' ? 'CREATION_DATE' : '');
+const ISSUES_DEFAULT = 'sonarqube.issues.default';
export function parseQuery(query: RawQuery): Query {
return {
);
};
-const LOCALSTORAGE_KEY = 'sonarqube.issues.default';
const LOCALSTORAGE_MY = 'my';
const LOCALSTORAGE_ALL = 'all';
export const isMySet = () => {
- const setting = window.localStorage.getItem(LOCALSTORAGE_KEY);
- return setting === LOCALSTORAGE_MY;
-};
-
-const save = (value: string) => {
- try {
- window.localStorage.setItem(LOCALSTORAGE_KEY, value);
- } catch (e) {
- // usually that means the storage is full
- // just do nothing in this case
- }
+ return get(ISSUES_DEFAULT) === LOCALSTORAGE_MY;
};
export const saveMyIssues = (myIssues: boolean) =>
- save(myIssues ? LOCALSTORAGE_MY : LOCALSTORAGE_ALL);
+ save(ISSUES_DEFAULT, myIssues ? LOCALSTORAGE_MY : LOCALSTORAGE_ALL);
export function getLocations(
{ flows, secondaryLocations }: Pick<Issue, 'flows' | 'secondaryLocations'>,
import { parseDate } from '../../../helpers/dates';
import { enhanceMeasuresWithMetrics, MeasureEnhanced } from '../../../helpers/measures';
import { getLeakPeriod, Period } from '../../../helpers/periods';
-import { getCustomGraph, getGraph } from '../../../helpers/storage';
+import { get } from '../../../helpers/storage';
import { METRICS, HISTORY_METRICS_LIST } from '../utils';
-import { DEFAULT_GRAPH, getDisplayedHistoryMetrics } from '../../projectActivity/utils';
+import {
+ DEFAULT_GRAPH,
+ getDisplayedHistoryMetrics,
+ PROJECT_ACTIVITY_GRAPH,
+ PROJECT_ACTIVITY_GRAPH_CUSTOM
+} from '../../projectActivity/utils';
import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer';
loadHistory = () => {
const { branchLike, component } = this.props;
- let graphMetrics = getDisplayedHistoryMetrics(getGraph(), getCustomGraph());
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ let graphMetrics = getDisplayedHistoryMetrics(
+ get(PROJECT_ACTIVITY_GRAPH) || 'issues',
+ customGraphs ? customGraphs.split(',') : []
+ );
if (!graphMetrics || graphMetrics.length <= 0) {
graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []);
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { getDisplayedHistoryMetrics, DEFAULT_GRAPH } from '../../projectActivity/utils';
+import {
+ getDisplayedHistoryMetrics,
+ DEFAULT_GRAPH,
+ PROJECT_ACTIVITY_GRAPH,
+ PROJECT_ACTIVITY_GRAPH_CUSTOM
+} from '../../projectActivity/utils';
import PreviewGraph from '../../../components/preview-graph/PreviewGraph';
import { getAllTimeMachineData, History } from '../../../api/time-machine';
import { Metric } from '../../../app/types';
import { parseDate } from '../../../helpers/dates';
import { translate } from '../../../helpers/l10n';
-import { getCustomGraph, getGraph } from '../../../helpers/storage';
+import { get } from '../../../helpers/storage';
const AnyPreviewGraph = PreviewGraph as any;
fetchHistory = () => {
const { component } = this.props;
- let graphMetrics: string[] = getDisplayedHistoryMetrics(getGraph(), getCustomGraph());
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ let graphMetrics = getDisplayedHistoryMetrics(
+ get(PROJECT_ACTIVITY_GRAPH) || 'issues',
+ customGraphs ? customGraphs.split(',') : []
+ );
if (!graphMetrics || graphMetrics.length <= 0) {
graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []);
}
*/
/* eslint-disable import/first, import/order */
jest.mock('../../../../helpers/storage', () => ({
- getCustomGraph: () => ['coverage'],
- getGraph: () => 'custom'
+ get: (key: string) => (key === 'sonarqube.project_activity.graph.custom' ? 'coverage' : 'custom')
}));
jest.mock('../../../../api/time-machine', () => ({
import * as actions from '../actions';
import { getBranchLikeQuery } from '../../../helpers/branches';
import { parseDate } from '../../../helpers/dates';
-import { getCustomGraph, getGraph } from '../../../helpers/storage';
+import { get } from '../../../helpers/storage';
import {
customMetricsChanged,
DEFAULT_GRAPH,
getHistoryMetrics,
isCustomGraph,
parseQuery,
+ PROJECT_ACTIVITY_GRAPH,
+ PROJECT_ACTIVITY_GRAPH_CUSTOM,
serializeQuery,
serializeUrlQuery
} from '../utils';
componentDidMount() {
this.mounted = true;
if (this.shouldRedirect()) {
- const newQuery = { ...this.state.query, graph: getGraph() };
+ const newQuery = { ...this.state.query, graph: get(PROJECT_ACTIVITY_GRAPH) || 'issues' };
if (isCustomGraph(newQuery.graph)) {
- newQuery.customMetrics = getCustomGraph();
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ newQuery.customMetrics = customGraphs ? customGraphs.split(',') : [];
}
this.context.router.replace({
pathname: this.props.location.pathname,
key => key !== 'id' && locationQuery[key] !== ''
);
- const graph = getGraph();
- const emptyCustomGraph = isCustomGraph(graph) && getCustomGraph().length <= 0;
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ const graph = get(PROJECT_ACTIVITY_GRAPH) || 'issues';
+ const emptyCustomGraph =
+ isCustomGraph(graph) && customGraphs && customGraphs.split(',').length <= 0;
// if there is no filter, but there are saved preferences in the localStorage
// also don't redirect to custom if there is no metrics selected for it
deleteEvent={this.deleteEvent}
graphLoading={!this.state.initialized || this.state.graphLoading}
initializing={!this.state.initialized}
- metrics={this.state.metrics}
measuresHistory={this.state.measuresHistory}
+ metrics={this.state.metrics}
project={this.props.component}
query={this.state.query}
updateQuery={this.updateQuery}
import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader';
import GraphsZoom from './GraphsZoom';
import GraphsHistory from './GraphsHistory';
-import { getCustomGraph, saveCustomGraph, saveGraph } from '../../../helpers/storage';
+import { get, save } from '../../../helpers/storage';
import {
datesQueryChanged,
generateSeries,
getSeriesMetricType,
historyQueryChanged,
isCustomGraph,
+ PROJECT_ACTIVITY_GRAPH,
+ PROJECT_ACTIVITY_GRAPH_CUSTOM,
splitSeriesInGraphs
} from '../utils';
/*:: import type { RawQuery } from '../../../helpers/query'; */
addCustomMetric = (metric /*: string */) => {
const customMetrics = [...this.props.query.customMetrics, metric];
- saveCustomGraph(customMetrics);
+ save(PROJECT_ACTIVITY_GRAPH_CUSTOM, customMetrics.join(','));
this.props.updateQuery({ customMetrics });
};
removeCustomMetric = (removedMetric /*: string */) => {
const customMetrics = this.props.query.customMetrics.filter(metric => metric !== removedMetric);
- saveCustomGraph(customMetrics);
+ save(PROJECT_ACTIVITY_GRAPH_CUSTOM, customMetrics.join(','));
this.props.updateQuery({ customMetrics });
};
updateGraph = (graph /*: string */) => {
- saveGraph(graph);
+ save(PROJECT_ACTIVITY_GRAPH, graph);
if (isCustomGraph(graph) && this.props.query.customMetrics.length <= 0) {
- this.props.updateQuery({ graph, customMetrics: getCustomGraph() });
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ this.props.updateQuery({ graph, customMetrics: customGraphs ? customGraphs.split(',') : [] });
} else {
this.props.updateQuery({ graph, customMetrics: [] });
}
analyses={this.props.analyses}
eventFilter={query.category}
graph={query.graph}
- graphs={this.state.graphs}
graphEndDate={graphEndDate}
graphStartDate={graphStartDate}
+ graphs={this.state.graphs}
leakPeriodDate={leakPeriodDate}
loading={loading}
measuresHistory={this.props.measuresHistory}
duplications: GRAPHS_METRICS_DISPLAYED['duplications'].concat(['duplicated_lines_density'])
};
+export const PROJECT_ACTIVITY_GRAPH = 'sonarqube.project_activity.graph';
+export const PROJECT_ACTIVITY_GRAPH_CUSTOM = 'sonarqube.project_activity.graph.custom';
+
export const datesQueryChanged = (prevQuery /*: Query */, nextQuery /*: Query */) =>
!isEqual(prevQuery.from, nextQuery.from) || !isEqual(prevQuery.to, nextQuery.to);
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import ListFooter from '../../../components/controls/ListFooter';
import { translate } from '../../../helpers/l10n';
-import * as storage from '../../../helpers/storage';
+import { get, save } from '../../../helpers/storage';
import { RawQuery } from '../../../helpers/query';
import '../styles.css';
import { Project, Facets } from '../types';
total?: number;
}
+const PROJECTS_SORT = 'sonarqube.projects.sort';
+const PROJECTS_VIEW = 'sonarqube.projects.view';
+const PROJECTS_VISUALIZATION = 'sonarqube.projects.visualization';
+
export default class AllProjects extends React.PureComponent<Props, State> {
mounted = false;
view?: string;
visualization?: string;
} = {};
- if (storage.getSort(storageOptionsSuffix)) {
- options.sort = storage.getSort(storageOptionsSuffix) || undefined;
+ if (get(PROJECTS_SORT, storageOptionsSuffix)) {
+ options.sort = get(PROJECTS_SORT, storageOptionsSuffix) || undefined;
}
- if (storage.getView(storageOptionsSuffix)) {
- options.view = storage.getView(storageOptionsSuffix) || undefined;
+ if (get(PROJECTS_VIEW, storageOptionsSuffix)) {
+ options.view = get(PROJECTS_VIEW, storageOptionsSuffix) || undefined;
}
- if (storage.getVisualization(storageOptionsSuffix)) {
- options.visualization = storage.getVisualization(storageOptionsSuffix) || undefined;
+ if (get(PROJECTS_VISUALIZATION, storageOptionsSuffix)) {
+ options.visualization = get(PROJECTS_VISUALIZATION, storageOptionsSuffix) || undefined;
}
return options;
};
this.updateLocationQuery(query);
}
- storage.saveSort(query.sort, storageOptionsSuffix);
- storage.saveView(query.view, storageOptionsSuffix);
- storage.saveVisualization(visualization, storageOptionsSuffix);
+ save(PROJECTS_SORT, query.sort, storageOptionsSuffix);
+ save(PROJECTS_VIEW, query.view, storageOptionsSuffix);
+ save(PROJECTS_VISUALIZATION, visualization, storageOptionsSuffix);
};
handleSortChange = (sort: string, desc: boolean) => {
const asString = (desc ? '-' : '') + sort;
this.updateLocationQuery({ sort: asString });
- storage.saveSort(asString, this.props.storageOptionsSuffix);
+ save(PROJECTS_SORT, asString, this.props.storageOptionsSuffix);
};
handleQueryChange(initialMount: boolean) {
import * as React from 'react';
import * as PropTypes from 'prop-types';
import AllProjectsContainer from './AllProjectsContainer';
-import { isFavoriteSet, isAllSet } from '../../../helpers/storage';
+import { PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE, PROJECTS_ALL } from '../utils';
+import { get } from '../../../helpers/storage';
import { searchProjects } from '../../../api/components';
import { CurrentUser, isLoggedIn } from '../../../app/types';
}
}
+ isFavoriteSet = (): boolean => {
+ const setting = get(PROJECTS_DEFAULT_FILTER);
+ return setting === PROJECTS_FAVORITE;
+ };
+
+ isAllSet = (): boolean => {
+ const setting = get(PROJECTS_DEFAULT_FILTER);
+ return setting === PROJECTS_ALL;
+ };
+
defineIfShouldBeRedirected() {
if (Object.keys(this.props.location.query).length > 0) {
// show ALL projects when there are some filters
} else {
this.setState({ shouldBeRedirected: false, shouldForceSorting: undefined });
}
- } else if (isFavoriteSet()) {
+ } else if (this.isFavoriteSet()) {
// show FAVORITE projects if "favorite" setting is explicitly set
this.setState({ shouldBeRedirected: true, shouldForceSorting: undefined });
- } else if (isAllSet()) {
+ } else if (this.isAllSet()) {
// show ALL projects if "all" setting is explicitly set
this.setState({ shouldBeRedirected: false, shouldForceSorting: undefined });
} else {
import { IndexLink, Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
import { CurrentUser, isLoggedIn } from '../../../app/types';
-import { saveAll, saveFavorite } from '../../../helpers/storage';
+import { save } from '../../../helpers/storage';
import { RawQuery } from '../../../helpers/query';
+import { PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE, PROJECTS_ALL } from '../utils';
interface Props {
currentUser: CurrentUser;
export default class FavoriteFilter extends React.PureComponent<Props> {
handleSaveFavorite = () => {
if (!this.props.organization) {
- saveFavorite();
+ save(PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE);
}
};
handleSaveAll = () => {
if (!this.props.organization) {
- saveAll();
+ save(PROJECTS_DEFAULT_FILTER, PROJECTS_ALL);
}
};
<header className="page-header text-center">
<div className="button-group">
<Link
- id="favorite-projects"
- to={{ pathname: pathnameForFavorite, query: this.props.query }}
- className="button"
activeClassName="button-active"
- onClick={this.handleSaveFavorite}>
+ className="button"
+ id="favorite-projects"
+ onClick={this.handleSaveFavorite}
+ to={{ pathname: pathnameForFavorite, query: this.props.query }}>
{translate('my_favorites')}
</Link>
<IndexLink
- id="all-projects"
- to={{ pathname: pathnameForAll, query: this.props.query }}
- className="button"
activeClassName="button-active"
- onClick={this.handleSaveAll}>
+ className="button"
+ id="all-projects"
+ onClick={this.handleSaveAll}
+ to={{ pathname: pathnameForAll, query: this.props.query }}>
{translate('all')}
</IndexLink>
</div>
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import AllProjects, { Props } from '../AllProjects';
-import { getView, saveSort, saveView, saveVisualization } from '../../../../helpers/storage';
+import { get, save } from '../../../../helpers/storage';
jest.mock('../ProjectsList', () => ({
// eslint-disable-next-line
});
jest.mock('../../../../helpers/storage', () => ({
- getSort: () => null,
- getView: jest.fn(() => null),
- getVisualization: () => null,
- saveSort: jest.fn(),
- saveView: jest.fn(),
- saveVisualization: jest.fn()
+ get: jest.fn(() => null),
+ save: jest.fn()
}));
const fetchProjects = require('../../utils').fetchProjects as jest.Mock<any>;
beforeEach(() => {
- (getView as jest.Mock<any>).mockImplementation(() => null);
- (saveSort as jest.Mock<any>).mockClear();
- (saveView as jest.Mock<any>).mockClear();
- (saveVisualization as jest.Mock<any>).mockClear();
+ (get as jest.Mock<any>).mockImplementation(() => null);
+ (save as jest.Mock<any>).mockClear();
fetchProjects.mockClear();
});
});
it('redirects to the saved search', () => {
- (getView as jest.Mock<any>).mockImplementation(() => 'leak');
+ (get as jest.Mock<any>).mockImplementation(
+ (key: string) => (key === 'sonarqube.projects.view' ? 'leak' : null)
+ );
const replace = jest.fn();
mountRender({}, jest.fn(), replace);
expect(replace).lastCalledWith({ pathname: '/projects', query: { view: 'leak' } });
const wrapper = shallowRender({}, push);
wrapper.find('PageHeader').prop<Function>('onSortChange')('size', false);
expect(push).lastCalledWith({ pathname: '/projects', query: { sort: 'size' } });
- expect(saveSort).lastCalledWith('size', undefined);
+ expect(save).lastCalledWith('sonarqube.projects.sort', 'size', undefined);
});
it('changes perspective to leak', () => {
pathname: '/projects',
query: { view: 'leak', visualization: undefined }
});
- expect(saveSort).lastCalledWith(undefined, undefined);
- expect(saveView).lastCalledWith('leak', undefined);
- expect(saveVisualization).lastCalledWith(undefined, undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.sort', undefined, undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.view', 'leak', undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', undefined, undefined);
});
it('updates sorting when changing perspective from leak', () => {
pathname: '/projects',
query: { sort: 'coverage', view: undefined, visualization: undefined }
});
- expect(saveSort).lastCalledWith('coverage', undefined);
- expect(saveView).lastCalledWith(undefined, undefined);
- expect(saveVisualization).lastCalledWith(undefined, undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.sort', 'coverage', undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.view', undefined, undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', undefined, undefined);
});
it('changes perspective to risk visualization', () => {
pathname: '/projects',
query: { view: 'visualizations', visualization: 'risk' }
});
- expect(saveSort).lastCalledWith(undefined, undefined);
- expect(saveView).lastCalledWith('visualizations', undefined);
- expect(saveVisualization).lastCalledWith('risk', undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.sort', undefined, undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.view', 'visualizations', undefined);
+ expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', 'risk', undefined);
});
function mountRender(props: any = {}, push: Function = jest.fn(), replace: Function = jest.fn()) {
}));
jest.mock('../../../../helpers/storage', () => ({
- isFavoriteSet: jest.fn(),
- isAllSet: jest.fn()
+ get: jest.fn()
}));
jest.mock('../../../../api/components', () => ({
import { CurrentUser } from '../../../../app/types';
import { doAsync } from '../../../../helpers/testUtils';
-const isFavoriteSet = require('../../../../helpers/storage').isFavoriteSet as jest.Mock<any>;
-const isAllSet = require('../../../../helpers/storage').isAllSet as jest.Mock<any>;
+const get = require('../../../../helpers/storage').get as jest.Mock<any>;
const searchProjects = require('../../../../api/components').searchProjects as jest.Mock<any>;
beforeEach(() => {
- isFavoriteSet.mockImplementation(() => false).mockClear();
- isAllSet.mockImplementation(() => false).mockClear();
+ get.mockImplementation(() => '').mockClear();
});
it('shows all projects with existing filter', () => {
});
it('shows favorite projects', () => {
- isFavoriteSet.mockImplementation(() => true);
+ get.mockImplementation(() => 'favorite');
const replace = jest.fn();
mountRender(undefined, undefined, replace);
expect(replace).lastCalledWith({ pathname: '/projects/favorite', query: {} });
});
it('shows all projects', () => {
- isAllSet.mockImplementation(() => true);
+ get.mockImplementation(() => 'all');
const replace = jest.fn();
mountRender(undefined, undefined, replace);
expect(replace).not.toBeCalled();
*/
/* eslint-disable import/first */
jest.mock('../../../../helpers/storage', () => ({
- saveAll: jest.fn(),
- saveFavorite: jest.fn()
+ save: jest.fn()
}));
import * as React from 'react';
import { shallow } from 'enzyme';
import FavoriteFilter from '../FavoriteFilter';
-import { saveAll, saveFavorite } from '../../../../helpers/storage';
+import { save } from '../../../../helpers/storage';
import { click } from '../../../../helpers/testUtils';
const currentUser = { isLoggedIn: true };
const query = { size: 1 };
beforeEach(() => {
- (saveAll as jest.Mock<any>).mockClear();
- (saveFavorite as jest.Mock<any>).mockClear();
+ (save as jest.Mock<any>).mockClear();
});
it('renders for logged in user', () => {
it('saves last selection', () => {
const wrapper = shallow(<FavoriteFilter currentUser={currentUser} query={query} />);
click(wrapper.find('#favorite-projects'));
- expect(saveFavorite).toBeCalled();
+ expect(save).toBeCalledWith('sonarqube.projects.default', 'favorite');
click(wrapper.find('#all-projects'));
- expect(saveAll).toBeCalled();
+ expect(save).toBeCalledWith('sonarqube.projects.default', 'all');
});
it('handles organization', () => {
<FavoriteFilter currentUser={currentUser} organization={{ key: 'org' }} query={query} />
);
click(wrapper.find('#favorite-projects'));
- expect(saveFavorite).not.toBeCalled();
+ expect(save).not.toBeCalled();
click(wrapper.find('#all-projects'));
- expect(saveAll).not.toBeCalled();
+ expect(save).not.toBeCalled();
});
it('does not render for anonymous', () => {
import { RouterState, RedirectFunction } from 'react-router';
import DefaultPageSelectorContainer from './components/DefaultPageSelectorContainer';
import FavoriteProjectsContainer from './components/FavoriteProjectsContainer';
-import { saveAll } from '../../helpers/storage';
+import { PROJECTS_DEFAULT_FILTER, PROJECTS_ALL } from './utils';
+import { save } from '../../helpers/storage';
const routes = [
{ indexRoute: { component: DefaultPageSelectorContainer } },
{
path: 'all',
onEnter(_: RouterState, replace: RedirectFunction) {
- saveAll();
+ save(PROJECTS_DEFAULT_FILTER, PROJECTS_ALL);
replace('/projects');
}
},
value: string;
}
+export const PROJECTS_DEFAULT_FILTER = 'sonarqube.projects.default';
+export const PROJECTS_FAVORITE = 'favorite';
+export const PROJECTS_ALL = 'all';
+
export const SORTING_METRICS: SortingOption[] = [
{ value: 'name' },
{ value: 'analysis_date' },
generateSeries,
getSeriesMetricType,
hasHistoryDataValue,
+ PROJECT_ACTIVITY_GRAPH,
+ PROJECT_ACTIVITY_GRAPH_CUSTOM,
splitSeriesInGraphs
} from '../../apps/projectActivity/utils';
-import { getCustomGraph, getGraph } from '../../helpers/storage';
+import { get } from '../../helpers/storage';
import { formatMeasure, getShortType } from '../../helpers/measures';
import { getBranchLikeQuery } from '../../helpers/branches';
/*:: import type { Serie } from '../charts/AdvancedTimeline'; */
constructor(props /*: Props */) {
super(props);
- const graph = getGraph();
- const customMetrics = getCustomGraph();
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ const graph = get(PROJECT_ACTIVITY_GRAPH) || 'issues';
+ const customMetrics = customGraphs ? customGraphs.split(',') : [];
const series = splitSeriesInGraphs(
this.getSeries(props.history, graph, customMetrics, props.metrics),
MAX_GRAPH_NB,
componentWillReceiveProps(nextProps /*: Props */) {
if (nextProps.history !== this.props.history || nextProps.metrics !== this.props.metrics) {
- const graph = getGraph();
- const customMetrics = getCustomGraph();
+ const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
+ const graph = get(PROJECT_ACTIVITY_GRAPH) || 'issues';
+ const customMetrics = customGraphs ? customGraphs.split(',') : [];
const series = splitSeriesInGraphs(
this.getSeries(nextProps.history, graph, customMetrics, nextProps.metrics),
MAX_GRAPH_NB,
import { WorkspaceContext, ComponentDescriptor, RuleDescriptor } from './context';
import WorkspaceNav from './WorkspaceNav';
import WorkspacePortal from './WorkspacePortal';
+import { get, save } from '../../helpers/storage';
import { lazyLoad } from '../lazyLoad';
import './styles.css';
+const WORKSPACE = 'sonarqube-workspace';
const WorkspaceRuleViewer = lazyLoad(() => import('./WorkspaceRuleViewer'));
const WorkspaceComponentViewer = lazyLoad(() => import('./WorkspaceComponentViewer'));
const MAX_HEIGHT = 0.85;
const INITIAL_HEIGHT = 300;
-const STORAGE_KEY = 'sonarqube-workspace';
const TYPE_KEY = '__type__';
export default class Workspace extends React.PureComponent<{}, State> {
loadWorkspace = () => {
try {
- const data: any[] = JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '');
+ const data: any[] = JSON.parse(get(WORKSPACE) || '');
const components: ComponentDescriptor[] = data.filter(x => x[TYPE_KEY] === 'component');
const rules: RuleDescriptor[] = data.filter(x => x[TYPE_KEY] === 'rule');
return { components, rules };
...this.state.components.map(x => omit({ ...x, [TYPE_KEY]: 'component' }, 'line')),
...this.state.rules.map(x => ({ ...x, [TYPE_KEY]: 'rule' }))
];
- try {
- window.localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
- } catch {
- // fail silently
- }
+ save(WORKSPACE, JSON.stringify(data));
};
openComponent = (component: ComponentDescriptor) => {
*/
import { getJSON } from './request';
import { toNotSoISOString } from './dates';
+import { save, get } from './storage';
+
+const L10_TIMESTAMP = 'l10n.timestamp';
+const L10_LOCALE = 'l10n.locale';
+const L10_BUNDLE = 'l10n.bundle';
interface LanguageBundle {
[name: string]: string;
}
function checkCachedBundle(): boolean {
- const cached = localStorage.getItem('l10n.bundle');
+ const cached = get(L10_BUNDLE);
if (!cached) {
return false;
export function requestMessages(): Promise<string> {
const browserLocale = getPreferredLanguage();
- const cachedLocale = localStorage.getItem('l10n.locale');
+ const cachedLocale = get(L10_LOCALE);
const params: BundleRequestParams = {};
if (browserLocale) {
params.locale = browserLocale;
if (cachedLocale && browserLocale.startsWith(cachedLocale)) {
- const bundleTimestamp = localStorage.getItem('l10n.timestamp');
+ const bundleTimestamp = get(L10_TIMESTAMP);
if (bundleTimestamp !== null && checkCachedBundle()) {
params.ts = bundleTimestamp;
}
return getL10nBundle(params).then(
({ effectiveLocale, messages }: BundleRequestResponse) => {
- try {
- const currentTimestamp = toNotSoISOString(new Date());
- localStorage.setItem('l10n.timestamp', currentTimestamp);
- localStorage.setItem('l10n.locale', effectiveLocale);
- localStorage.setItem('l10n.bundle', JSON.stringify(messages));
- } catch (e) {
- // do nothing
- }
+ const currentTimestamp = toNotSoISOString(new Date());
+ save(L10_TIMESTAMP, currentTimestamp);
+ save(L10_LOCALE, effectiveLocale);
+ save(L10_BUNDLE, JSON.stringify(messages));
resetBundle(messages);
return effectiveLocale;
},
({ response }) => {
if (response && response.status === 304) {
- resetBundle(JSON.parse(localStorage.getItem('l10n.bundle') || '{}'));
+ resetBundle(JSON.parse(get(L10_BUNDLE) || '{}') as LanguageBundle);
} else {
throw new Error('Unexpected status code: ' + response.status);
}
}
export function getCurrentLocale() {
- // check `window && window.localStorage` for tests
- return (
- (window && window.localStorage && window.localStorage.getItem('l10n.locale')) ||
- DEFAULT_LANGUAGE
- );
+ return get(L10_LOCALE) || DEFAULT_LANGUAGE;
}
export function getShortMonthName(index: number) {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-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 PROJECT_ACTIVITY_GRAPH_CUSTOM = 'sonarqube.project_activity.graph.custom';
-
-function save(key: string, value?: string, suffix?: string): void {
+export function save(key: string, value?: string, suffix?: string): void {
try {
const finalKey = suffix ? `${key}.${suffix}` : key;
if (value) {
}
}
-function get(key: string, suffix?: string): string | null {
- return window.localStorage.getItem(suffix ? `${key}.${suffix}` : key);
-}
-
-export function saveFavorite(): void {
- save(PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE);
-}
-
-export function isFavoriteSet(): boolean {
- const setting = get(PROJECTS_DEFAULT_FILTER);
- return setting === PROJECTS_FAVORITE;
-}
-
-export function saveAll(): void {
- save(PROJECTS_DEFAULT_FILTER, PROJECTS_ALL);
-}
-
-export function isAllSet(): boolean {
- const setting = get(PROJECTS_DEFAULT_FILTER);
- return setting === PROJECTS_ALL;
-}
-
-export function saveView(view?: string, suffix?: string): void {
- save(PROJECTS_VIEW, view, suffix);
-}
-
-export function getView(suffix?: string): string | null {
- return get(PROJECTS_VIEW, suffix);
-}
-
-export function saveVisualization(visualization?: string, suffix?: string): void {
- save(PROJECTS_VISUALIZATION, visualization, suffix);
-}
-
-export function getVisualization(suffix?: string): string | null {
- return get(PROJECTS_VISUALIZATION, suffix);
-}
-
-export function saveSort(sort?: string, suffix?: string): void {
- save(PROJECTS_SORT, sort, suffix);
-}
-
-export function getSort(suffix?: string): string | null {
- return get(PROJECTS_SORT, suffix);
-}
-
-export function saveCustomGraph(metrics?: string[]): void {
- save(PROJECT_ACTIVITY_GRAPH_CUSTOM, metrics ? metrics.join(',') : '');
-}
-
-export function getCustomGraph(): string[] {
- const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
- return customGraphs ? customGraphs.split(',') : [];
-}
-
-export function saveGraph(graph?: string): void {
- save(PROJECT_ACTIVITY_GRAPH, graph);
+export function remove(key: string, suffix?: string): void {
+ try {
+ window.localStorage.removeItem(suffix ? `${key}.${suffix}` : key);
+ } catch {
+ // Fail silently
+ }
}
-export function getGraph(): string {
- return get(PROJECT_ACTIVITY_GRAPH) || 'issues';
+export function get(key: string, suffix?: string): string | null {
+ try {
+ return window.localStorage.getItem(suffix ? `${key}.${suffix}` : key);
+ } catch {
+ return null;
+ }
}