Switch avatars to be built out of CSS and only overlayed with the gravatar when a non-default exists

This commit is contained in:
Joseph Schorr 2015-03-30 17:55:04 -04:00
parent 2d8d0c6fd3
commit 27a9b84587
94 changed files with 663 additions and 303 deletions

View file

@ -0,0 +1,17 @@
/**
* Adds a ng-image-watch attribute, which is a callback invoked when the image is loaded or fails.
*/
angular.module('quay').directive('ngImageWatch', function () {
return {
restrict: 'A',
link: function postLink($scope, $element, $attr) {
$element.bind('error', function() {
$scope.$eval($attr.ngImageWatch)(false);
});
$element.bind('load', function() {
$scope.$eval($attr.ngImageWatch)(true);
});
}
};
});

View file

@ -120,6 +120,8 @@ angular.module('quay').directive('quayClasses', function(Features, Config) {
/**
* Adds a quay-include attribtue that adds a template solely if the expression evaluates to true.
* Automatically adds the Features and Config services to the scope.
*
Usage: quay-include="{'Features.BILLING': 'partials/landing-normal.html', '!Features.BILLING': 'partials/landing-login.html'}"
*/
angular.module('quay').directive('quayInclude', function($compile, $templateCache, $http, Features, Config) {
return {
@ -127,7 +129,7 @@ angular.module('quay').directive('quayInclude', function($compile, $templateCach
restrict: 'A',
link: function($scope, $element, $attr, ctrl) {
var getTemplate = function(templateName) {
var templateUrl = '/static/partials/' + templateName;
var templateUrl = '/static/' + templateName;
return $http.get(templateUrl, {cache: $templateCache});
};

View file

@ -0,0 +1,19 @@
/**
* An element which displays its contents wrapped in an <a> tag, but only if the href is not null.
*/
angular.module('quay').directive('anchor', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/anchor.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'href': '@href',
'isOnlyText': '=isOnlyText'
},
controller: function($scope, $element) {
}
};
return directiveDefinitionObject;
});

View file

@ -1,5 +1,5 @@
/**
* An element which displays an avatar for the given {email,name} or hash.
* An element which displays an avatar for the given avatar data.
*/
angular.module('quay').directive('avatar', function () {
var directiveDefinitionObject = {
@ -9,25 +9,36 @@ angular.module('quay').directive('avatar', function () {
transclude: true,
restrict: 'C',
scope: {
'hash': '=hash',
'email': '=email',
'name': '=name',
'data': '=data',
'size': '=size'
},
controller: function($scope, $element, AvatarService) {
controller: function($scope, $element, AvatarService, Config, UIService) {
$scope.AvatarService = AvatarService;
$scope.Config = Config;
$scope.isLoading = true;
$scope.hasGravatar = false;
$scope.loadGravatar = false;
var refreshHash = function() {
if (!$scope.name && !$scope.email) { return; }
$scope._hash = AvatarService.computeHash($scope.email, $scope.name);
$scope.imageCallback = function(r) {
$scope.isLoading = false;
$scope.hasGravatar = r;
};
$scope.$watch('hash', function(hash) {
$scope._hash = hash;
$scope.$watch('size', function(size) {
size = size * 1 || 16;
$scope.fontSize = (size - 4) + 'px';
$scope.lineHeight = size + 'px';
});
$scope.$watch('name', refreshHash);
$scope.$watch('email', refreshHash);
$scope.$watch('data', function(data) {
if (!data) { return; }
$scope.loadGravatar = Config.AVATAR_KIND == 'gravatar' &&
(data.kind == 'user' || data.kind == 'org');
$scope.isLoading = $scope.loadGravatar;
$scope.hasGravatar = false;
});
}
};
return directiveDefinitionObject;

View file

@ -39,6 +39,21 @@ angular.module('quay').directive('entityReference', function () {
return '/organization/' + org['name'] + '/admin?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
};
$scope.getTitle = function(entity) {
if (!entity) { return ''; }
switch (entity.kind) {
case 'org':
return 'Organization';
case 'team':
return 'Team';
case 'user':
return entity.is_robot ? 'Robot Account' : 'User';
}
};
$scope.getPrefix = function(name) {
if (!name) { return ''; }
var plus = name.indexOf('+');

View file

@ -56,6 +56,8 @@ angular.module('quay').directive('entitySearch', function () {
$scope.currentEntityInternal = $scope.currentEntity;
$scope.Config = Config;
var isSupported = function(kind, opt_array) {
return $.inArray(kind, opt_array || $scope.allowedEntities || ['user', 'team', 'robot']) >= 0;
};
@ -102,7 +104,7 @@ angular.module('quay').directive('entitySearch', function () {
}
CreateService.createOrganizationTeam(ApiService, $scope.namespace, teamname, function(created) {
$scope.setEntity(created.name, 'team', false);
$scope.setEntity(created.name, 'team', false, created.avatar);
$scope.teams[teamname] = created;
});
});
@ -121,17 +123,18 @@ angular.module('quay').directive('entitySearch', function () {
}
CreateService.createRobotAccount(ApiService, $scope.isOrganization, $scope.namespace, robotname, function(created) {
$scope.setEntity(created.name, 'user', true);
$scope.setEntity(created.name, 'user', true, created.avatar);
$scope.robots.push(created);
});
});
};
$scope.setEntity = function(name, kind, is_robot) {
$scope.setEntity = function(name, kind, is_robot, avatar) {
var entity = {
'name': name,
'kind': kind,
'is_robot': is_robot
'is_robot': is_robot,
'avatar': avatar
};
if ($scope.isOrganization) {

View file

@ -68,7 +68,8 @@ angular.module('quay').directive('repositoryPermissionsTable', function () {
'kind': kind,
'name': name,
'is_robot': permission.is_robot,
'is_org_member': permission.is_org_member
'is_org_member': permission.is_org_member,
'avatar': permission.avatar
};
};

View file

@ -19,6 +19,45 @@ angular.module('quay').directive('teamsManager', function () {
{ 'id': 'admin', 'title': 'Admin', 'kind': 'primary' }
];
$scope.members = {};
$scope.orderedTeams = [];
var loadTeamMembers = function() {
if (!$scope.organization) { return; }
for (var name in $scope.organization.teams) {
if (!$scope.organization.teams.hasOwnProperty(name)) { continue; }
loadMembersOfTeam(name);
}
};
var loadMembersOfTeam = function(name) {
var params = {
'orgname': $scope.organization.name,
'teamname': name
};
$scope.members[name] = {};
ApiService.getOrganizationTeamMembers(null, params).then(function(resp) {
$scope.members[name].members = resp.members;
}, function() {
delete $scope.members[name];
});
};
var loadOrderedTeams = function() {
if (!$scope.organization) { return; }
$scope.orderedTeams = [];
$scope.organization.ordered_teams.map(function(name) {
$scope.orderedTeams.push($scope.organization.teams[name]);
});
};
$scope.$watch('organization', loadOrderedTeams);
$scope.$watch('organization', loadTeamMembers);
$scope.setRole = function(role, teamname) {
var previousRole = $scope.organization.teams[teamname].role;
$scope.organization.teams[teamname].role = role;
@ -54,6 +93,10 @@ angular.module('quay').directive('teamsManager', function () {
var orgname = $scope.organization.name;
CreateService.createOrganizationTeam(ApiService, orgname, teamname, function(created) {
$scope.organization.teams[teamname] = created;
$scope.members[teamname] = {};
$scope.members[teamname].members = [];
$scope.organization.ordered_teams.push(teamname);
$scope.orderedTeams.push(created);
});
};
@ -72,6 +115,12 @@ angular.module('quay').directive('teamsManager', function () {
};
ApiService.deleteOrganizationTeam(null, params).then(function() {
var index = $scope.organization.ordered_teams.indexOf(teamname);
if (index >= 0) {
$scope.organization.ordered_teams.splice(index, 1);
}
loadOrderedTeams();
delete $scope.organization.teams[teamname];
}, ApiService.errorDisplay('Cannot delete team'));
};

View file

@ -14,7 +14,9 @@ angular.module('quay').factory('AvatarService', ['Config', '$sanitize', 'md5',
break;
case 'gravatar':
return '//www.gravatar.com/avatar/' + hash + '?d=identicon&size=' + size;
// TODO(jschorr): Remove once the new layout is in place everywhere.
var default_kind = Config.isNewLayout() ? '404' : 'identicon';
return '//www.gravatar.com/avatar/' + hash + '?d=' + default_kind + '&size=' + size;
break;
}
};

View file

@ -71,5 +71,10 @@ angular.module('quay').factory('Config', [function() {
return value;
};
config.isNewLayout = function() {
// TODO(jschorr): Remove once new layout is in place for everyone.
return document.cookie.toString().indexOf('quay.exp-new-layout=true') >= 0;
};
return config;
}]);