diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2017-03-03 11:47:55 +0100 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2017-03-03 11:47:55 +0100 |
commit | b6baff8775a45fd057ed9f15ba9ac29633261fea (patch) | |
tree | 1f3f8e2ae50ab60ff56e8afb207c836fad62566a /server/sonar-web/src/main/js/apps/issues | |
parent | 313ffad34e614acb9379fe1e0a0879a49b05be52 (diff) | |
download | sonarqube-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')
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> <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 }); |