From 8b25d5b77bd5e0ac1ee08e469188913d7d9e1d0e Mon Sep 17 00:00:00 2001 From: Kenny Lee Sin Cheong <2530351+kleesc@users.noreply.github.com> Date: Tue, 23 Oct 2018 13:26:40 -0400 Subject: [PATCH] Fix UI rendering issue when creating/deleting tags from the UI (#3269) ### Description of Changes Tag operations in UI would not be rendered properly when using the paginated tags endpoint. When a user would create/delete a tag from the repo-panel-tags, `digest` would be called. This caused the `$scope.repository.tags` to be removed. To fix this: * Bind the tags directly to the scope instead of the repository * Change references to scope.repository.tags to use scope.repositoryTags --- --- .../directives/repo-view/repo-panel-tags.html | 2 +- static/directives/tag-operations-dialog.html | 3 +- .../directives/repo-view/repo-panel-tags.js | 21 ++++--- .../js/directives/ui/tag-operations-dialog.js | 57 +++++++++++++------ .../directives/ui/tag-specific-image-view.js | 10 ++-- static/js/pages/repo-view.js | 12 ++-- static/partials/repo-view.html | 1 + 7 files changed, 69 insertions(+), 37 deletions(-) diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index 873be00e6..d34fab7ff 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -347,7 +347,7 @@ -
diff --git a/static/directives/tag-operations-dialog.html b/static/directives/tag-operations-dialog.html index 40a1c6555..ba17c3c6f 100644 --- a/static/directives/tag-operations-dialog.html +++ b/static/directives/tag-operations-dialog.html @@ -71,6 +71,7 @@
{{ deleteTagInfo.tag }}? -
The following images and any other images not referenced by a tag will be deletedunavailable and deleted in {{ getFormattedTimespan(repository.tag_expiration_s) }}:
diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index 4e602cd2c..f1f54752f 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -10,6 +10,7 @@ angular.module('quay').directive('repoPanelTags', function () { restrict: 'C', scope: { 'repository': '=repository', + 'repositoryTags': '=repositoryTags', 'selectedTags': '=selectedTags', 'historyFilter': '=historyFilter', 'imagesResource': '=imagesResource', @@ -64,14 +65,14 @@ angular.module('quay').directive('repoPanelTags', function () { }; var setTagState = function() { - if (!$scope.repository || !$scope.selectedTags) { return; } + if (!$scope.repositoryTags || !$scope.selectedTags) { return; } // Build a list of all the tags, with extending information. var allTags = []; - for (var tag in $scope.repository.tags) { - if (!$scope.repository.tags.hasOwnProperty(tag)) { continue; } + for (var tag in $scope.repositoryTags) { + if (!$scope.repositoryTags.hasOwnProperty(tag)) { continue; } - var tagData = $scope.repository.tags[tag]; + var tagData = $scope.repositoryTags[tag]; var tagInfo = $.extend(tagData, { 'name': tag, 'last_modified_datetime': TableService.getReversedTimestamp(tagData.last_modified), @@ -233,17 +234,21 @@ angular.module('quay').directive('repoPanelTags', function () { if (!selectedTags || !$scope.repository || !$scope.imageMap) { return; } $scope.checkedTags.setChecked(selectedTags.map(function(tag) { - return $scope.repository.tags[tag]; + return $scope.repositoryTags[tag]; })); }, true); $scope.$watch('repository', function(updatedRepoObject, previousRepoObject) { - if (updatedRepoObject.tags === previousRepoObject.tags) { return; } - // Process each of the tags. setTagState(); loadRepoSignatures(); - }, true); + }); + + $scope.$watch('repositoryTags', function(tags) { + // Process each of the tags. + setTagState(); + loadRepoSignatures(); + }); $scope.loadImageVulnerabilities = function(image_id, imageData) { VulnerabilityService.loadImageVulnerabilities($scope.repository, image_id, function(resp) { diff --git a/static/js/directives/ui/tag-operations-dialog.js b/static/js/directives/ui/tag-operations-dialog.js index c3db786e4..417a997a8 100644 --- a/static/js/directives/ui/tag-operations-dialog.js +++ b/static/js/directives/ui/tag-operations-dialog.js @@ -11,6 +11,7 @@ angular.module('quay').directive('tagOperationsDialog', function () { restrict: 'C', scope: { 'repository': '=repository', + 'repositoryTags': '=repositoryTags', 'actionHandler': '=actionHandler', 'imageLoader': '=imageLoader', 'tagChanged': '&tagChanged', @@ -20,22 +21,44 @@ angular.module('quay').directive('tagOperationsDialog', function () { $scope.addingTag = false; $scope.changeTagsExpirationInfo = null; - var markChanged = function(added, removed) { - // Reload the repository. - $scope.repository.get().then(function(resp) { - $scope.repository = resp; - $scope.imageLoader.reset() + var reloadTags = function(page, tags, added, removed) { + var params = { + 'repository': $scope.repository.namespace + '/' + $scope.repository.name, + 'limit': 100, + 'page': page, + 'onlyActiveTags': true + }; - // 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); + ApiService.listRepoTags(null, params).then(function(resp) { + var newTags = resp.tags.reduce(function(result, item, index, array) { + var tag_name = item['name']; + result[tag_name] = item; + return result; + }, {}); + + $.extend(tags, newTags); + + if (resp.has_additional) { + loadPaginatedRepositoryTags(page + 1, tags, added, removed); + } else { + $scope.repositoryTags = tags; + $scope.imageLoader.reset(); + + $timeout(function() { + $scope.tagChanged({ + 'data': { 'added': added, 'removed': removed } + }); + }, 1); + } }); }; + var markChanged = function(added, removed) { + // Reload the tags + tags = {}; + reloadTags(1, tags, added, removed); + }; + $scope.alertOnTagOpsDisabled = function() { if ($scope.repository.tag_operations_disabled) { $('#tagOperationsDisabledModal').modal('show'); @@ -46,17 +69,17 @@ angular.module('quay').directive('tagOperationsDialog', function () { }; $scope.isAnotherImageTag = function(image, tag) { - if (!$scope.repository.tags) { return; } + if (!$scope.repositoryTags) { return; } - var found = $scope.repository.tags[tag]; + var found = $scope.repositoryTags[tag]; if (found == null) { return false; } return found.image_id != image; }; $scope.isOwnedTag = function(image, tag) { - if (!$scope.repository.tags) { return; } + if (!$scope.repositoryTags) { return; } - var found = $scope.repository.tags[tag]; + var found = $scope.repositoryTags[tag]; if (found == null) { return false; } return found.image_id == image; }; @@ -227,7 +250,7 @@ angular.module('quay').directive('tagOperationsDialog', function () { actions.push({ 'action': 'delete', 'label': label - }) + }); } }); diff --git a/static/js/directives/ui/tag-specific-image-view.js b/static/js/directives/ui/tag-specific-image-view.js index 26fc7c4b9..68a4085f2 100644 --- a/static/js/directives/ui/tag-specific-image-view.js +++ b/static/js/directives/ui/tag-specific-image-view.js @@ -11,6 +11,7 @@ angular.module('quay').directive('tagSpecificImagesView', function () { restrict: 'C', scope: { 'repository': '=repository', + 'repositoryTags': '=repositoryTags', 'tag': '=tag', 'imageLoader': '=imageLoader', 'imageCutoff': '=imageCutoff' @@ -22,7 +23,7 @@ angular.module('quay').directive('tagSpecificImagesView', function () { $scope.getImageListingClasses = function(image) { var classes = ''; - if (!$scope.repository) { + if (!$scope.repositoryTags) { return ''; } @@ -30,7 +31,7 @@ angular.module('quay').directive('tagSpecificImagesView', function () { classes += 'child '; } - var currentTag = $scope.repository.tags[$scope.tag]; + var currentTag = $scope.repositoryTags[$scope.tag]; if (currentTag && image.id == currentTag.image_id) { classes += 'tag-image '; } @@ -39,12 +40,12 @@ angular.module('quay').directive('tagSpecificImagesView', function () { }; var refresh = function() { - if (!$scope.repository || !$scope.tag || !$scope.imageLoader) { + if (!$scope.repositoryTags || !$scope.tag || !$scope.imageLoader) { $scope.tagSpecificImages = []; return; } - var tag = $scope.repository.tags[$scope.tag]; + var tag = $scope.repositoryTags[$scope.tag]; if (!tag) { $scope.tagSpecificImages = []; return; @@ -58,6 +59,7 @@ angular.module('quay').directive('tagSpecificImagesView', function () { }; $scope.$watch('repository', refresh); + $scope.$watch('repositoryTags', refresh); $scope.$watch('tag', refresh); } }; diff --git a/static/js/pages/repo-view.js b/static/js/pages/repo-view.js index a7dcba245..c3b04ba8d 100644 --- a/static/js/pages/repo-view.js +++ b/static/js/pages/repo-view.js @@ -30,7 +30,8 @@ 'repository': null, 'imageLoader': imageLoader, 'builds': null, - 'historyFilter': '' + 'historyFilter': '', + 'repositoryTags': null }; $scope.repositoryTags = {}; @@ -41,19 +42,20 @@ UserService.updateUserIn($scope); // Watch the repository to filter any tags removed. - $scope.$watch('viewScope.repository', function(repository) { + $scope.$watch('viewScope.repositoryTags', 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]; + return !!$scope.viewScope.repositoryTags[tag]; }); }; var loadRepositoryTags = function() { loadPaginatedRepositoryTags(1); + $scope.viewScope.repositoryTags = $scope.repositoryTags; }; var loadPaginatedRepositoryTags = function(page) { @@ -82,6 +84,7 @@ var loadRepository = function() { // Mark the images to be reloaded. $scope.viewScope.images = null; + loadRepositoryTags(); var params = { 'repository': $scope.namespace + '/' + $scope.name, @@ -91,9 +94,6 @@ $scope.repositoryResource = ApiService.getRepoAsResource(params).get(function(repo) { if (repo != undefined) { - loadRepositoryTags(); - repo.tags = $scope.repositoryTags; - $scope.repository = repo; $scope.viewScope.repository = repo; diff --git a/static/partials/repo-view.html b/static/partials/repo-view.html index dbbb6e28a..046f63fc1 100644 --- a/static/partials/repo-view.html +++ b/static/partials/repo-view.html @@ -76,6 +76,7 @@