Start on new tag view
This commit is contained in:
parent
581a284744
commit
afc8e95e19
103 changed files with 148505 additions and 458 deletions
|
@ -162,6 +162,51 @@ angular.module("core-ui", [])
|
|||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corConfirmDialog', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 1,
|
||||
templateUrl: '/static/directives/cor-confirm-dialog.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'dialogTitle': '@dialogTitle',
|
||||
'dialogActionTitle': '@dialogActionTitle',
|
||||
|
||||
'dialogContext': '=dialogContext',
|
||||
'dialogAction': '&dialogAction'
|
||||
},
|
||||
controller: function($rootScope, $scope, $element) {
|
||||
$scope.working = false;
|
||||
|
||||
$scope.$watch('dialogContext', function(dc) {
|
||||
if (!dc) { return; }
|
||||
$scope.show();
|
||||
});
|
||||
|
||||
$scope.performAction = function() {
|
||||
$scope.working = true;
|
||||
$scope.dialogAction({
|
||||
'info': $scope.dialogContext,
|
||||
'callback': function(result) {
|
||||
$scope.hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.show = function() {
|
||||
$scope.working = false;
|
||||
$element.find('.modal').modal({});
|
||||
};
|
||||
|
||||
$scope.hide = function() {
|
||||
$element.find('.modal').modal('hide');
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corTabPanel', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 1,
|
||||
|
@ -581,4 +626,84 @@ angular.module("core-ui", [])
|
|||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corCheckableMenu', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 1,
|
||||
templateUrl: '/static/directives/cor-checkable-menu.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'controller': '=controller'
|
||||
},
|
||||
controller: function($rootScope, $scope, $element) {
|
||||
$scope.getClass = function(items, checked) {
|
||||
if (checked.length == 0) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
if (checked.length == items.length) {
|
||||
return 'all';
|
||||
}
|
||||
|
||||
return 'some';
|
||||
};
|
||||
|
||||
$scope.toggleItems = function($event) {
|
||||
$event.stopPropagation();
|
||||
$scope.controller.toggleItems();
|
||||
};
|
||||
|
||||
this.checkByFilter = function(filter) {
|
||||
$scope.controller.checkByFilter(filter);
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corCheckableMenuItem', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 1,
|
||||
templateUrl: '/static/directives/cor-checkable-menu-item.html',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
require: '^corCheckableMenu',
|
||||
scope: {
|
||||
'itemFilter': '=itemFilter'
|
||||
},
|
||||
link: function($scope, $element, $attr, parent) {
|
||||
$scope.parent = parent;
|
||||
},
|
||||
|
||||
controller: function($rootScope, $scope, $element) {
|
||||
$scope.selected = function() {
|
||||
$scope.parent.checkByFilter(this.itemFilter);
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corCheckableItem', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 1,
|
||||
templateUrl: '/static/directives/cor-checkable-item.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'item': '=item',
|
||||
'controller': '=controller'
|
||||
},
|
||||
controller: function($rootScope, $scope, $element) {
|
||||
$scope.toggleItem = function() {
|
||||
$scope.controller.toggleItem($scope.item);
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
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';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,38 +52,14 @@
|
|||
return !!UserService.getOrganization(namespace);
|
||||
};
|
||||
|
||||
// Star a repository or unstar a repository.
|
||||
$scope.toggleStar = function(repo) {
|
||||
$scope.starToggled = function(repo) {
|
||||
if (repo.is_starred) {
|
||||
unstarRepo(repo);
|
||||
} else {
|
||||
starRepo(repo);
|
||||
}
|
||||
}
|
||||
|
||||
// Star a repository and update the UI.
|
||||
var starRepo = function(repo) {
|
||||
var data = {
|
||||
'namespace': repo.namespace,
|
||||
'repository': repo.name
|
||||
};
|
||||
ApiService.createStar(data).then(function(result) {
|
||||
repo.is_starred = true;
|
||||
$scope.starred_repositories.value.push(repo);
|
||||
}, ApiService.errorDisplay('Could not star repository'));
|
||||
};
|
||||
|
||||
// Unstar a repository and update the UI.
|
||||
var unstarRepo = function(repo) {
|
||||
var data = {
|
||||
'repository': repo.namespace + '/' + repo.name
|
||||
};
|
||||
ApiService.deleteStar(null, data).then(function(result) {
|
||||
repo.is_starred = false;
|
||||
} else {
|
||||
$scope.starred_repositories.value = $scope.starred_repositories.value.filter(function(repo) {
|
||||
return repo.is_starred;
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not unstar repository'));
|
||||
}
|
||||
};
|
||||
|
||||
// Finds a duplicate repo if it exists. If it doesn't, inserts the repo.
|
||||
|
|
|
@ -3,10 +3,49 @@
|
|||
* Repository view page.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('repo-view', 'repo-view.html', RepoCtrl);
|
||||
pages.create('repo-view', 'repo-view.html', RepoViewCtrl, {
|
||||
'newLayout': true,
|
||||
'title': '{{ namespace }}/{{ name }}',
|
||||
'description': 'Repository {{ namespace }}/{{ name }}'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('repo-view', 'old-repo-view.html', OldRepoViewCtrl, {
|
||||
}, ['old-layout']);
|
||||
}]);
|
||||
|
||||
function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiService, $routeParams, $rootScope, $location, $timeout, Config, UtilService) {
|
||||
function RepoViewCtrl($scope, $routeParams, ApiService, UserService) {
|
||||
$scope.namespace = $routeParams.namespace;
|
||||
$scope.name = $routeParams.name;
|
||||
|
||||
$scope.logsShown = 0;
|
||||
|
||||
// Make sure we track the current user.
|
||||
UserService.updateUserIn($scope);
|
||||
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
// Load the repository.
|
||||
loadRepository();
|
||||
|
||||
$scope.setTag = function(tagName) {
|
||||
window.console.log('set tag')
|
||||
};
|
||||
|
||||
$scope.showLogs = function() {
|
||||
$scope.logsShown++;
|
||||
};
|
||||
}
|
||||
|
||||
function OldRepoViewCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiService, $routeParams, $rootScope, $location, $timeout, Config, UtilService) {
|
||||
$scope.Config = Config;
|
||||
|
||||
var namespace = $routeParams.namespace;
|
||||
|
|
|
@ -2,6 +2,46 @@
|
|||
* Service which provides helper methods for performing some simple UI operations.
|
||||
*/
|
||||
angular.module('quay').factory('UIService', [function() {
|
||||
var CheckStateController = function(items) {
|
||||
this.items = items;
|
||||
this.checked = [];
|
||||
};
|
||||
|
||||
CheckStateController.prototype.isChecked = function(item) {
|
||||
return $.inArray(item, this.checked) >= 0;
|
||||
};
|
||||
|
||||
CheckStateController.prototype.toggleItem = function(item) {
|
||||
if (this.isChecked(item)) {
|
||||
this.uncheckItem(item);
|
||||
} else {
|
||||
this.checkItem(item);
|
||||
}
|
||||
};
|
||||
|
||||
CheckStateController.prototype.toggleItems = function() {
|
||||
if (this.checked.length) {
|
||||
this.checked = [];
|
||||
} else {
|
||||
this.checked = this.items.slice();
|
||||
}
|
||||
};
|
||||
|
||||
CheckStateController.prototype.checkByFilter = function(filter) {
|
||||
this.checked = $.grep(this.items, filter);
|
||||
};
|
||||
|
||||
CheckStateController.prototype.checkItem = function(item) {
|
||||
this.checked.push(item);
|
||||
};
|
||||
|
||||
CheckStateController.prototype.uncheckItem = function(item) {
|
||||
this.checked = $.grep(this.checked, function(cItem) {
|
||||
return cItem != item;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
var uiService = {};
|
||||
|
||||
uiService.hidePopover = function(elem) {
|
||||
|
@ -33,5 +73,9 @@ angular.module('quay').factory('UIService', [function() {
|
|||
}
|
||||
};
|
||||
|
||||
uiService.createCheckStateController = function(items) {
|
||||
return new CheckStateController(items);
|
||||
};
|
||||
|
||||
return uiService;
|
||||
}]);
|
||||
|
|
Reference in a new issue