Start on new tag view
This commit is contained in:
parent
581a284744
commit
afc8e95e19
103 changed files with 148505 additions and 458 deletions
23
static/js/directives/repo-view/repo-panel-info.js
Normal file
23
static/js/directives/repo-view/repo-panel-info.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* An element which displays the information panel for a repository view.
|
||||
*/
|
||||
angular.module('quay').directive('repoPanelInfo', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/repo-view/repo-panel-info.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository'
|
||||
},
|
||||
controller: function($scope, $element, ApiService) {
|
||||
$scope.updateDescription = function(content) {
|
||||
$scope.repository.description = content;
|
||||
$scope.repository.put();
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
222
static/js/directives/repo-view/repo-panel-tags.js
Normal file
222
static/js/directives/repo-view/repo-panel-tags.js
Normal file
|
@ -0,0 +1,222 @@
|
|||
/**
|
||||
* An element which displays the tags panel for a repository view.
|
||||
*/
|
||||
angular.module('quay').directive('repoPanelTags', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/repo-view/repo-panel-tags.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository',
|
||||
'repositoryUpdated': '&repositoryUpdated'
|
||||
},
|
||||
controller: function($scope, $element, $filter, ApiService, UIService) {
|
||||
var orderBy = $filter('orderBy');
|
||||
|
||||
$scope.checkedTags = UIService.createCheckStateController([]);
|
||||
$scope.options = {
|
||||
'predicate': 'last_modified_datetime',
|
||||
'reverse': false
|
||||
};
|
||||
|
||||
$scope.iterationState = {};
|
||||
|
||||
var loadImages = function() {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||
};
|
||||
|
||||
$scope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) {
|
||||
$scope.images = resp.images;
|
||||
});
|
||||
};
|
||||
|
||||
var setTagState = function() {
|
||||
if (!$scope.repository) { return; }
|
||||
|
||||
var tags = [];
|
||||
var allTags = [];
|
||||
var imageMap = {};
|
||||
var imageTracks = [];
|
||||
|
||||
// Build a list of tags and filtered tags.
|
||||
for (var tag in $scope.repository.tags) {
|
||||
if (!$scope.repository.tags.hasOwnProperty(tag)) { continue; }
|
||||
|
||||
var tagInfo = $.extend($scope.repository.tags[tag], {
|
||||
'name': tag,
|
||||
'last_modified_datetime': new Date($scope.repository.tags[tag].last_modified)
|
||||
});
|
||||
|
||||
allTags.push(tagInfo);
|
||||
|
||||
if (!$scope.options.tagFilter || tag.indexOf($scope.options.tagFilter) >= 0 ||
|
||||
tagInfo.image_id.indexOf($scope.options.tagFilter) >= 0) {
|
||||
tags.push(tagInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the tags by the predicate and the reverse, and map the information.
|
||||
var ordered = orderBy(tags, $scope.options.predicate, $scope.options.reverse);
|
||||
for (var i = 0; i < ordered.length; ++i) {
|
||||
var tagInfo = ordered[i];
|
||||
if (!imageMap[tagInfo.image_id]) {
|
||||
imageMap[tagInfo.image_id] = [];
|
||||
}
|
||||
|
||||
imageMap[tagInfo.image_id].push(tagInfo);
|
||||
};
|
||||
|
||||
// Calculate the image tracks.
|
||||
var colors = d3.scale.category10();
|
||||
var index = 0;
|
||||
|
||||
for (var image_id in imageMap) {
|
||||
if (imageMap[image_id].length >= 2){
|
||||
imageTracks.push({
|
||||
'image_id': image_id,
|
||||
'color': colors(index),
|
||||
'count': imageMap[image_id].length,
|
||||
'tags': imageMap[image_id]
|
||||
});
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.imageMap = imageMap;
|
||||
$scope.imageTracks = imageTracks;
|
||||
$scope.tags = ordered;
|
||||
$scope.checkedTags = UIService.createCheckStateController(ordered);
|
||||
$scope.allTags = allTags;
|
||||
$scope.iterationState = {};
|
||||
}
|
||||
|
||||
$scope.$watch('options.predicate', setTagState);
|
||||
$scope.$watch('options.reverse', setTagState);
|
||||
$scope.$watch('options.tagFilter', setTagState);
|
||||
$scope.$watch('repository', function(repository) {
|
||||
if (!repository) { return; }
|
||||
|
||||
// Load the repository's images.
|
||||
loadImages();
|
||||
|
||||
// Process each of the tags.
|
||||
setTagState();
|
||||
});
|
||||
|
||||
$scope.trackLineClass = function(index, track_info) {
|
||||
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
|
||||
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
|
||||
|
||||
if (index == startIndex) {
|
||||
return 'start';
|
||||
}
|
||||
|
||||
if (index == endIndex) {
|
||||
return 'end';
|
||||
}
|
||||
|
||||
if (index > startIndex && index < endIndex) {
|
||||
return 'middle';
|
||||
}
|
||||
|
||||
if (index < startIndex) {
|
||||
return 'before';
|
||||
}
|
||||
|
||||
if (index > endIndex) {
|
||||
return 'after';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.tablePredicateClass = function(name, predicate, reverse) {
|
||||
if (name != predicate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'current ' + (reverse ? 'reversed' : '');
|
||||
};
|
||||
|
||||
$scope.askDeleteTag = function(tag) {
|
||||
$scope.deleteTagInfo = {
|
||||
'tag': tag
|
||||
};
|
||||
};
|
||||
|
||||
$scope.askDeleteMultipleTags = function(tags) {
|
||||
$scope.deleteMultipleTagsInfo = {
|
||||
'tags': tags
|
||||
};
|
||||
};
|
||||
|
||||
$scope.deleteMultipleTags = function(tags, callback) {
|
||||
var count = tags.length;
|
||||
var perform = function(index) {
|
||||
if (index >= count) {
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var tag_info = tags[index];
|
||||
$scope.deleteTag(tag_info.name, function(result) {
|
||||
if (!result) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
perform(index + 1);
|
||||
});
|
||||
};
|
||||
|
||||
perform(0);
|
||||
};
|
||||
|
||||
$scope.deleteTag = function(tag, callback) {
|
||||
if (!$scope.repository.can_admin) { return; }
|
||||
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'tag': tag
|
||||
};
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot delete tag', callback);
|
||||
ApiService.deleteFullTag(null, params).then(function() {
|
||||
callback(true);
|
||||
$scope.tags = $.grep($scope.tags, function(tagInfo) {
|
||||
return tagInfo.name != tag;
|
||||
});
|
||||
|
||||
$scope.checkedTags = UIService.createCheckStateController($scope.tags);
|
||||
$scope.repositoryUpdated({});
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.orderBy = function(predicate) {
|
||||
if (predicate == $scope.options.predicate) {
|
||||
$scope.options.reverse = !$scope.options.reverse;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.options.reverse = false;
|
||||
$scope.options.predicate = predicate;
|
||||
};
|
||||
|
||||
$scope.commitTagFilter = function(tag) {
|
||||
var r = new RegExp('^[0-9a-f]{7}$');
|
||||
return tag.name.match(r);
|
||||
};
|
||||
|
||||
$scope.allTagFilter = function(tag) {
|
||||
return true;
|
||||
};
|
||||
|
||||
$scope.noTagFilter = function(tag) {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
|
@ -13,7 +13,7 @@ angular.module('quay').directive('repoListGrid', function () {
|
|||
starred: '=starred',
|
||||
user: "=user",
|
||||
namespace: '=namespace',
|
||||
toggleStar: '&toggleStar'
|
||||
starToggled: '&starToggled'
|
||||
},
|
||||
controller: function($scope, $element, UserService) {
|
||||
$scope.isOrganization = function(namespace) {
|
||||
|
|
53
static/js/directives/ui/repo-star.js
Normal file
53
static/js/directives/ui/repo-star.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* An element that displays the star status of a repository and allows it to be toggled.
|
||||
*/
|
||||
angular.module('quay').directive('repoStar', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/repo-star.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
repository: '=repository',
|
||||
starToggled: '&starToggled'
|
||||
},
|
||||
controller: function($scope, $element, UserService, ApiService) {
|
||||
// Star a repository or unstar a repository.
|
||||
$scope.toggleStar = function() {
|
||||
if ($scope.repository.is_starred) {
|
||||
unstarRepo();
|
||||
} else {
|
||||
starRepo();
|
||||
}
|
||||
};
|
||||
|
||||
// Star a repository and update the UI.
|
||||
var starRepo = function() {
|
||||
var data = {
|
||||
'namespace': $scope.repository.namespace,
|
||||
'repository': $scope.repository.name
|
||||
};
|
||||
|
||||
ApiService.createStar(data).then(function(result) {
|
||||
$scope.repository.is_starred = true;
|
||||
$scope.starToggled({'repository': $scope.repository});
|
||||
}, ApiService.errorDisplay('Could not star repository'));
|
||||
};
|
||||
|
||||
// Unstar a repository and update the UI.
|
||||
var unstarRepo = function(repo) {
|
||||
var data = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||
};
|
||||
|
||||
ApiService.deleteStar(null, data).then(function(result) {
|
||||
$scope.repository.is_starred = false;
|
||||
$scope.starToggled({'repository': $scope.repository});
|
||||
}, ApiService.errorDisplay('Could not unstar repository'));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -27,7 +27,7 @@ angular.module('quay').directive('resourceView', function () {
|
|||
return 'loading';
|
||||
}
|
||||
|
||||
if (current.error) {
|
||||
if (current.hasError) {
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue