diff options
Diffstat (limited to 'server/sonar-web/src/main/js/apps/issues')
7 files changed, 134 insertions, 256 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 deleted file mode 100644 index 95e18194208..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 0f08c21112a..49d7152c45f 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,186 +18,114 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import $ from 'jquery'; -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' - }; +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>'; }, initialize (options) { - SourceViewer.prototype.initialize.apply(this, arguments); - return this.listenTo(options.app.state, 'change:selectedIndex', this.select); + 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); }, - 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')); + 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); + }, + + 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); + this.bindShortcuts(); }, bindShortcuts () { - const that = this; - const doAction = function (action) { - const selectedIssueView = that.getSelectedIssueEl(); - if (!selectedIssueView) { - return; - } - selectedIssueView.find('.js-issue-' + action).click(); - }; key('up', 'componentViewer', () => { - that.options.app.controller.selectPrev(); + this.options.app.controller.selectPrev(); return false; }); key('down', 'componentViewer', () => { - that.options.app.controller.selectNext(); + this.options.app.controller.selectNext(); return false; }); key('left,backspace', 'componentViewer', () => { - that.options.app.controller.closeComponentViewer(); + this.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 () { - return key.deleteScope('componentViewer'); - }, - - onDestroy () { - SourceViewer.prototype.onDestroy.apply(this, arguments); - this.unbindScrollEvents(); - return this.unbindShortcuts(); + key.deleteScope('componentViewer'); }, 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 - }; - }, - 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(); + if (selectedIssue.get('component') === this.baseIssue.get('component')) { + this.baseIssue = selectedIssue; + this.showViewer(this.scrollToBaseIssue); + this.scrollToBaseIssue(); } else { - r = $.Deferred().resolve().promise(); + this.options.app.controller.showComponentViewer(selectedIssue); } - 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; - return $(window).scrollTop(goal); + $(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 }); }, - closeComponentViewer () { - return this.options.app.controller.closeComponentViewer(); + scrollToBaseIssue () { + this.scrollToLine(this.baseIssue.get('line')); } }); 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 edc86827050..dd03639f5b0 100644 --- a/server/sonar-web/src/main/js/apps/issues/controller.js +++ b/server/sonar-web/src/main/js/apps/issues/controller.js @@ -21,6 +21,8 @@ import $ from 'jquery'; import Backbone from 'backbone'; import Controller from '../../components/navigator/controller'; import ComponentViewer from './component-viewer/main'; +import getStore from '../../app/utils/getStore'; +import { receiveIssues } from '../../store/issues/duck'; const FACET_DATA_FIELDS = ['components', 'users', 'rules', 'languages']; @@ -35,6 +37,11 @@ export default Controller.extend({ }; }, + receiveIssues (issues) { + const store = getStore(); + store.dispatch(receiveIssues(issues)); + }, + fetchList (firstPage) { const that = this; if (firstPage == null) { @@ -54,6 +61,7 @@ export default Controller.extend({ } return $.get(window.baseUrl + '/api/issues/search', data).done(r => { const issues = that.options.app.list.parseIssues(r); + this.receiveIssues(issues); if (firstPage) { that.options.app.list.reset(issues); } else { 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 deleted file mode 100644 index dbb50e24779..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-checkbox.hbs +++ /dev/null @@ -1,3 +0,0 @@ -<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 deleted file mode 100644 index 16a212ddd60..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs +++ /dev/null @@ -1,6 +0,0 @@ -<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 9c57d181aa9..8c357939b0e 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,10 +18,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import $ from 'jquery'; -import IssueView from '../../components/issue/issue-view'; +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import Marionette from 'backbone.marionette'; +import ConnectedIssue from '../../components/issue/ConnectedIssue'; import IssueFilterView from './issue-filter-view'; -import CheckboxTemplate from './templates/issues-issue-checkbox.hbs'; -import FilterTemplate from './templates/issues-issue-filter.hbs'; +import WithStore from '../../components/shared/WithStore'; +import getStore from '../../app/utils/getStore'; +import { getIssueByKey } from '../../store/rootReducer'; const SHOULD_NULL = { any: ['issues'], @@ -31,35 +35,52 @@ const SHOULD_NULL = { assigned: ['assignees'] }; -export default IssueView.extend({ - checkboxTemplate: CheckboxTemplate, - filterTemplate: FilterTemplate, +export default Marionette.ItemView.extend({ + className: 'issues-workspace-list-item', - 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' - }; + 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); + this.subscribeToStore(); }, - initialize (options) { - IssueView.prototype.initialize.apply(this, arguments); - this.listenTo(options.app.state, 'change:selectedIndex', this.select); + template () { + return '<div></div>'; + }, + + subscribeToStore () { + const store = getStore(); + store.subscribe(() => { + const issue = getIssueByKey(store.getState(), this.model.get('key')); + this.model.set(issue); + }); }, onRender () { - 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'); - } + this.showIssue(); + }, + + onDestroy () { + unmountComponentAtNode(this.el); + }, + + showIssue () { + const selected = this.model.get('index') === this.options.app.state.get('selectedIndex'); + + render(( + <WithStore> + <ConnectedIssue + issueKey={this.model.get('key')} + checked={this.model.get('selected')} + onCheck={this.onIssueCheck} + onClick={this.openComponentViewer} + onFilterClick={this.onIssueFilterClick} + selected={selected}/> + </WithStore> + ), this.el); }, onIssueFilterClick (e) { @@ -89,26 +110,21 @@ export default IssueView.extend({ this.popup.render(); }, - onIssueToggle (e) { + onIssueCheck (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 }); }, - 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 () { + changeSelection () { const selected = this.model.get('index') === this.options.app.state.get('selectedIndex'); - this.$el.toggleClass('selected', selected); + if (selected) { + this.select(); + } else { + this.unselect(); + } }, selectCurrent () { @@ -137,12 +153,5 @@ export default IssueView.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 669f4c139e6..383d3145f24 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,14 +37,6 @@ 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')); @@ -56,26 +48,12 @@ 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 () { @@ -122,7 +100,6 @@ 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 }); |