mirror of
https://github.com/wekan/wekan.git
synced 2026-02-04 07:31:47 +01:00
Re-factor the avatar system and support avatar uploads
The user is now able to upload an avatar, and pick one in a list. This functionality should eventually be abstracted in a community package but we still need to work on a great public API. We rely on collectionFS to manage uploaded avatars. We also removed bengott:avatar which was trying to solve the wrong problem (namely displaying the avatar, which is as simple as displaying an image), and not a avatar system as it should be. Gravatar support is coming (back) soon. We may also want to have a list of default fun avatars the user can choose instead of uploading its own one.
This commit is contained in:
parent
98d7278d08
commit
46cc691534
22 changed files with 161 additions and 227 deletions
|
|
@ -1,22 +1,42 @@
|
|||
template(name="userAvatar")
|
||||
.member.js-member(class="{{class}}" title="{{userData.profile.name}} ({{userData.username}})")
|
||||
+avatar(user=userData size=size)
|
||||
.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}})")
|
||||
if userData.profile.avatarUrl
|
||||
img.avatar.avatar-image(src=userData.profile.avatarUrl)
|
||||
if showStatus
|
||||
span.member-presence-status(class=presenceStatusClassName)
|
||||
span.member-type(class=memberType)
|
||||
|
||||
|
||||
template(name="userPopup")
|
||||
.board-member-menu
|
||||
.mini-profile-info
|
||||
+userAvatar(user=user)
|
||||
.info
|
||||
h3.bottom
|
||||
a.js-profile(href="{{ pathFor route='Profile' username=user.username }}")= user.profile.name
|
||||
p.quiet.bottom @{{ user.username }}
|
||||
.info
|
||||
h3.bottom
|
||||
a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
|
||||
p.quiet.bottom @{{ user.username }}
|
||||
|
||||
template(name="memberName")
|
||||
a.inline-object.js-show-mem-menu(href="{{ pathFor route='Profile' username=user.username }}")
|
||||
= user.profile.name
|
||||
a.js-show-mem-menu(href="{{pathFor route='Profile' username=user.username}}")
|
||||
= user.profile.fullname
|
||||
if username
|
||||
| ({{ user.username }})
|
||||
|
||||
template(name="changeAvatarPopup")
|
||||
ul.pop-over-list
|
||||
each uploadedAvatars
|
||||
li: a.js-select-avatar
|
||||
.member: .avatar
|
||||
img.avatar-image(src="{{url avatarUrlOptions}}")
|
||||
| Uploaded avatar
|
||||
if isSelected
|
||||
i.fa.fa-check
|
||||
p.sub-name
|
||||
unless isSelected
|
||||
a.js-delete-avatar
|
||||
| Delete
|
||||
| -
|
||||
= original.name
|
||||
input.hide.js-upload-avatar-input(accept="image/*;capture=camera" type="file")
|
||||
button.full.js-upload-avatar
|
||||
i.fa.fa-upload
|
||||
| Upload an avatar
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
Meteor.subscribe('my-avatars');
|
||||
|
||||
Template.userAvatar.helpers({
|
||||
userData: function() {
|
||||
if (! this.user) {
|
||||
this.user = Users.findOne(this.userId);
|
||||
}
|
||||
return this.user;
|
||||
return Users.findOne(this.userId, {
|
||||
fields: {
|
||||
profile: 1,
|
||||
username: 1
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
memberType: function() {
|
||||
var userId = this.userId || this.user._id;
|
||||
var user = Users.findOne(userId);
|
||||
var user = Users.findOne(this.userId);
|
||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
||||
},
|
||||
|
||||
presenceStatusClassName: function() {
|
||||
var userPresence = Presences.findOne({ userId: this.user._id });
|
||||
var userPresence = Presences.findOne({ userId: this.userId });
|
||||
if (! userPresence)
|
||||
return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
|
|
@ -20,3 +25,68 @@ Template.userAvatar.helpers({
|
|||
return 'idle';
|
||||
}
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
template: function() {
|
||||
return 'changeAvatarPopup';
|
||||
},
|
||||
|
||||
avatarUrlOptions: function() {
|
||||
return {
|
||||
auth: false,
|
||||
brokenIsFine: true
|
||||
};
|
||||
},
|
||||
|
||||
uploadedAvatars: function() {
|
||||
return Avatars.find({userId: Meteor.userId()});
|
||||
},
|
||||
|
||||
isSelected: function() {
|
||||
var userProfile = Meteor.user().profile;
|
||||
var avatarUrl = userProfile && userProfile.avatarUrl;
|
||||
var currentAvatarUrl = this.currentData().url(this.avatarUrlOptions());
|
||||
return avatarUrl === currentAvatarUrl;
|
||||
},
|
||||
|
||||
setAvatar: function(avatarUrl) {
|
||||
Meteor.users.update(Meteor.userId(), {
|
||||
$set: {
|
||||
'profile.avatarUrl': avatarUrl
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
events: function() {
|
||||
return [{
|
||||
'click .js-upload-avatar': function() {
|
||||
this.$('.js-upload-avatar-input').click();
|
||||
},
|
||||
'change .js-upload-avatar-input': function(evt) {
|
||||
var self = this;
|
||||
var file, fileUrl;
|
||||
|
||||
FS.Utility.eachFile(evt, function(f) {
|
||||
file = Avatars.insert(new FS.File(f));
|
||||
fileUrl = file.url(self.avatarUrlOptions());
|
||||
});
|
||||
var fetchAvatarInterval = window.setInterval(function() {
|
||||
$.ajax({
|
||||
url: fileUrl,
|
||||
success: function() {
|
||||
self.setAvatar(file.url(self.avatarUrlOptions()));
|
||||
window.clearInterval(fetchAvatarInterval);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
},
|
||||
'click .js-select-avatar': function() {
|
||||
var avatarUrl = this.currentData().url(this.avatarUrlOptions());
|
||||
this.setAvatar(avatarUrl);
|
||||
},
|
||||
'click .js-delete-avatar': function() {
|
||||
Avatars.remove(this.currentData()._id);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}).register('changeAvatarPopup');
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ template(name="headerUserBar")
|
|||
else
|
||||
= currentUser.username
|
||||
a.header-user-bar-avatar.js-change-avatar
|
||||
+userAvatar(user=currentUser)
|
||||
+userAvatar(userId=currentUser._id)
|
||||
|
||||
template(name="memberMenuPopup")
|
||||
ul.pop-over-list
|
||||
|
|
@ -33,8 +33,6 @@ template(name="editProfilePopup")
|
|||
input.js-profile-initials(type="text" value=profile.initials)
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
|
||||
template(name="changeAvatarPopup")
|
||||
|
||||
template(name="changePasswordPopup")
|
||||
+atForm(state='changePwd')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
<template name='profile'>
|
||||
{{ # if profile }}
|
||||
<div class="tabbed-pane-header">
|
||||
<div class="tabbed-pane-header-wrapper clearfix">
|
||||
<a class="tabbed-pane-header-image profile-image ed js-change-avatar-profile" href="#">
|
||||
{{> userAvatar user=profile size="large"}}
|
||||
</a>
|
||||
<div class="tabbed-pane-header-details">
|
||||
<div class="js-current-details">
|
||||
<div class="tabbed-pane-header-details-name">
|
||||
<h1 class="inline"> {{ profile.profile.name }} </h1>
|
||||
<p class="window-title-extra quiet"> @{{ profile.username }} </p>
|
||||
</div>
|
||||
<div class="tabbed-pane-header-details-content">
|
||||
<p>{{ profile.profile.bio }}</p>
|
||||
</div>
|
||||
<div class="tabbed-pane-header-details-content"></div>
|
||||
</div>
|
||||
{{ > profileEditForm }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{ > message label='user-profile-not-found' }}
|
||||
{{ /if }}
|
||||
</template>
|
||||
|
||||
<template name="settings">
|
||||
{{ > profile profile=currentUser }}
|
||||
<div class="tabbed-pane-main-col clearfix">
|
||||
<div class="tabbed-pane-main-col-loading hide js-loading-page">
|
||||
<span class="tabbed-pane-main-col-loading-spinner spinner"></span>
|
||||
</div>
|
||||
<div class="tabbed-pane-main-col-wrapper js-content">
|
||||
<div class="window-module clearfix">
|
||||
<div class="window-module-title">
|
||||
<h3>{{_ "account-details"}}</h3>
|
||||
</div>
|
||||
<a class="big-link js-change-name-and-bio" href="#">
|
||||
<span class="text">{{_ 'change-name-initials-bio'}}</span>
|
||||
</a>
|
||||
<a class="big-link js-change-avatar" href="#">
|
||||
<span class="text">{{_ 'change-avatar'}}</span>
|
||||
</a>
|
||||
<a class="big-link js-change-password" href="#">
|
||||
<span class="text">{{_ 'change-password'}}</span>
|
||||
</a>
|
||||
<a class="big-link js-change-email" href="#">
|
||||
<span class="text">{{_ 'change-email'}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="profileEditForm">
|
||||
{{#if $eq currentUser.username profile.username }}
|
||||
{{# if session 'ProfileEditForm' }}
|
||||
<form id="ProfileEditForm" class="js-profile-form">
|
||||
<p class="error js-profile-form-error hide"></p>
|
||||
<label>{{_ "username"}}</label>
|
||||
<input type="text" id="username" value="{{ profile.username }}" disabled>
|
||||
<label>{{_ "fullname"}}</label>
|
||||
<input type="text" id="name" value="{{ profile.profile.name }}">
|
||||
<label>
|
||||
{{_ "bio"}} <span class="quiet">({{_ 'optional'}})</span>
|
||||
</label>
|
||||
<textarea id="bio">{{ profile.profile.bio }}</textarea>
|
||||
<input type="submit" class="primary wide js-submit-profile" value="{{_ 'save'}}">
|
||||
<input type="button" class="js-cancel-edit-profile" value="{{_ 'cancel'}}">
|
||||
</form>
|
||||
{{ else }}
|
||||
<a class="button-link tabbed-pane-header-details-edit js-edit-profile" href="#">
|
||||
<span class="icon-sm fa fa-pencil"></span>
|
||||
{{_ "edit-profile"}}
|
||||
</a>
|
||||
{{ /if }}
|
||||
{{ /if }}
|
||||
</template>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
Template.profileEditForm.events({
|
||||
'click .js-edit-profile': function() {
|
||||
Session.set('ProfileEditForm', true);
|
||||
},
|
||||
'click .js-cancel-edit-profile': function() {
|
||||
Session.set('ProfileEditForm', false);
|
||||
},
|
||||
'submit #ProfileEditForm': function(evt, t) {
|
||||
var name = t.find('#name').value;
|
||||
var bio = t.find('#bio').value;
|
||||
|
||||
// trim and update
|
||||
if ($.trim(name)) {
|
||||
Users.update(this.profile()._id, {
|
||||
$set: {
|
||||
'profile.name': name,
|
||||
'profile.bio': bio
|
||||
}
|
||||
}, function() {
|
||||
|
||||
// update complete close profileEditForm
|
||||
Session.set('ProfileEditForm', false);
|
||||
});
|
||||
}
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
Template.memberName.events({
|
||||
'click .js-show-mem-menu': Popup.open('user')
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue