Add repository list pagination
Also changes our binds to be bind-once, which should significantly reduce memory and increase performance for large lists Fixes #1856
This commit is contained in:
parent
502fa23d31
commit
e4ad25ea81
6 changed files with 55 additions and 20 deletions
|
@ -1310,6 +1310,10 @@ a:focus {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.co-top-bar {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.co-check-bar .co-checked-actions .btn {
|
.co-check-bar .co-checked-actions .btn {
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
|
@ -1318,21 +1322,25 @@ a:focus {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.co-check-bar .co-filter-box {
|
.co-check-bar .co-filter-box, .co-top-bar .co-filter-box {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.co-check-bar .co-filter-box .page-controls {
|
.co-check-bar .co-filter-box .page-controls, .co-top-bar .co-filter-box .page-controls {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.co-check-bar .co-filter-box input {
|
.co-check-bar .co-filter-box input, .co-top-bar .co-filter-box input {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.co-top-bar .co-filter-box input {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
border-bottom: none !important;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
<div class="repo-list-table-element">
|
<div class="repo-list-table-element">
|
||||||
<div class="cor-loader" ng-if="isLoading"></div>
|
<div class="cor-loader" ng-if="isLoading"></div>
|
||||||
<div ng-if="orderedRepositories.entries.length == 0 && !isLoading">
|
|
||||||
|
<div class="co-top-bar">
|
||||||
|
<span class="co-filter-box">
|
||||||
|
<span class="page-controls" total-count="orderedRepositories.entries.length" current-page="options.page" page-size="reposPerPage"></span>
|
||||||
|
<input class="form-control" type="text" ng-model="options.filter" placeholder="Filter Repositories..." style="margin-right: 10px;">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="orderedRepositories.entries.length == 0 && !isLoading && !options.filter">
|
||||||
<div class="empty-primary-msg" ng-if="namespaces.length != 1">You do not have any viewable repositories.</div>
|
<div class="empty-primary-msg" ng-if="namespaces.length != 1">You do not have any viewable repositories.</div>
|
||||||
<div class="empty-primary-msg" ng-if="namespaces.length == 1">This namespace doesn't have any viewable repositories.</div>
|
<div class="empty-primary-msg" ng-if="namespaces.length == 1">This namespace doesn't have any viewable repositories.</div>
|
||||||
<div class="empty-secondary-msg">Either no repositories exist yet or you may not have permission to view any. If you have permission, try <a href="/new">creating a new repository</a>.</div>
|
<div class="empty-secondary-msg">Either no repositories exist yet or you may not have permission to view any. If you have permission, try <a href="/new">creating a new repository</a>.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="orderedRepositories.entries.length == 0 && !isLoading && options.filter">
|
||||||
|
<div class="empty-primary-msg">No matching repositories</div>
|
||||||
|
<div class="empty-secondary-msg">There are not matching repositories for the entered filter</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table class="co-table" ng-if="orderedRepositories.entries.length && !isLoading">
|
<table class="co-table" ng-if="orderedRepositories.entries.length && !isLoading">
|
||||||
<thead>
|
<thead>
|
||||||
<td class="hidden-xs"
|
<td class="hidden-xs"
|
||||||
|
@ -31,26 +44,26 @@
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="repository in orderedRepositories.entries">
|
<tr ng-repeat="repository in orderedRepositories.entries | slice:(reposPerPage * options.page):(reposPerPage * (options.page + 1))">
|
||||||
<td class="repo-name-icon">
|
<td class="repo-name-icon">
|
||||||
<span class="avatar" size="24" data="getAvatarData(repository.namespace)"></span>
|
<span class="avatar" size="24" data="::getAvatarData(repository.namespace)"></span>
|
||||||
<a href="/repository/{{ repository.namespace }}/{{ repository.name }}">
|
<a href="/repository/{{ ::repository.namespace }}/{{ ::repository.name }}">
|
||||||
<span class="namespace">{{ repository.namespace }}</span>
|
<span class="namespace">{{ ::repository.namespace }}</span>
|
||||||
<span class="name">{{ repository.name }}</span>
|
<span class="name">{{ ::repository.name }}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="last-modified">
|
<td class="last-modified">
|
||||||
<span ng-if="repository.last_modified">
|
<span ng-if="::repository.last_modified">
|
||||||
{{ repository.last_modified * 1000 | amCalendar }}
|
{{ ::repository.last_modified * 1000 | amCalendar }}
|
||||||
</span>
|
</span>
|
||||||
<span class="empty" ng-if="!repository.last_modified">(Empty Repository)</span>
|
<span class="empty" ng-if="::!repository.last_modified">(Empty Repository)</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="popularity hidden-xs">
|
<td class="popularity hidden-xs">
|
||||||
<span class="strength-indicator" value="repository.popularity" maximum="maxPopularity"
|
<span class="strength-indicator" value="::repository.popularity" maximum="::maxPopularity"
|
||||||
log-base="10"></span>
|
log-base="10"></span>
|
||||||
</td>
|
</td>
|
||||||
<td ng-show="loggedIn">
|
<td ng-show="loggedIn">
|
||||||
<span class="repo-star" repository="repository"
|
<span class="repo-star" repository="::repository"
|
||||||
star-toggled="starToggled({'repository': repository})"></span>
|
star-toggled="starToggled({'repository': repository})"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="repo-list-view-element">
|
<div class="repo-list-view-element">
|
||||||
<!-- Toggle -->
|
<!-- Toggle -->
|
||||||
<div class="repo-list-toggleb btn-group" ng-show="!loading">
|
<div class="repo-list-toggleb btn-group" ng-show="!loading && optionAllowed">
|
||||||
<i class="btn btn-default fa fa-th-large" ng-class="!showAsList ? 'active' : ''"
|
<i class="btn btn-default fa fa-th-large" ng-class="!showAsList ? 'active' : ''"
|
||||||
ng-click="setShowAsList(false)" title="Grid View" data-container="body" bs-tooltip></i>
|
ng-click="setShowAsList(false)" title="Grid View" data-container="body" bs-tooltip></i>
|
||||||
<i class="btn btn-default fa fa-th-list" ng-class="showAsList ? 'active' : ''"
|
<i class="btn btn-default fa fa-th-list" ng-class="showAsList ? 'active' : ''"
|
||||||
|
|
|
@ -8,11 +8,15 @@ angular.module('quay').directive('ngImageWatch', function ($parse) {
|
||||||
var fn = $parse(attr['ngImageWatch']);
|
var fn = $parse(attr['ngImageWatch']);
|
||||||
return function(scope, element) {
|
return function(scope, element) {
|
||||||
element.bind('error', function() {
|
element.bind('error', function() {
|
||||||
|
scope.$apply(function() {
|
||||||
fn(scope, {result: false});
|
fn(scope, {result: false});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
element.bind('load', function() {
|
element.bind('load', function() {
|
||||||
|
scope.$apply(function() {
|
||||||
fn(scope, {result: true});
|
fn(scope, {result: true});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,22 @@ angular.module('quay').directive('repoListTable', function () {
|
||||||
controller: function($scope, $element, $filter, TableService, UserService) {
|
controller: function($scope, $element, $filter, TableService, UserService) {
|
||||||
$scope.repositories = null;
|
$scope.repositories = null;
|
||||||
$scope.orderedRepositories = [];
|
$scope.orderedRepositories = [];
|
||||||
|
$scope.reposPerPage = 50;
|
||||||
|
|
||||||
$scope.maxPopularity = 0;
|
$scope.maxPopularity = 0;
|
||||||
$scope.options = {
|
$scope.options = {
|
||||||
'predicate': 'popularity',
|
'predicate': 'popularity',
|
||||||
'reverse': false,
|
'reverse': false,
|
||||||
'filter': null
|
'filter': null,
|
||||||
|
'page': 0
|
||||||
};
|
};
|
||||||
|
|
||||||
var buildOrderedRepositories = function() {
|
var buildOrderedRepositories = function() {
|
||||||
if (!$scope.repositories) { return; }
|
if (!$scope.repositories) { return; }
|
||||||
|
|
||||||
$scope.orderedRepositories = TableService.buildOrderedItems($scope.repositories, $scope.options,
|
$scope.orderedRepositories = TableService.buildOrderedItems($scope.repositories,
|
||||||
[], ['last_modified_datetime', 'popularity'])
|
$scope.options,
|
||||||
|
['namespace', 'name'], ['last_modified_datetime', 'popularity'])
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.tablePredicateClass = function(name, predicate, reverse) {
|
$scope.tablePredicateClass = function(name, predicate, reverse) {
|
||||||
|
@ -70,6 +73,7 @@ angular.module('quay').directive('repoListTable', function () {
|
||||||
|
|
||||||
$scope.$watch('options.predicate', buildOrderedRepositories);
|
$scope.$watch('options.predicate', buildOrderedRepositories);
|
||||||
$scope.$watch('options.reverse', buildOrderedRepositories);
|
$scope.$watch('options.reverse', buildOrderedRepositories);
|
||||||
|
$scope.$watch('options.filter', buildOrderedRepositories);
|
||||||
|
|
||||||
$scope.$watch('repositoriesResources', function(resources) {
|
$scope.$watch('repositoriesResources', function(resources) {
|
||||||
$scope.repositories = [];
|
$scope.repositories = [];
|
||||||
|
|
|
@ -17,6 +17,7 @@ angular.module('quay').directive('repoListView', function () {
|
||||||
$scope.resources = [];
|
$scope.resources = [];
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
$scope.showAsList = CookieService.get('quay.repoview') == 'list';
|
$scope.showAsList = CookieService.get('quay.repoview') == 'list';
|
||||||
|
$scope.optionAllowed = true;
|
||||||
|
|
||||||
$scope.$watch('namespaces', function(namespaces) {
|
$scope.$watch('namespaces', function(namespaces) {
|
||||||
if (!namespaces) { return; }
|
if (!namespaces) { return; }
|
||||||
|
@ -31,6 +32,11 @@ angular.module('quay').directive('repoListView', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.optionAllowed = $scope.resources.length <= 250;
|
||||||
|
if (!$scope.optionAllowed) {
|
||||||
|
$scope.showAsList = true;
|
||||||
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.setShowAsList = function(value) {
|
$scope.setShowAsList = function(value) {
|
||||||
|
|
Reference in a new issue