Ver código fonte

(Re-)implement default avatar using user initials

We use a embedded svg to scale the initials text to its container
size. The user is free to overwrite its initials in the profile form.
Maxime Quandalle 10 anos atrás
pai
commit
765b0168ea

+ 17 - 15
client/components/main/popup.styl

@@ -218,22 +218,24 @@ $popupWidth = 300px
   .pop-over-list
   .pop-over-list
     padding-top: 8px
     padding-top: 8px
 
 
-.mini-profile-info
-  margin-top: 8px
-  min-height: 56px
-  position: relative
+  .miniprofile-header
+    margin-top: 8px
+    min-height: 56px
+    position: relative
 
 
-  .member-large
-    position: absolute
-    top: 2px
-    left: 2px
+    .member
+      position: absolute
+      top: 2px
+      left: 2px
+      height: 50px
+      width: @height
 
 
-  .info
-    margin: 0 0 0 64px
-    word-wrap: break-word
+    .info
+      margin: 0 0 0 64px
+      word-wrap: break-word
 
 
-    h3 a
-      text-decoration: none
+      h3 a
+        text-decoration: none
 
 
-      &:hover
-        text-decoration: underline
+        &:hover
+          text-decoration: underline

+ 9 - 7
client/components/sidebar/sidebar.jade

@@ -47,13 +47,15 @@ template(name="labelsWidget")
         i.fa.fa-plus
         i.fa.fa-plus
 
 
 template(name="memberPopup")
 template(name="memberPopup")
-  .board-member-menu: .mini-profile-info
-    +userAvatar(user=user)
-    .info
-      h3.bottom
-        .js-profile
-          = user.profile.name
-      p.quiet.bottom @#{user.username}
+  .board-member-menu
+    .miniprofile-header
+      +userAvatar(userId=user._id)
+      .info
+        h3.bottom
+          .js-profile
+            = user.profile.name
+        p.quiet.bottom @#{user.username}
+
     if currentUser.isBoardMember
     if currentUser.isBoardMember
       ul.pop-over-list
       ul.pop-over-list
         li
         li

+ 18 - 4
client/components/users/userAvatar.jade

@@ -1,15 +1,22 @@
 template(name="userAvatar")
 template(name="userAvatar")
-  .member.js-member(title="{{userData.profile.fullname}} ({{userData.username}})")
+  a.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}})")
     if userData.profile.avatarUrl
     if userData.profile.avatarUrl
       img.avatar.avatar-image(src=userData.profile.avatarUrl)
       img.avatar.avatar-image(src=userData.profile.avatarUrl)
+    else
+      +userAvatarInitials(userId=userData._id)
+
     if showStatus
     if showStatus
       span.member-presence-status(class=presenceStatusClassName)
       span.member-presence-status(class=presenceStatusClassName)
       span.member-type(class=memberType)
       span.member-type(class=memberType)
 
 
+template(name="userAvatarInitials")
+  svg.avatar.avatar-initials(viewBox="0 0 {{viewPortWidth}} 15")
+    text(x="0" y="13")= initials
+
 template(name="userPopup")
 template(name="userPopup")
   .board-member-menu
   .board-member-menu
     .mini-profile-info
     .mini-profile-info
-      +userAvatar(user=user)
+      +userAvatar(userId=user._id)
       .info
       .info
         h3.bottom
         h3.bottom
           a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
           a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
@@ -25,8 +32,8 @@ template(name="changeAvatarPopup")
   ul.pop-over-list
   ul.pop-over-list
     each uploadedAvatars
     each uploadedAvatars
       li: a.js-select-avatar
       li: a.js-select-avatar
-        .member: .avatar
-          img.avatar-image(src="{{url avatarUrlOptions}}")
+        .member
+          img.avatar.avatar-image(src="{{url avatarUrlOptions}}")
         | Uploaded avatar
         | Uploaded avatar
         if isSelected
         if isSelected
           i.fa.fa-check
           i.fa.fa-check
@@ -36,6 +43,13 @@ template(name="changeAvatarPopup")
               | Delete
               | Delete
             |  -
             |  -
           = original.name
           = original.name
+    li: a.js-select-initials
+      .member
+        +userAvatarInitials(userId=currentUser._id)
+      | Initials
+      if noAvatarUrl
+        i.fa.fa-check
+       p.sub-name Default avatar
   input.hide.js-upload-avatar-input(accept="image/*;capture=camera" type="file")
   input.hide.js-upload-avatar-input(accept="image/*;capture=camera" type="file")
   button.full.js-upload-avatar
   button.full.js-upload-avatar
     i.fa.fa-upload
     i.fa.fa-upload

+ 21 - 0
client/components/users/userAvatar.js

@@ -26,6 +26,18 @@ Template.userAvatar.helpers({
   }
   }
 });
 });
 
 
+Template.userAvatarInitials.helpers({
+  initials: function() {
+    var user = Users.findOne(this.userId);
+    return user && user.getInitials();
+  },
+
+  viewPortWidth: function() {
+    var user = Users.findOne(this.userId);
+    return (user && user.getInitials().length || 1) * 12;
+  }
+});
+
 BlazeComponent.extendComponent({
 BlazeComponent.extendComponent({
   template: function() {
   template: function() {
     return 'changeAvatarPopup';
     return 'changeAvatarPopup';
@@ -49,6 +61,12 @@ BlazeComponent.extendComponent({
     return avatarUrl === currentAvatarUrl;
     return avatarUrl === currentAvatarUrl;
   },
   },
 
 
+  noAvatarUrl: function() {
+    var userProfile = Meteor.user().profile;
+    var avatarUrl = userProfile && userProfile.avatarUrl;
+    return ! avatarUrl;
+  },
+
   setAvatar: function(avatarUrl) {
   setAvatar: function(avatarUrl) {
     Meteor.users.update(Meteor.userId(), {
     Meteor.users.update(Meteor.userId(), {
       $set: {
       $set: {
@@ -84,6 +102,9 @@ BlazeComponent.extendComponent({
         var avatarUrl = this.currentData().url(this.avatarUrlOptions());
         var avatarUrl = this.currentData().url(this.avatarUrlOptions());
         this.setAvatar(avatarUrl);
         this.setAvatar(avatarUrl);
       },
       },
+      'click .js-select-initials': function() {
+        this.setAvatar('');
+      },
       'click .js-delete-avatar': function() {
       'click .js-delete-avatar': function() {
         Avatars.remove(this.currentData()._id);
         Avatars.remove(this.currentData()._id);
       }
       }

+ 9 - 15
client/components/users/userAvatar.styl

@@ -5,11 +5,11 @@ avatar-radius = 50%
 .member
 .member
   border-radius: 3px
   border-radius: 3px
   display: block
   display: block
+  position: relative
   float: left
   float: left
   height: 30px
   height: 30px
   width: @height
   width: @height
   margin: 0 4px 4px 0
   margin: 0 4px 4px 0
-  position: relative
   cursor: pointer
   cursor: pointer
   user-select: none
   user-select: none
   z-index: 1
   z-index: 1
@@ -17,26 +17,20 @@ avatar-radius = 50%
   border-radius: avatar-radius
   border-radius: avatar-radius
 
 
   .avatar
   .avatar
-    height: 100%
-    width: @height
-    display: flex
-    align-items: center
-    justify-content: center
     overflow: hidden
     overflow: hidden
     border-radius: avatar-radius
     border-radius: avatar-radius
 
 
-    .avatar-initials
-      font-weight: bold
-      max-width: 100%
-      max-height: 100%
-      font-size: 14px
-      line-height: 200%
+    &.avatar-initials
+      height: 70%
+      width: @height
+      padding: 15%
       background-color: #dbdbdb
       background-color: #dbdbdb
       color: #444444
       color: #444444
+      position: absolute
 
 
-    .avatar-image
-      max-width: 100%
-      max-height: 100%
+    &.avatar-image
+      height: 100%
+      width: @height
 
 
   .member-presence-status
   .member-presence-status
     background-color: #b3b3b3
     background-color: #b3b3b3

+ 17 - 2
collections/users.js

@@ -31,13 +31,28 @@ Users.helpers({
       return _.where(board.members, {userId: this._id})[0].isAdmin;
       return _.where(board.members, {userId: this._id})[0].isAdmin;
   },
   },
 
 
+  getInitials: function() {
+    var profile = this.profile || {};
+    if (profile.initials)
+      return profile.initials;
+
+    else if (profile.fullname) {
+      return _.reduce(profile.fullname.split(/\s+/), function(memo, word) {
+        return memo + word[0];
+      }, '').toUpperCase();
+
+    } else {
+      return this.pseudo[0].toUpperCase();
+    }
+  },
+
   toggleBoardStar: function(boardId) {
   toggleBoardStar: function(boardId) {
-    var queryType = Meteor.user().hasStarred(boardId) ? '$pull' : '$addToSet';
+    var queryType = this.hasStarred(boardId) ? '$pull' : '$addToSet';
     var query = {};
     var query = {};
     query[queryType] = {
     query[queryType] = {
       'profile.starredBoards': boardId
       'profile.starredBoards': boardId
     };
     };
-    Meteor.users.update(Meteor.userId(), query);
+    Meteor.users.update(this._id, query);
   }
   }
 });
 });