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 ---
This commit is contained in:
		
							parent
							
								
									bb01e08d44
								
							
						
					
					
						commit
						8b25d5b77b
					
				
					 7 changed files with 69 additions and 37 deletions
				
			
		|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 | ||||
|             }) | ||||
|             }); | ||||
|           } | ||||
|         }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
|   }; | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
		Reference in a new issue