From 347bf31f2def967d11d000d0464adcc894a88d13 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 12 Mar 2015 12:22:47 -0700 Subject: [PATCH] Have tags selected be handled universally throughout the entire repository view page. --- .../directives/repo-view/repo-panel-tags.css | 20 +++----- .../repo-view/repo-panel-changes.html | 3 +- .../directives/repo-view/repo-panel-tags.html | 10 ++-- .../repo-view/repo-panel-changes.js | 47 ++++++----------- .../directives/repo-view/repo-panel-tags.js | 43 ++++++++++++---- .../js/directives/ui/tag-operations-dialog.js | 12 +++-- static/js/pages/repo-view.js | 51 ++++++++++++++++--- static/js/services/ui-service.js | 23 +++++++-- static/partials/repo-view.html | 30 +++++++---- 9 files changed, 154 insertions(+), 85 deletions(-) diff --git a/static/css/directives/repo-view/repo-panel-tags.css b/static/css/directives/repo-view/repo-panel-tags.css index e50f43a34..69fdd91a2 100644 --- a/static/css/directives/repo-view/repo-panel-tags.css +++ b/static/css/directives/repo-view/repo-panel-tags.css @@ -11,33 +11,29 @@ position: relative; } -.repo-panel-tags-element .image-track-dot:after { - content: "\f10c"; - font-family: FontAwesome; - +.repo-panel-tags-element .image-track-dot { display: inline-block; position: absolute; top: 15px; - left: 0px; - width: 17px; - - font-size: 11px; - text-align: center; + left: 2px; + width: 12px; + height: 12px; background: white; z-index: 300; - height: 13px; + + border: 2px solid black; + border-radius: 50%; } .repo-panel-tags-element .image-track-line { position: absolute; top: 0px; - bottom: -11px; + bottom: -1px; left: 7px; width: 0px; display: inline-block; - height: 100%; border-left: 2px solid black; display: none; diff --git a/static/directives/repo-view/repo-panel-changes.html b/static/directives/repo-view/repo-panel-changes.html index 470fc5649..1922b49b9 100644 --- a/static/directives/repo-view/repo-panel-changes.html +++ b/static/directives/repo-view/repo-panel-changes.html @@ -60,5 +60,4 @@
\ No newline at end of file + action-handler="tagActionHandler" tag-changed="handleTagChanged(data)"> \ No newline at end of file diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index 11dc45847..43d1f725e 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -21,8 +21,9 @@ - Visualize + + Visualize + @@ -60,8 +61,9 @@ {{ tag.size | bytes }} {{ tag.image_id.substr(0, 12) }} - - + + diff --git a/static/js/directives/repo-view/repo-panel-changes.js b/static/js/directives/repo-view/repo-panel-changes.js index c85b19530..a2eee551c 100644 --- a/static/js/directives/repo-view/repo-panel-changes.js +++ b/static/js/directives/repo-view/repo-panel-changes.js @@ -87,33 +87,32 @@ angular.module('quay').directive('repoPanelChanges', function () { transclude: false, restrict: 'C', scope: { - 'repository': '=repository' + 'repository': '=repository', + 'selectedTags': '=selectedTags', + 'isEnabled': '=isEnabled' }, - controller: function($scope, $element, $location, $timeout, ApiService, UtilService, ImageMetadataService) { + controller: function($scope, $element, $timeout, ApiService, UtilService, ImageMetadataService) { var update = function() { - if (!$scope.repository) { return; } - - var tagString = $location.search()['tags'] || ''; - if (!tagString) { - $scope.selectedTags = []; - return; - } + if (!$scope.repository || !$scope.selectedTags) { return; } $scope.currentImage = null; - $scope.currentImage = null; - $scope.selectedTags = tagString.split(','); + $scope.currentTag = null; - if (!$scope.imageResource) { + if (!$scope.imagesResource) { loadImages(); - } else { - refreshTree(); } }; - $scope.$on('$routeUpdate', update); + $scope.$watch('selectedTags', update) $scope.$watch('repository', update); + $scope.$watch('isEnabled', function(isEnabled) { + if (isEnabled) { + refreshTree(); + } + }); + var refreshTree = function() { if (!$scope.repository || !$scope.images) { return; } @@ -157,10 +156,6 @@ angular.module('quay').directive('repoPanelChanges', function () { $scope.images = resp.images; $scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images); - $scope.selectedTags = $.grep($scope.selectedTags, function(tag) { - return !!$scope.tracker.lookupTag(tag); - }); - if ($scope.selectedTags && $scope.selectedTags.length) { refreshTree(); } @@ -193,26 +188,16 @@ angular.module('quay').directive('repoPanelChanges', function () { $scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images); data.removed.map(function(tag) { - $scope.selectedTags = $.grep($scope.selectedTags, function(cTag) { - return cTag != tag; - }); - - if ($scope.selectedTags.length) { - $location.search('tags', $scope.selectedTags.join(',')); - } else { - $location.search('tags', null); - } - $scope.currentImage = null; $scope.currentTag = null; }); data.added.map(function(tag) { $scope.selectedTags.push(tag); - $location.search('tags', $scope.selectedTags.join(',')); - $scope.currentTag = tag; }); + + refreshTree(); }; } }; diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index 9934eed18..5acdb9fed 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -10,9 +10,9 @@ angular.module('quay').directive('repoPanelTags', function () { restrict: 'C', scope: { 'repository': '=repository', - 'repositoryUpdated': '&repositoryUpdated' + 'selectedTags': '=selectedTags' }, - controller: function($scope, $element, $filter, ApiService, UIService) { + controller: function($scope, $element, $filter, $location, ApiService, UIService) { var orderBy = $filter('orderBy'); $scope.checkedTags = UIService.createCheckStateController([]); @@ -35,7 +35,7 @@ angular.module('quay').directive('repoPanelTags', function () { }; var setTagState = function() { - if (!$scope.repository) { return; } + if (!$scope.repository || !$scope.selectedTags) { return; } var tags = []; var allTags = []; @@ -60,21 +60,29 @@ angular.module('quay').directive('repoPanelTags', function () { } // Sort the tags by the predicate and the reverse, and map the information. + var imageIDs = []; var ordered = orderBy(tags, $scope.options.predicate, $scope.options.reverse); + var checked = []; + for (var i = 0; i < ordered.length; ++i) { var tagInfo = ordered[i]; if (!imageMap[tagInfo.image_id]) { imageMap[tagInfo.image_id] = []; + imageIDs.push(tagInfo.image_id) } imageMap[tagInfo.image_id].push(tagInfo); + + if ($.inArray(tagInfo.name, $scope.selectedTags) >= 0) { + checked.push(tagInfo); + } }; // Calculate the image tracks. var colors = d3.scale.category10(); var index = 0; - for (var image_id in imageMap) { + imageIDs.sort().map(function(image_id) { if (imageMap[image_id].length >= 2){ imageTracks.push({ 'image_id': image_id, @@ -84,19 +92,34 @@ angular.module('quay').directive('repoPanelTags', function () { }); ++index; } - } + }); $scope.imageMap = imageMap; $scope.imageTracks = imageTracks; + $scope.tags = ordered; - $scope.checkedTags = UIService.createCheckStateController(ordered); $scope.allTags = allTags; - $scope.iterationState = {}; + + $scope.checkedTags = UIService.createCheckStateController(ordered, checked); + $scope.checkedTags.listen(function(checked) { + $scope.selectedTags = checked.map(function(tag_info) { + return tag_info.name; + }); + }); } $scope.$watch('options.predicate', setTagState); $scope.$watch('options.reverse', setTagState); $scope.$watch('options.tagFilter', setTagState); + + $scope.$watch('selectedTags', function(selectedTags) { + if (!selectedTags || !$scope.repository || !$scope.imageMap) { return; } + + $scope.checkedTags.checked = selectedTags.map(function(tag) { + return $scope.repository.tags[tag]; + }); + }, true); + $scope.$watch('repository', function(repository) { if (!repository) { return; } @@ -175,10 +198,8 @@ angular.module('quay').directive('repoPanelTags', function () { return tag.image_id == image_id; }; - $scope.getCheckedTagsString = function(checked) { - return checked.map(function(tag_info) { - return tag_info.name; - }).join(','); + $scope.setTab = function(tab) { + $location.search('tab', tab); }; } }; diff --git a/static/js/directives/ui/tag-operations-dialog.js b/static/js/directives/ui/tag-operations-dialog.js index 5deb538ef..f9645be4d 100644 --- a/static/js/directives/ui/tag-operations-dialog.js +++ b/static/js/directives/ui/tag-operations-dialog.js @@ -30,9 +30,14 @@ angular.module('quay').directive('tagOperationsDialog', function () { ApiService.listRepositoryImages(null, params).then(function(resp) { $scope.images = resp.images; - $scope.tagChanged({ - 'data': { 'added': added, 'removed': removed } - }); + + // Note: We need the timeout here so that Angular can $digest the images change + // on the parent scope before the tagChanged callback occurs. + $timeout(function() { + $scope.tagChanged({ + 'data': { 'added': added, 'removed': removed } + }); + }, 1); }) }); }; @@ -72,7 +77,6 @@ angular.module('quay').directive('tagOperationsDialog', function () { ApiService.changeTagImage(data, params).then(function(resp) { $element.find('#createOrMoveTagModal').modal('hide'); $scope.addingTag = false; - markChanged([tag], []); }, errorHandler); }; diff --git a/static/js/pages/repo-view.js b/static/js/pages/repo-view.js index c1b188739..34c575f85 100644 --- a/static/js/pages/repo-view.js +++ b/static/js/pages/repo-view.js @@ -13,25 +13,56 @@ }, ['old-layout']); }]); - function RepoViewCtrl($scope, $routeParams, ApiService, UserService, AngularPollChannel) { + function RepoViewCtrl($scope, $routeParams, $location, ApiService, UserService, AngularPollChannel) { $scope.namespace = $routeParams.namespace; $scope.name = $routeParams.name; $scope.logsShown = 0; + $scope.viewScope = { + 'selectedTags': [], + 'repository': null, + 'builds': null, + 'changesVisible': false + }; var buildPollChannel = null; // Make sure we track the current user. UserService.updateUserIn($scope); + // Watch the selected tags and update the URL accordingly. + $scope.$watch('viewScope.selectedTags', function(selectedTags) { + if (!selectedTags || !$scope.viewScope.repository) { return; } + + var tags = filterTags(selectedTags); + if (!tags.length) { + $location.search('tag', null); + return; + } + + $location.search('tag', tags.join(',')); + }, true); + + // Watch the repository to filter any tags removed. + $scope.$watch('viewScope.repository', function(repository) { + if (!repository) { return; } + $scope.viewScope.selectedTags = filterTags($scope.viewScope.selectedTags); + }); + + var filterTags = function(tags) { + return (tags || []).filter(function(tag) { + return !!$scope.viewScope.repository.tags[tag]; + }); + }; + var loadRepository = function() { var params = { 'repository': $scope.namespace + '/' + $scope.name }; $scope.repositoryResource = ApiService.getRepoAsResource(params).get(function(repo) { - $scope.repository = repo; - $scope.setTag($routeParams.tag); + $scope.viewScope.repository = repo; + $scope.setTags($routeParams.tag); // Track builds. buildPollChannel = AngularPollChannel.create($scope, loadRepositoryBuilds, 5000 /* 5s */); @@ -49,7 +80,7 @@ }; $scope.repositoryBuildsResource = ApiService.getRepoBuildsAsResource(params, /* background */true).get(function(resp) { - $scope.builds = resp.builds; + $scope.viewScope.builds = resp.builds; callback(true); }, errorHandler); }; @@ -57,14 +88,22 @@ // Load the repository. loadRepository(); + $scope.setTags = function(tagNames) { + if (!tagNames) { + $scope.viewScope.selectedTags = []; + return; + } - $scope.setTag = function(tagName) { - window.console.log('set tag') + $scope.viewScope.selectedTags = $.unique(tagNames.split(',')); }; $scope.showLogs = function() { $scope.logsShown++; }; + + $scope.handleChangesState = function(value) { + $scope.viewScope.changesVisible = value; + }; } function OldRepoViewCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiService, $routeParams, $rootScope, $location, $timeout, Config, UtilService) { diff --git a/static/js/services/ui-service.js b/static/js/services/ui-service.js index 1f084c6d6..2e857e8fa 100644 --- a/static/js/services/ui-service.js +++ b/static/js/services/ui-service.js @@ -2,9 +2,14 @@ * Service which provides helper methods for performing some simple UI operations. */ angular.module('quay').factory('UIService', [function() { - var CheckStateController = function(items) { + var CheckStateController = function(items, opt_checked) { this.items = items; - this.checked = []; + this.checked = opt_checked || []; + this.listeners_ = []; + }; + + CheckStateController.prototype.listen = function(callback) { + this.listeners_.push(callback); }; CheckStateController.prototype.isChecked = function(item) { @@ -25,22 +30,32 @@ angular.module('quay').factory('UIService', [function() { } else { this.checked = this.items.slice(); } + this.callListeners_(); }; CheckStateController.prototype.checkByFilter = function(filter) { this.checked = $.grep(this.items, filter); + this.callListeners_(); }; CheckStateController.prototype.checkItem = function(item) { this.checked.push(item); + this.callListeners_(); }; CheckStateController.prototype.uncheckItem = function(item) { this.checked = $.grep(this.checked, function(cItem) { return cItem != item; }); + this.callListeners_(); }; + CheckStateController.prototype.callListeners_ = function() { + var checked = this.checked; + this.listeners_.map(function(listener) { + listener(checked); + }); + }; var uiService = {}; @@ -73,8 +88,8 @@ angular.module('quay').factory('UIService', [function() { } }; - uiService.createCheckStateController = function(items) { - return new CheckStateController(items); + uiService.createCheckStateController = function(items, opt_checked) { + return new CheckStateController(items, opt_checked); }; return uiService; diff --git a/static/partials/repo-view.html b/static/partials/repo-view.html index c7fdb5b54..5d00dc98c 100644 --- a/static/partials/repo-view.html +++ b/static/partials/repo-view.html @@ -5,9 +5,9 @@
- + {{ namespace }} / {{ name }} - +
@@ -25,18 +25,19 @@
- + + ng-if="viewScope.repository.can_admin"> + ng-if="viewScope.repository.can_admin"> @@ -44,12 +45,16 @@
-
+
-
+
@@ -59,16 +64,19 @@
-
+
-
-
+
+
-
+
settings