From 0e3dc2c14c241fb700c747a042383d795728eb31 Mon Sep 17 00:00:00 2001 From: Florian Kraft Date: Mon, 27 Jul 2015 18:41:41 +0200 Subject: [PATCH] initial splitoff of watchers into separate service Signed-off-by: Florian Kraft --- .../work_packages/tabs/watchers.html | 46 +--- .../details-tab-watchers-controller.js | 257 +++++++++--------- .../app/work_packages/controllers/index.js | 36 +-- frontend/app/work_packages/services/index.js | 5 + .../services/watchers-service.js | 74 +++++ 5 files changed, 243 insertions(+), 175 deletions(-) create mode 100644 frontend/app/work_packages/services/watchers-service.js diff --git a/frontend/app/templates/work_packages/tabs/watchers.html b/frontend/app/templates/work_packages/tabs/watchers.html index 0224abf59a..3af1ce4677 100644 --- a/frontend/app/templates/work_packages/tabs/watchers.html +++ b/frontend/app/templates/work_packages/tabs/watchers.html @@ -1,37 +1,11 @@ -
-
    -
  • - - - - -
  • -
+
-
-

-
- -
{{watcherListString()}}
- - - {{ $item.props.name }} - - -
-
-
-
-

- -
+ + Add watcher +
diff --git a/frontend/app/work_packages/controllers/details-tab-watchers-controller.js b/frontend/app/work_packages/controllers/details-tab-watchers-controller.js index 3dfa4f4fe1..5ee0621c1d 100644 --- a/frontend/app/work_packages/controllers/details-tab-watchers-controller.js +++ b/frontend/app/work_packages/controllers/details-tab-watchers-controller.js @@ -26,132 +26,147 @@ // See doc/COPYRIGHT.rdoc for more details. //++ -module.exports = function($scope, $filter, $timeout, I18n, ADD_WATCHER_SELECT_INDEX) { - $scope.I18n = I18n; - $scope.focusElementIndex; - - $scope.watcher = { selected: null }; - $scope.watcher.selected = $scope.watchers; - - fetchAvailableWatchers(); - $scope.watcherListString = function() { - return _.map($scope.watcher.selected, function(item) { - return item.props.name; - }).join(', '); - }; - - /** - * @name getResourceIdentifier - * @function - * - * @description - * Returns the resource identifier of an API resource retrieved via hyperagent - * - * @param {Object} resource The resource object - * - * @returns {String} identifier - */ - function getResourceIdentifier(resource) { - // TODO move to helper - return resource.links.self.href; - } - - /** - * @name getFilteredCollection - * @function - * - * @description - * Filters collection of HAL resources by entries listed in resourcesToBeFilteredOut - * - * @param {Array} collection Array of resources retrieved via hyperagent - * @param {Array} resourcesToBeFilteredOut Entries to be filtered out - * - * @returns {Array} filtered collection - */ - function getFilteredCollection(collection, resourcesToBeFilteredOut) { - return collection.filter(function(resource) { - return resourcesToBeFilteredOut.map(getResourceIdentifier).indexOf(getResourceIdentifier(resource)) === -1; +module.exports = function($scope, I18n, WatchersService, ADD_WATCHER_SELECT_INDEX) { + 'use strict'; + + var fetchWatchers = function(loading) { + $scope.error = false; + $scope.loading = angular.isUndefined(loading) ? true : false; + WatchersService.all($scope.workPackage).then(function(users) { + $scope.watchers = users.watching; + $scope.available = users.available; + }, function() { + $scope.watchers = []; + $scope.available = []; + $scope.error = true; + }).finally(function() { + $scope.loading = false; }); } - function fetchAvailableWatchers() { - if ($scope.workPackage.links.availableWatchers === undefined) { - $scope.availableWatchers = []; - return; - } - - $scope.workPackage.links.availableWatchers - .fetch() - .then(function(data) { - // Temporarily filter out watchers already assigned to the work package on the client-side - $scope.availableWatchers = getFilteredCollection(data.embedded.elements, $scope.watchers); - // TODO do filtering on the API side and replace the update of the - // available watchers with the code provided in the following line - // $scope.availableWatchers = data.embedded.elements; - }); - } - - function addWatcher(newValue, oldValue) { - if (newValue && newValue !== oldValue) { - var user = newValue[newValue.length - 1], - href = user ? user.links.self.href : null; - - if (href) { - var data = JSON.stringify({ user: { href: href } }); - $scope.workPackage.link('addWatcher', {}) - .fetch({ajax: { - method: 'POST', - contentType: 'application/json; charset=utf-8', - data: data - }}) - .then(addWatcherSuccess, $scope.outputError); - } - } - } - - function addWatcherSuccess() { - $scope.outputMessage(I18n.t("js.label_watcher_added_successfully")); - $scope.refreshWorkPackage(); - - $scope.watcher.selected = null; - - $scope.focusElementIndex = ADD_WATCHER_SELECT_INDEX; - } + fetchWatchers(); - $scope.deleteWatcher = function(watcher) { - watcher.links.removeWatcher - .fetch({ ajax: watcher.links.removeWatcher.props }) - .then(deleteWatcherSuccess(watcher), $scope.outputError); - }; - - function deleteWatcherSuccess(watcher) { - $scope.outputMessage(I18n.t("js.label_watcher_deleted_successfully")); - removeWatcherFromList(watcher); - } - - function removeWatcherFromList(watcher) { - var index = $scope.watchers.indexOf(watcher); - - if (index >= 0) { - $scope.watchers.splice(index, 1); - - updateWatcherFocus(index); - $scope.$emit('workPackageRefreshRequired'); - } - } - - function updateWatcherFocus(index) { - if ($scope.watchers.length == 0) { - $scope.focusElementIndex = ADD_WATCHER_SELECT_INDEX; - } else { - $scope.focusElementIndex = (index < $scope.watchers.length) ? index : $scope.watchers.length - 1; - } + $scope.I18n = I18n; - $timeout(function() { - $scope.$broadcast('updateFocus'); - }); + $scope.add = function() { + $scope.adding = true; } - $scope.$watch('watchers.length', fetchAvailableWatchers); - $scope.$watch('watcher.selected', addWatcher); + // $scope.watcherListString = function() { + // return _.map($scope.watcher.selected, function(item) { + // return item.props.name; + // }).join(', '); + // }; + + // /** + // * @name getResourceIdentifier + // * @function + // * + // * @description + // * Returns the resource identifier of an API resource retrieved via hyperagent + // * + // * @param {Object} resource The resource object + // * + // * @returns {String} identifier + // */ + // function getResourceIdentifier(resource) { + // // TODO move to helper + // return resource.links.self.href; + // } + + // /** + // * @name getFilteredCollection + // * @function + // * + // * @description + // * Filters collection of HAL resources by entries listed in resourcesToBeFilteredOut + // * + // * @param {Array} collection Array of resources retrieved via hyperagent + // * @param {Array} resourcesToBeFilteredOut Entries to be filtered out + // * + // * @returns {Array} filtered collection + // */ + // function getFilteredCollection(collection, resourcesToBeFilteredOut) { + // return collection.filter(function(resource) { + // return resourcesToBeFilteredOut.map(getResourceIdentifier).indexOf(getResourceIdentifier(resource)) === -1; + // }); + // } + + // function fetchAvailableWatchers() { + // if ($scope.workPackage.links.availableWatchers === undefined) { + // $scope.availableWatchers = []; + // return; + // } + + // $scope.workPackage.links.availableWatchers + // .fetch() + // .then(function(data) { + // // Temporarily filter out watchers already assigned to the work package on the client-side + // $scope.availableWatchers = getFilteredCollection(data.embedded.elements, $scope.watchers); + // // TODO do filtering on the API side and replace the update of the + // // available watchers with the code provided in the following line + // // $scope.availableWatchers = data.embedded.elements; + // }); + // } + + // function addWatcher(newValue, oldValue) { + // if (newValue && newValue !== oldValue) { + // var user = newValue[newValue.length - 1], + // href = user ? user.links.self.href : null; + + // if (href) { + // var data = JSON.stringify({ user: { href: href } }); + // $scope.workPackage.link('addWatcher', {}) + // .fetch({ajax: { + // method: 'POST', + // contentType: 'application/json; charset=utf-8', + // data: data + // }}) + // .then(addWatcherSuccess, $scope.outputError); + // } + // } + // } + + // function addWatcherSuccess() { + // $scope.outputMessage(I18n.t("js.label_watcher_added_successfully")); + // $scope.refreshWorkPackage(); + + // $scope.watcher.selected = null; + + // $scope.focusElementIndex = ADD_WATCHER_SELECT_INDEX; + // } + + // $scope.deleteWatcher = function(watcher) { + // watcher.links.removeWatcher + // .fetch({ ajax: watcher.links.removeWatcher.props }) + // .then(deleteWatcherSuccess(watcher), $scope.outputError); + // }; + + // function deleteWatcherSuccess(watcher) { + // $scope.outputMessage(I18n.t("js.label_watcher_deleted_successfully")); + // removeWatcherFromList(watcher); + // } + + // function removeWatcherFromList(watcher) { + // var index = $scope.watchers.indexOf(watcher); + + // if (index >= 0) { + // $scope.watchers.splice(index, 1); + + // updateWatcherFocus(index); + // $scope.$emit('workPackageRefreshRequired'); + // } + // } + + // function updateWatcherFocus(index) { + // if ($scope.watchers.length == 0) { + // $scope.focusElementIndex = ADD_WATCHER_SELECT_INDEX; + // } else { + // $scope.focusElementIndex = (index < $scope.watchers.length) ? index : $scope.watchers.length - 1; + // } + + // $timeout(function() { + // $scope.$broadcast('updateFocus'); + // }); + // } }; diff --git a/frontend/app/work_packages/controllers/index.js b/frontend/app/work_packages/controllers/index.js index 94cc4b20c2..8e0482467d 100644 --- a/frontend/app/work_packages/controllers/index.js +++ b/frontend/app/work_packages/controllers/index.js @@ -47,30 +47,30 @@ angular.module('openproject.workPackages.controllers') require('./details-tab-overview-controller') ]) .constant('ADD_WATCHER_SELECT_INDEX', -1) - .controller('DetailsTabWatchersController', ['$scope', - '$filter', - '$timeout', + .controller('DetailsTabWatchersController', [ + '$scope', 'I18n', + 'WatchersService', 'ADD_WATCHER_SELECT_INDEX', require('./details-tab-watchers-controller') ]) .constant('RELATION_TYPES', { - relatedTo: "Relation::Relates", - duplicates: "Relation::Duplicates", - duplicated: "Relation::Duplicated", - blocks: "Relation::Blocks", - blocked: "Relation::Blocked", - precedes: "Relation::Precedes", - follows: "Relation::Follows" + relatedTo: 'Relation::Relates', + duplicates: 'Relation::Duplicates', + duplicated: 'Relation::Duplicated', + blocks: 'Relation::Blocks', + blocked: 'Relation::Blocked', + precedes: 'Relation::Precedes', + follows: 'Relation::Follows' }) .constant('RELATION_IDENTIFIERS', { - parent: "parent", - relatedTo: "relates", - duplicates: "duplicates", - duplicated: "duplicated", - blocks: "blocks", - blocked: "blocked", - precedes: "precedes", - follows: "follows" + parent: 'parent', + relatedTo: 'relates', + duplicates: 'duplicates', + duplicated: 'duplicated', + blocks: 'blocks', + blocked: 'blocked', + precedes: 'precedes', + follows: 'follows' }) .controller('WorkPackageDetailsController', [ '$scope', diff --git a/frontend/app/work_packages/services/index.js b/frontend/app/work_packages/services/index.js index 8d75b7233b..5017a870a8 100644 --- a/frontend/app/work_packages/services/index.js +++ b/frontend/app/work_packages/services/index.js @@ -82,4 +82,9 @@ angular.module('openproject.workPackages.services') '$timeout', '$http', require('./work-package-attachments-service') + ]) + .service('WatchersService', [ + '$http', + '$q', + require('./watchers-service.js') ]); diff --git a/frontend/app/work_packages/services/watchers-service.js b/frontend/app/work_packages/services/watchers-service.js new file mode 100644 index 0000000000..aee1f54e47 --- /dev/null +++ b/frontend/app/work_packages/services/watchers-service.js @@ -0,0 +1,74 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// 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 General Public License for more details. +// +// You should have received a copy of the GNU 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. +// +// See doc/COPYRIGHT.rdoc for more details. +//++ + + +module.exports = function($http, $q) { + 'use strict'; + + var getWatchers = function(path) { + return function() { + var watchers = $q.defer(); + $http.get(path).success(function(data) { + watchers.resolve(data._embedded.elements); + }).error(function(err) { + watchers.reject(err); + }); + return watchers.promise; + }; + }; + + var load = function(workPackage) { + var path = workPackage.links.watchers.url(); + return getWatchers(path)(); + }, + available = function(workPackage) { + var path = workPackage.links.availableWatchers.url(); + return getWatchers(path)(); + }, + all = function(workPackage) { + var watchers = $q.defer(); + $q.all([load(workPackage), available(workPackage)]).then(function(allWatchers) { + var watching = allWatchers[0], + available = _.filter(allWatchers[1], function(user) { + return _.findIndex(allWatchers[0], function(matchedUser) { + return matchedUser.id !== user.id; + }) > -1; + }) + watchers.resolve({ watching: watching, available: available }); + }, function(err) { + watchers.reject(err); + }); + return watchers.promise; + }; + + return { + load: load, + available: available, + all: all + }; +};