Переглянути джерело

(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 роки тому
батько
коміт
765b0168ea

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

@@ -218,22 +218,24 @@ $popupWidth = 300px
   .pop-over-list
     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
 
 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
       ul.pop-over-list
         li

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

@@ -1,15 +1,22 @@
 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
       img.avatar.avatar-image(src=userData.profile.avatarUrl)
+    else
+      +userAvatarInitials(userId=userData._id)
+
     if showStatus
       span.member-presence-status(class=presenceStatusClassName)
       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")
   .board-member-menu
     .mini-profile-info
-      +userAvatar(user=user)
+      +userAvatar(userId=user._id)
       .info
         h3.bottom
           a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
@@ -25,8 +32,8 @@ template(name="changeAvatarPopup")
   ul.pop-over-list
     each uploadedAvatars
       li: a.js-select-avatar
-        .member: .avatar
-          img.avatar-image(src="{{url avatarUrlOptions}}")
+        .member
+          img.avatar.avatar-image(src="{{url avatarUrlOptions}}")
         | Uploaded avatar
         if isSelected
           i.fa.fa-check
@@ -36,6 +43,13 @@ template(name="changeAvatarPopup")
               | Delete
             |  -
           = 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")
   button.full.js-upload-avatar
     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({
   template: function() {
     return 'changeAvatarPopup';
@@ -49,6 +61,12 @@ BlazeComponent.extendComponent({
     return avatarUrl === currentAvatarUrl;
   },
 
+  noAvatarUrl: function() {
+    var userProfile = Meteor.user().profile;
+    var avatarUrl = userProfile && userProfile.avatarUrl;
+    return ! avatarUrl;
+  },
+
   setAvatar: function(avatarUrl) {
     Meteor.users.update(Meteor.userId(), {
       $set: {
@@ -84,6 +102,9 @@ BlazeComponent.extendComponent({
         var avatarUrl = this.currentData().url(this.avatarUrlOptions());
         this.setAvatar(avatarUrl);
       },
+      'click .js-select-initials': function() {
+        this.setAvatar('');
+      },
       'click .js-delete-avatar': function() {
         Avatars.remove(this.currentData()._id);
       }

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

@@ -5,11 +5,11 @@ avatar-radius = 50%
 .member
   border-radius: 3px
   display: block
+  position: relative
   float: left
   height: 30px
   width: @height
   margin: 0 4px 4px 0
-  position: relative
   cursor: pointer
   user-select: none
   z-index: 1
@@ -17,26 +17,20 @@ avatar-radius = 50%
   border-radius: avatar-radius
 
   .avatar
-    height: 100%
-    width: @height
-    display: flex
-    align-items: center
-    justify-content: center
     overflow: hidden
     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
       color: #444444
+      position: absolute
 
-    .avatar-image
-      max-width: 100%
-      max-height: 100%
+    &.avatar-image
+      height: 100%
+      width: @height
 
   .member-presence-status
     background-color: #b3b3b3

+ 17 - 2
collections/users.js

@@ -31,13 +31,28 @@ Users.helpers({
       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) {
-    var queryType = Meteor.user().hasStarred(boardId) ? '$pull' : '$addToSet';
+    var queryType = this.hasStarred(boardId) ? '$pull' : '$addToSet';
     var query = {};
     query[queryType] = {
       'profile.starredBoards': boardId
     };
-    Meteor.users.update(Meteor.userId(), query);
+    Meteor.users.update(this._id, query);
   }
 });