aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/issues
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2017-03-03 11:47:55 +0100
committerStas Vilchik <vilchiks@gmail.com>2017-03-03 11:47:55 +0100
commitb6baff8775a45fd057ed9f15ba9ac29633261fea (patch)
tree1f3f8e2ae50ab60ff56e8afb207c836fad62566a /server/sonar-web/src/main/js/apps/issues
parent313ffad34e614acb9379fe1e0a0879a49b05be52 (diff)
downloadsonarqube-b6baff8775a45fd057ed9f15ba9ac29633261fea.tar.gz
sonarqube-b6baff8775a45fd057ed9f15ba9ac29633261fea.zip
Revert "refactor source viewer (#1705)"
This reverts commit ce9f0892fc3d15638c4eaa4054ed06f3d7e5fc19.
Diffstat (limited to 'server/sonar-web/src/main/js/apps/issues')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js35
-rw-r--r--server/sonar-web/src/main/js/apps/issues/component-viewer/main.js214
-rw-r--r--server/sonar-web/src/main/js/apps/issues/controller.js9
-rw-r--r--server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js90
-rw-r--r--server/sonar-web/src/main/js/apps/issues/workspace-list-view.js23
7 files changed, 264 insertions, 116 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js b/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js
new file mode 100644
index 00000000000..95e18194208
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js
@@ -0,0 +1,35 @@
+/*
+ * 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 IssueView from '../workspace-list-item-view';
+
+export default IssueView.extend({
+ onRender () {
+ IssueView.prototype.onRender.apply(this, arguments);
+ this.$el.removeClass('issue-navigate-right issue-with-checkbox');
+ },
+
+ serializeData () {
+ return {
+ ...IssueView.prototype.serializeData.apply(this, arguments),
+ showComponent: false
+ };
+ }
+});
+
diff --git a/server/sonar-web/src/main/js/apps/issues/component-viewer/main.js b/server/sonar-web/src/main/js/apps/issues/component-viewer/main.js
index 49d7152c45f..0f08c21112a 100644
--- a/server/sonar-web/src/main/js/apps/issues/component-viewer/main.js
+++ b/server/sonar-web/src/main/js/apps/issues/component-viewer/main.js
@@ -18,114 +18,186 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import $ from 'jquery';
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import Marionette from 'backbone.marionette';
-import SourceViewer from '../../../components/SourceViewer/SourceViewer';
-import WithStore from '../../../components/shared/WithStore';
-
-export default Marionette.ItemView.extend({
- template () {
- return '<div></div>';
+import SourceViewer from '../../../components/source-viewer/main';
+import IssueView from './issue-view';
+
+export default SourceViewer.extend({
+ events () {
+ return {
+ ...SourceViewer.prototype.events.apply(this, arguments),
+ 'click .js-close-component-viewer': 'closeComponentViewer',
+ 'click .code-issue': 'selectIssue'
+ };
},
initialize (options) {
- this.handleLoadIssues = this.handleLoadIssues.bind(this);
- this.scrollToBaseIssue = this.scrollToBaseIssue.bind(this);
- this.selectIssue = this.selectIssue.bind(this);
- this.listenTo(options.app.state, 'change:selectedIndex', this.select);
- },
-
- onRender () {
- this.showViewer();
- },
-
- onDestroy () {
- this.unbindShortcuts();
- unmountComponentAtNode(this.el);
- },
-
- handleLoadIssues (component: string) {
- // TODO fromLine: number, toLine: number
- const issues = this.options.app.list.toJSON().filter(issue => issue.componentKey === component);
- return Promise.resolve(issues);
+ SourceViewer.prototype.initialize.apply(this, arguments);
+ return this.listenTo(options.app.state, 'change:selectedIndex', this.select);
},
- showViewer (onLoaded) {
- if (!this.baseIssue) {
- return;
- }
-
- const componentKey = this.baseIssue.get('component');
-
- render((
- <WithStore>
- <SourceViewer
- aroundLine={this.baseIssue.get('line')}
- component={componentKey}
- displayAllIssues={true}
- loadIssues={this.handleLoadIssues}
- onLoaded={onLoaded}
- onIssueSelect={this.selectIssue}
- selectedIssue={this.baseIssue.get('key')}/>
- </WithStore>
- ), this.el);
- },
-
- openFileByIssue (issue) {
- this.baseIssue = issue;
- this.selectedIssue = issue.get('key');
- this.showViewer(this.scrollToBaseIssue);
+ onLoaded () {
+ SourceViewer.prototype.onLoaded.apply(this, arguments);
this.bindShortcuts();
+ if (this.baseIssue != null) {
+ this.baseIssue.trigger('locations', this.baseIssue);
+ this.scrollToLine(this.baseIssue.get('line'));
+ }
},
bindShortcuts () {
+ const that = this;
+ const doAction = function (action) {
+ const selectedIssueView = that.getSelectedIssueEl();
+ if (!selectedIssueView) {
+ return;
+ }
+ selectedIssueView.find('.js-issue-' + action).click();
+ };
key('up', 'componentViewer', () => {
- this.options.app.controller.selectPrev();
+ that.options.app.controller.selectPrev();
return false;
});
key('down', 'componentViewer', () => {
- this.options.app.controller.selectNext();
+ that.options.app.controller.selectNext();
return false;
});
key('left,backspace', 'componentViewer', () => {
- this.options.app.controller.closeComponentViewer();
+ that.options.app.controller.closeComponentViewer();
return false;
});
+ key('f', 'componentViewer', () => doAction('transition'));
+ key('a', 'componentViewer', () => doAction('assign'));
+ key('m', 'componentViewer', () => doAction('assign-to-me'));
+ key('p', 'componentViewer', () => doAction('plan'));
+ key('i', 'componentViewer', () => doAction('set-severity'));
+ key('c', 'componentViewer', () => doAction('comment'));
},
unbindShortcuts () {
- key.deleteScope('componentViewer');
+ return key.deleteScope('componentViewer');
+ },
+
+ onDestroy () {
+ SourceViewer.prototype.onDestroy.apply(this, arguments);
+ this.unbindScrollEvents();
+ return this.unbindShortcuts();
},
select () {
const selected = this.options.app.state.get('selectedIndex');
const selectedIssue = this.options.app.list.at(selected);
+ if (selectedIssue.get('component') === this.model.get('key')) {
+ selectedIssue.trigger('locations', selectedIssue);
+ return this.scrollToIssue(selectedIssue.get('key'));
+ } else {
+ this.unbindShortcuts();
+ return this.options.app.controller.showComponentViewer(selectedIssue);
+ }
+ },
+
+ getSelectedIssueEl () {
+ const selected = this.options.app.state.get('selectedIndex');
+ if (selected == null) {
+ return null;
+ }
+ const selectedIssue = this.options.app.list.at(selected);
+ if (selectedIssue == null) {
+ return null;
+ }
+ const selectedIssueView = this.$('#issue-' + (selectedIssue.get('key')));
+ if (selectedIssueView.length > 0) {
+ return selectedIssueView;
+ } else {
+ return null;
+ }
+ },
+
+ selectIssue (e) {
+ const key = $(e.currentTarget).data('issue-key');
+ const issue = this.issues.find(model => model.get('key') === key);
+ const index = this.options.app.list.indexOf(issue);
+ return this.options.app.state.set({ selectedIndex: index });
+ },
+
+ scrollToIssue (key) {
+ const el = this.$('#issue-' + key);
+ if (el.length > 0) {
+ const line = el.closest('[data-line-number]').data('line-number');
+ return this.scrollToLine(line);
+ } else {
+ this.unbindShortcuts();
+ const selected = this.options.app.state.get('selectedIndex');
+ const selectedIssue = this.options.app.list.at(selected);
+ return this.options.app.controller.showComponentViewer(selectedIssue);
+ }
+ },
+
+ openFileByIssue (issue) {
+ this.baseIssue = issue;
+ const componentKey = issue.get('component');
+ const componentUuid = issue.get('componentUuid');
+ return this.open(componentUuid, componentKey);
+ },
+
+ linesLimit () {
+ let line = this.LINES_LIMIT / 2;
+ if ((this.baseIssue != null) && this.baseIssue.has('line')) {
+ line = Math.max(line, this.baseIssue.get('line'));
+ }
+ return {
+ from: line - this.LINES_LIMIT / 2 + 1,
+ to: line + this.LINES_LIMIT / 2
+ };
+ },
- if (selectedIssue.get('component') === this.baseIssue.get('component')) {
- this.baseIssue = selectedIssue;
- this.showViewer(this.scrollToBaseIssue);
- this.scrollToBaseIssue();
+ limitIssues (issues) {
+ const that = this;
+ let index = this.ISSUES_LIMIT / 2;
+ if ((this.baseIssue != null) && this.baseIssue.has('index')) {
+ index = Math.max(index, this.baseIssue.get('index'));
+ }
+ return issues.filter(issue => Math.abs(issue.get('index') - index) <= that.ISSUES_LIMIT / 2);
+ },
+
+ requestIssues () {
+ const that = this;
+ let r;
+ if (this.options.app.list.last().get('component') === this.model.get('key')) {
+ r = this.options.app.controller.fetchNextPage();
} else {
- this.options.app.controller.showComponentViewer(selectedIssue);
+ r = $.Deferred().resolve().promise();
}
+ return r.done(() => {
+ that.issues.reset(that.options.app.list.filter(issue => issue.get('component') === that.model.key()));
+ that.issues.reset(that.limitIssues(that.issues));
+ return that.addIssuesPerLineMeta(that.issues);
+ });
+ },
+
+ renderIssues () {
+ this.issues.forEach(this.renderIssue, this);
+ return this.$('.source-line-issues').addClass('hidden');
+ },
+
+ renderIssue (issue) {
+ const issueView = new IssueView({
+ el: '#issue-' + issue.get('key'),
+ model: issue,
+ app: this.options.app
+ });
+ this.issueViews.push(issueView);
+ return issueView.render();
},
scrollToLine (line) {
const row = this.$(`[data-line-number=${line}]`);
const topOffset = $(window).height() / 2 - 60;
const goal = row.length > 0 ? row.offset().top - topOffset : 0;
- $(window).scrollTop(goal);
- },
-
- selectIssue (issueKey) {
- const issue = this.options.app.list.find(model => model.get('key') === issueKey);
- const index = this.options.app.list.indexOf(issue);
- this.options.app.state.set({ selectedIndex: index });
+ return $(window).scrollTop(goal);
},
- scrollToBaseIssue () {
- this.scrollToLine(this.baseIssue.get('line'));
+ closeComponentViewer () {
+ return this.options.app.controller.closeComponentViewer();
}
});
diff --git a/server/sonar-web/src/main/js/apps/issues/controller.js b/server/sonar-web/src/main/js/apps/issues/controller.js
index 71df7acdb7c..edc86827050 100644
--- a/server/sonar-web/src/main/js/apps/issues/controller.js
+++ b/server/sonar-web/src/main/js/apps/issues/controller.js
@@ -44,7 +44,14 @@ export default Controller.extend({
this.options.app.state.set({ selectedIndex: 0, page: 1 }, { silent: true });
this.closeComponentViewer();
}
- const data = this.getQueryAsObject();
+ const data = this._issuesParameters();
+ Object.assign(data, this.options.app.state.get('query'));
+ if (this.options.app.state.get('query').assigned_to_me) {
+ Object.assign(data, { assignees: '__me__' });
+ }
+ if (this.options.app.state.get('isContext')) {
+ Object.assign(data, this.options.app.state.get('contextQuery'));
+ }
return $.get(window.baseUrl + '/api/issues/search', data).done(r => {
const issues = that.options.app.list.parseIssues(r);
if (firstPage) {
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs
new file mode 100644
index 00000000000..dbb50e24779
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs
@@ -0,0 +1,3 @@
+<div class="js-toggle issue-checkbox-container">
+ <i class="issue-checkbox icon-checkbox {{#if selected}}icon-checkbox-checked{{/if}}"></i>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs
new file mode 100644
index 00000000000..16a212ddd60
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs
@@ -0,0 +1,6 @@
+<li class="issue-meta">
+ <button class="button-link issue-action issue-action-with-options js-issue-filter"
+ aria-label="{{t "issue.filter_similar_issues"}}">
+ <i class="icon-filter icon-half-transparent"></i>&nbsp;<i class="icon-dropdown"></i>
+ </button>
+</li>
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
index 43817e46586..9c57d181aa9 100644
--- a/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
+++ b/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
@@ -18,12 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import $ from 'jquery';
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import Marionette from 'backbone.marionette';
-import Issue from '../../components/issue/Issue';
+import IssueView from '../../components/issue/issue-view';
import IssueFilterView from './issue-filter-view';
-import WithStore from '../../components/shared/WithStore';
+import CheckboxTemplate from './templates/issues-issue-checkbox.hbs';
+import FilterTemplate from './templates/issues-issue-filter.hbs';
const SHOULD_NULL = {
any: ['issues'],
@@ -33,43 +31,35 @@ const SHOULD_NULL = {
assigned: ['assignees']
};
-export default Marionette.ItemView.extend({
- className: 'issues-workspace-list-item',
+export default IssueView.extend({
+ checkboxTemplate: CheckboxTemplate,
+ filterTemplate: FilterTemplate,
- initialize (options) {
- this.openComponentViewer = this.openComponentViewer.bind(this);
- this.onIssueFilterClick = this.onIssueFilterClick.bind(this);
- this.onIssueCheck = this.onIssueCheck.bind(this);
- this.listenTo(options.app.state, 'change:selectedIndex', this.showIssue);
- this.listenTo(this.model, 'change:selected', this.showIssue);
+ events () {
+ return {
+ ...IssueView.prototype.events.apply(this, arguments),
+ 'click': 'selectCurrent',
+ 'dblclick': 'openComponentViewer',
+ 'click .js-issue-navigate': 'openComponentViewer',
+ 'click .js-issue-filter': 'onIssueFilterClick',
+ 'click .js-toggle': 'onIssueToggle'
+ };
},
- template () {
- return '<div></div>';
+ initialize (options) {
+ IssueView.prototype.initialize.apply(this, arguments);
+ this.listenTo(options.app.state, 'change:selectedIndex', this.select);
},
onRender () {
- this.showIssue();
- },
-
- onDestroy () {
- unmountComponentAtNode(this.el);
- },
-
- showIssue () {
- const selected = this.model.get('index') === this.options.app.state.get('selectedIndex');
-
- render((
- <WithStore>
- <Issue
- issue={this.model}
- checked={this.model.get('selected')}
- onCheck={this.onIssueCheck}
- onClick={this.openComponentViewer}
- onFilterClick={this.onIssueFilterClick}
- selected={selected}/>
- </WithStore>
- ), this.el);
+ IssueView.prototype.onRender.apply(this, arguments);
+ this.select();
+ this.addFilterSelect();
+ this.addCheckbox();
+ this.$el.addClass('issue-navigate-right');
+ if (this.options.app.state.get('canBulkChange')) {
+ this.$el.addClass('issue-with-checkbox');
+ }
},
onIssueFilterClick (e) {
@@ -99,21 +89,26 @@ export default Marionette.ItemView.extend({
this.popup.render();
},
- onIssueCheck (e) {
+ onIssueToggle (e) {
e.preventDefault();
- e.stopPropagation();
this.model.set({ selected: !this.model.get('selected') });
const selected = this.model.collection.where({ selected: true }).length;
this.options.app.state.set({ selected });
},
- changeSelection () {
+ addFilterSelect () {
+ this.$('.issue-table-meta-cell-first')
+ .find('.issue-meta-list')
+ .append(this.filterTemplate(this.model.toJSON()));
+ },
+
+ addCheckbox () {
+ this.$el.append(this.checkboxTemplate(this.model.toJSON()));
+ },
+
+ select () {
const selected = this.model.get('index') === this.options.app.state.get('selectedIndex');
- if (selected) {
- this.select();
- } else {
- this.unselect();
- }
+ this.$el.toggleClass('selected', selected);
},
selectCurrent () {
@@ -142,5 +137,12 @@ export default Marionette.ItemView.extend({
} else {
return this.options.app.controller.showComponentViewer(this.model);
}
+ },
+
+ serializeData () {
+ return {
+ ...IssueView.prototype.serializeData.apply(this, arguments),
+ showComponent: true
+ };
}
});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
index 383d3145f24..669f4c139e6 100644
--- a/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
+++ b/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
@@ -37,6 +37,14 @@ export default WorkspaceListView.extend({
bindShortcuts () {
const that = this;
+ const doAction = function (action) {
+ const selectedIssue = that.collection.at(that.options.app.state.get('selectedIndex'));
+ if (selectedIssue == null) {
+ return;
+ }
+ const selectedIssueView = that.children.findByModel(selectedIssue);
+ selectedIssueView.$('.js-issue-' + action).click();
+ };
WorkspaceListView.prototype.bindShortcuts.apply(this, arguments);
key('right', 'list', () => {
const selectedIssue = that.collection.at(that.options.app.state.get('selectedIndex'));
@@ -48,12 +56,26 @@ export default WorkspaceListView.extend({
selectedIssue.set({ selected: !selectedIssue.get('selected') });
return false;
});
+ key('f', 'list', () => doAction('transition'));
+ key('a', 'list', () => doAction('assign'));
+ key('m', 'list', () => doAction('assign-to-me'));
+ key('p', 'list', () => doAction('plan'));
+ key('i', 'list', () => doAction('set-severity'));
+ key('c', 'list', () => doAction('comment'));
+ key('t', 'list', () => doAction('edit-tags'));
},
unbindShortcuts () {
WorkspaceListView.prototype.unbindShortcuts.apply(this, arguments);
key.unbind('right', 'list');
key.unbind('space', 'list');
+ key.unbind('f', 'list');
+ key.unbind('a', 'list');
+ key.unbind('m', 'list');
+ key.unbind('p', 'list');
+ key.unbind('i', 'list');
+ key.unbind('c', 'list');
+ key.unbind('t', 'list');
},
scrollTo () {
@@ -100,6 +122,7 @@ export default WorkspaceListView.extend({
displayComponent (container, model) {
const data = { ...model.toJSON() };
+ /* eslint-disable no-console */
const qualifier = this.options.app.state.get('contextComponentQualifier');
if (qualifier === 'VW' || qualifier === 'SVW') {
Object.assign(data, { organization: undefined });