Merge pull request #3060 from quay/max-results-help-text
Configurable options for search, disable next page & add help text when at max results
This commit is contained in:
commit
f89ad30320
7 changed files with 50 additions and 9 deletions
|
@ -23,7 +23,8 @@ CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'MIXPANEL_KEY',
|
||||||
'CONTACT_INFO', 'AVATAR_KIND', 'LOCAL_OAUTH_HANDLER', 'DOCUMENTATION_LOCATION',
|
'CONTACT_INFO', 'AVATAR_KIND', 'LOCAL_OAUTH_HANDLER', 'DOCUMENTATION_LOCATION',
|
||||||
'DOCUMENTATION_METADATA', 'SETUP_COMPLETE', 'DEBUG', 'MARKETO_MUNCHKIN_ID',
|
'DOCUMENTATION_METADATA', 'SETUP_COMPLETE', 'DEBUG', 'MARKETO_MUNCHKIN_ID',
|
||||||
'STATIC_SITE_BUCKET', 'RECAPTCHA_SITE_KEY', 'CHANNEL_COLORS',
|
'STATIC_SITE_BUCKET', 'RECAPTCHA_SITE_KEY', 'CHANNEL_COLORS',
|
||||||
'TAG_EXPIRATION_OPTIONS', 'INTERNAL_OIDC_SERVICE_ID']
|
'TAG_EXPIRATION_OPTIONS', 'INTERNAL_OIDC_SERVICE_ID',
|
||||||
|
'SEARCH_RESULTS_PER_PAGE', 'SEARCH_MAX_RESULT_PAGE_COUNT']
|
||||||
|
|
||||||
|
|
||||||
def frontend_visible_config(config_dict):
|
def frontend_visible_config(config_dict):
|
||||||
|
@ -535,3 +536,9 @@ class DefaultConfig(ImmutableConfig):
|
||||||
# Defines the delay required (in seconds) before the last_accessed field of a user/robot or access
|
# Defines the delay required (in seconds) before the last_accessed field of a user/robot or access
|
||||||
# token will be updated after the previous update.
|
# token will be updated after the previous update.
|
||||||
LAST_ACCESSED_UPDATE_THRESHOLD_S = 60
|
LAST_ACCESSED_UPDATE_THRESHOLD_S = 60
|
||||||
|
|
||||||
|
# Defines the number of results per page used to show search results
|
||||||
|
SEARCH_RESULTS_PER_PAGE = 10
|
||||||
|
|
||||||
|
# Defines the maximum number of pages the user can paginate before they are limited
|
||||||
|
SEARCH_MAX_RESULT_PAGE_COUNT = 10
|
||||||
|
|
|
@ -12,7 +12,7 @@ from auth.permissions import (OrganizationMemberPermission, ReadRepositoryPermis
|
||||||
ReadRepositoryPermission)
|
ReadRepositoryPermission)
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
from app import avatar, authentication
|
from app import app, avatar, authentication
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from stringscore import liquidmetal
|
from stringscore import liquidmetal
|
||||||
|
@ -335,7 +335,8 @@ class ConductSearch(ApiResource):
|
||||||
return {'results': sorted(results, key=itemgetter('score'), reverse=True)}
|
return {'results': sorted(results, key=itemgetter('score'), reverse=True)}
|
||||||
|
|
||||||
|
|
||||||
MAX_PER_PAGE = 10
|
MAX_PER_PAGE = app.config.get('SEARCH_RESULTS_PER_PAGE', 10)
|
||||||
|
MAX_RESULT_PAGE_COUNT = app.config.get('SEARCH_MAX_RESULT_PAGE_COUNT', 10)
|
||||||
|
|
||||||
@resource('/v1/find/repositories')
|
@resource('/v1/find/repositories')
|
||||||
class ConductRepositorySearch(ApiResource):
|
class ConductRepositorySearch(ApiResource):
|
||||||
|
@ -347,7 +348,7 @@ class ConductRepositorySearch(ApiResource):
|
||||||
def get(self, parsed_args):
|
def get(self, parsed_args):
|
||||||
""" Get a list of apps and repositories that match the specified query. """
|
""" Get a list of apps and repositories that match the specified query. """
|
||||||
query = parsed_args['query']
|
query = parsed_args['query']
|
||||||
page = min(max(1, parsed_args['page']), 10)
|
page = min(max(1, parsed_args['page']), MAX_RESULT_PAGE_COUNT)
|
||||||
offset = (page - 1) * MAX_PER_PAGE
|
offset = (page - 1) * MAX_PER_PAGE
|
||||||
limit = offset + MAX_PER_PAGE + 1
|
limit = offset + MAX_PER_PAGE + 1
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,20 @@
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.search .search-max-results-info {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search .page-navigation-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search .page-navigation-wrapper :first-child {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
.search {
|
.search {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
function SearchCtrl($scope, ApiService, $routeParams, $location) {
|
function SearchCtrl($scope, ApiService, $routeParams, $location, Config) {
|
||||||
var refreshResults = function() {
|
var refreshResults = function() {
|
||||||
$scope.currentPage = ($routeParams['page'] || '1') * 1;
|
$scope.currentPage = ($routeParams['page'] || '1') * 1;
|
||||||
|
|
||||||
|
@ -17,10 +17,16 @@
|
||||||
'page': $scope.currentPage
|
'page': $scope.currentPage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var MAX_PAGE_RESULTS = Config['SEARCH_MAX_RESULT_PAGE_COUNT'];
|
||||||
|
var page = $routeParams['page'] || 1;
|
||||||
|
|
||||||
$scope.maxPopularity = 0;
|
$scope.maxPopularity = 0;
|
||||||
$scope.resultsResource = ApiService.conductRepoSearchAsResource(params).get(function(resp) {
|
$scope.resultsResource = ApiService.conductRepoSearchAsResource(params).get(function(resp) {
|
||||||
$scope.results = resp['results'];
|
$scope.results = resp['results'];
|
||||||
$scope.hasAdditional = resp['has_additional'];
|
// Only show "Next Page" if we have more results, and we aren't on the max page
|
||||||
|
$scope.showNextButton = page < MAX_PAGE_RESULTS && resp['has_additional'];
|
||||||
|
// Show some help text if we're on the last page, making them specify the search more
|
||||||
|
$scope.showMaxResultsHelpText = page >= MAX_PAGE_RESULTS;
|
||||||
$scope.startIndex = resp['start_index'];
|
$scope.startIndex = resp['start_index'];
|
||||||
resp['results'].forEach(function(result) {
|
resp['results'].forEach(function(result) {
|
||||||
$scope.maxPopularity = Math.max($scope.maxPopularity, result['popularity']);
|
$scope.maxPopularity = Math.max($scope.maxPopularity, result['popularity']);
|
||||||
|
@ -45,5 +51,5 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchCtrl.$inject = ['$scope', 'ApiService', '$routeParams', '$location'];
|
SearchCtrl.$inject = ['$scope', 'ApiService', '$routeParams', '$location', 'Config'];
|
||||||
})();
|
})();
|
|
@ -40,8 +40,11 @@
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
<div class="page-navigation-wrapper">
|
||||||
<a class="btn btn-default" ng-click="previousPage()" ng-if="currentPage > 1">Previous Page</a>
|
<a class="btn btn-default" ng-click="previousPage()" ng-if="currentPage > 1">Previous Page</a>
|
||||||
<a class="btn btn-default" ng-click="nextPage()" ng-if="hasAdditional">Next Page</a>
|
<a class="btn btn-default" ng-click="nextPage()" ng-if="showNextButton">Next Page</a>
|
||||||
|
<span class="search-max-results-info" ng-if="showMaxResultsHelpText">You've reached the maximum number of viewable results. Please refine your search.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
Binary file not shown.
|
@ -167,6 +167,16 @@ CONFIG_SCHEMA = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
'SEARCH_RESULTS_PER_PAGE' : {
|
||||||
|
'type': 'number',
|
||||||
|
'description': 'Number of results returned per page by search page. Defaults to 10',
|
||||||
|
'x-example': 10,
|
||||||
|
},
|
||||||
|
'SEARCH_MAX_RESULT_PAGE_COUNT' : {
|
||||||
|
'type': 'number',
|
||||||
|
'description': 'Maximum number of pages the user can paginate in search before they are limited. Defaults to 10',
|
||||||
|
'x-example': 10,
|
||||||
|
},
|
||||||
|
|
||||||
# E-mail.
|
# E-mail.
|
||||||
'FEATURE_MAILING': {
|
'FEATURE_MAILING': {
|
||||||
|
|
Reference in a new issue