mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-24 16:18:42 -05:00
REFACTOR: uploading avatar should share code with upload component
This commit is contained in:
parent
4088fba4f2
commit
3cbb32cc20
7 changed files with 104 additions and 141 deletions
|
@ -0,0 +1,30 @@
|
|||
import UploadMixin from 'discourse/mixins/upload';
|
||||
|
||||
export default Em.Component.extend(UploadMixin, {
|
||||
tagName: 'span',
|
||||
imageIsNotASquare: false,
|
||||
type: 'avatar',
|
||||
|
||||
uploadUrl: Discourse.computed.url('username', '/users/%@/preferences/user_image'),
|
||||
|
||||
uploadButtonText: function() {
|
||||
return this.get("uploading") ? I18n.t("uploading") : I18n.t("user.change_avatar.upload_picture");
|
||||
}.property("uploading"),
|
||||
|
||||
uploadDone: function(data) {
|
||||
var self = this;
|
||||
|
||||
// indicates the users is using an uploaded avatar
|
||||
this.set("custom_avatar_upload_id", data.result.upload_id);
|
||||
|
||||
// display a warning whenever the image is not a square
|
||||
this.set("imageIsNotASquare", data.result.width !== data.result.height);
|
||||
// in order to be as much responsive as possible, we're cheating a bit here
|
||||
// indeed, the server gives us back the url to the file we've just uploaded
|
||||
// often, this file is not a square, so we need to crop it properly
|
||||
// this will also capture the first frame of animated avatars when they're not allowed
|
||||
Discourse.Utilities.cropAvatar(data.result.url, data.files[0].type).then(function(avatarTemplate) {
|
||||
self.set("uploadedAvatarTemplate", avatarTemplate);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
export default Em.Component.extend({
|
||||
uploading: false,
|
||||
uploadProgress: 0,
|
||||
import UploadMixin from 'discourse/mixins/upload';
|
||||
|
||||
export default Em.Component.extend(UploadMixin, {
|
||||
|
||||
backgroundStyle: function() {
|
||||
var imageUrl = this.get('imageUrl');
|
||||
|
@ -9,50 +9,11 @@ export default Em.Component.extend({
|
|||
return "background-image: url(" + imageUrl + ")";
|
||||
}.property('imageUrl'),
|
||||
|
||||
_initializeUploader: function() {
|
||||
var $upload = this.$('input[type=file]'), // note: we can't cache this as fileupload replaces the input after upload
|
||||
self = this;
|
||||
|
||||
$upload.fileupload({
|
||||
url: this.get('uploadUrl'),
|
||||
dataType: "json",
|
||||
fileInput: $upload,
|
||||
formData: { image_type: this.get('type') }
|
||||
});
|
||||
|
||||
$upload.on('fileuploadsubmit', function (e, data) {
|
||||
var result = Discourse.Utilities.validateUploadedFiles(data.files, true);
|
||||
self.setProperties({ uploadProgress: 0, uploading: result });
|
||||
return result;
|
||||
});
|
||||
$upload.on("fileuploadprogressall", function(e, data) {
|
||||
var progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
self.set("uploadProgress", progress);
|
||||
});
|
||||
$upload.on("fileuploaddone", function(e, data) {
|
||||
if(data.result.url) {
|
||||
self.set('imageUrl', data.result.url);
|
||||
} else {
|
||||
bootbox.alert(I18n.t('post.errors.upload'));
|
||||
}
|
||||
});
|
||||
$upload.on("fileuploadfail", function(e, data) {
|
||||
Discourse.Utilities.displayErrorForUpload(data);
|
||||
});
|
||||
$upload.on("fileuploadalways", function() {
|
||||
self.setProperties({ uploading: false, uploadProgress: 0});
|
||||
});
|
||||
}.on('didInsertElement'),
|
||||
|
||||
_destroyUploader: function() {
|
||||
this.$('input[type=file]').fileupload('destroy');
|
||||
}.on('willDestroyElement'),
|
||||
|
||||
actions: {
|
||||
selectFile: function() {
|
||||
this.$('input[type=file]').click();
|
||||
uploadDone: function(data) {
|
||||
this.set('imageUrl', data.result.url);
|
||||
},
|
||||
|
||||
actions: {
|
||||
trash: function() {
|
||||
this.set('imageUrl', null);
|
||||
this.sendAction('clear');
|
||||
|
|
54
app/assets/javascripts/discourse/mixins/upload.js.es6
Normal file
54
app/assets/javascripts/discourse/mixins/upload.js.es6
Normal file
|
@ -0,0 +1,54 @@
|
|||
export default Em.Mixin.create({
|
||||
uploading: false,
|
||||
uploadProgress: 0,
|
||||
|
||||
uploadDone: function() {
|
||||
Em.warn("You should implement `uploadDone`");
|
||||
},
|
||||
|
||||
_initializeUploader: function() {
|
||||
var $upload = this.$('input[type=file]'), // note: we can't cache this as fileupload replaces the input after upload
|
||||
self = this;
|
||||
|
||||
$upload.fileupload({
|
||||
url: this.get('uploadUrl'),
|
||||
dataType: "json",
|
||||
fileInput: $upload,
|
||||
formData: { image_type: this.get('type') },
|
||||
pasteZone: this.$()
|
||||
});
|
||||
|
||||
$upload.on('fileuploadsubmit', function (e, data) {
|
||||
var result = Discourse.Utilities.validateUploadedFiles(data.files, true);
|
||||
self.setProperties({ uploadProgress: 0, uploading: result });
|
||||
return result;
|
||||
});
|
||||
$upload.on("fileuploadprogressall", function(e, data) {
|
||||
var progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
self.set("uploadProgress", progress);
|
||||
});
|
||||
$upload.on("fileuploaddone", function(e, data) {
|
||||
if(data.result.url) {
|
||||
self.uploadDone(data);
|
||||
} else {
|
||||
bootbox.alert(I18n.t('post.errors.upload'));
|
||||
}
|
||||
});
|
||||
$upload.on("fileuploadfail", function(e, data) {
|
||||
Discourse.Utilities.displayErrorForUpload(data);
|
||||
});
|
||||
$upload.on("fileuploadalways", function() {
|
||||
self.setProperties({ uploading: false, uploadProgress: 0});
|
||||
});
|
||||
}.on('didInsertElement'),
|
||||
|
||||
_destroyUploader: function() {
|
||||
this.$('input[type=file]').fileupload('destroy');
|
||||
}.on('willDestroyElement'),
|
||||
|
||||
actions: {
|
||||
selectFile: function() {
|
||||
this.$('input[type=file]').click();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -350,7 +350,6 @@ Discourse.User = Discourse.Model.extend({
|
|||
@returns {Promise} the result of the clear profile background request
|
||||
*/
|
||||
clearProfileBackground: function() {
|
||||
var user = this;
|
||||
return Discourse.ajax("/users/" + this.get("username_lower") + "/preferences/profile_background/clear", {
|
||||
type: 'PUT',
|
||||
data: { }
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<input type="file" accept="image/*" style="display:none" />
|
||||
<button class="btn" {{action selectFile}} {{bind-attr disabled="uploading"}} title="{{i18n user.change_avatar.upload_title}}">
|
||||
<i class="fa fa-picture-o"></i> {{uploadButtonText}}
|
||||
</button>
|
||||
{{#if uploading}}
|
||||
<span>{{i18n upload_selector.uploading}} {{view.uploadProgress}}%</span>
|
||||
{{/if}}
|
||||
{{#if imageIsNotASquare}}
|
||||
<div class="warning">{{i18n user.change_avatar.image_is_not_a_square}}</div>
|
||||
{{/if}}
|
|
@ -7,7 +7,7 @@
|
|||
<div>
|
||||
<input type="radio" id="gravatar" name="avatar" value="gravatar" {{action useGravatar}}>
|
||||
<label class="radio" for="gravatar">{{bound-avatar controller "large" gravatar_avatar_upload_id}} {{{i18n user.change_avatar.gravatar}}} {{email}}</label>
|
||||
<button href="#" {{action refreshGravatar}} title="{{i18n user.change_avatar.refresh_gravatar_title}}" {{bind-attr enabled="view.gravatarRefreshEnabled"}} class="btn no-text"><i class="fa fa-refresh"></i></button>
|
||||
<button href {{action refreshGravatar}} title="{{i18n user.change_avatar.refresh_gravatar_title}}" {{bind-attr enabled="view.gravatarRefreshEnabled"}} class="btn no-text"><i class="fa fa-refresh"></i></button>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="uploaded_avatar" name="avatar" value="uploaded" {{action useUploadedAvatar}}>
|
||||
|
@ -22,16 +22,9 @@
|
|||
{{i18n user.change_avatar.uploaded_avatar_empty}}
|
||||
{{/if}}
|
||||
</label>
|
||||
<button id="fake-avatar-input" class="btn" {{bind-attr disabled="view.uploading"}} title="{{i18n user.change_avatar.upload_title}}">
|
||||
<i class="fa fa-picture-o"></i> {{view.uploadButtonText}}
|
||||
</button>
|
||||
<input type="file" id="avatar-input" accept="image/*" style="display:none">
|
||||
{{#if view.uploading}}
|
||||
<span>{{i18n upload_selector.uploading}} {{view.uploadProgress}}%</span>
|
||||
{{/if}}
|
||||
{{#if view.imageIsNotASquare}}
|
||||
<div class="warning">{{i18n user.change_avatar.image_is_not_a_square}}</div>
|
||||
{{/if}}
|
||||
{{avatar-uploader username=username
|
||||
uploadedAvatarTemplate=view.uploadedAvatarTemplate
|
||||
custom_avatar_upload_id=controller.custom_avatar_upload_id}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,90 +10,10 @@ Discourse.AvatarSelectorView = Discourse.ModalBodyView.extend({
|
|||
templateName: 'modal/avatar_selector',
|
||||
classNames: ['avatar-selector'],
|
||||
title: I18n.t('user.change_avatar.title'),
|
||||
uploading: false,
|
||||
uploadProgress: 0,
|
||||
saveDisabled: false,
|
||||
gravatarRefreshEnabled: Em.computed.not('controller.gravatarRefreshDisabled'),
|
||||
imageIsNotASquare : false,
|
||||
|
||||
hasUploadedAvatar: Em.computed.or('uploadedAvatarTemplate', 'controller.custom_avatar_upload_id'),
|
||||
|
||||
didInsertElement: function() {
|
||||
var self = this;
|
||||
var $upload = $("#avatar-input");
|
||||
|
||||
this._super();
|
||||
|
||||
// simulate a click on the hidden file input when clicking on our fake file input
|
||||
$("#fake-avatar-input").on("click", function(e) {
|
||||
// do *NOT* use the cached `$upload` variable, because fileupload is cloning & replacing the input
|
||||
// cf. https://github.com/blueimp/jQuery-File-Upload/wiki/Frequently-Asked-Questions#why-is-the-file-input-field-cloned-and-replaced-after-each-selection
|
||||
$("#avatar-input").click();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// define the upload endpoint
|
||||
$upload.fileupload({
|
||||
url: Discourse.getURL("/users/" + this.get("controller.username") + "/preferences/user_image"),
|
||||
dataType: "json",
|
||||
fileInput: $upload,
|
||||
formData: { image_type: "avatar" }
|
||||
});
|
||||
|
||||
// when a file has been selected
|
||||
$upload.on('fileuploadsubmit', function (e, data) {
|
||||
var result = Discourse.Utilities.validateUploadedFiles(data.files, true);
|
||||
self.setProperties({
|
||||
uploadProgress: 0,
|
||||
uploading: result,
|
||||
imageIsNotASquare: false
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
// when there is a progression for the upload
|
||||
$upload.on("fileuploadprogressall", function (e, data) {
|
||||
var progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
self.set("uploadProgress", progress);
|
||||
});
|
||||
|
||||
// when the upload is successful
|
||||
$upload.on("fileuploaddone", function (e, data) {
|
||||
// make sure we have a url
|
||||
if (data.result.url) {
|
||||
// indicates the users is using an uploaded avatar
|
||||
self.set("controller.custom_avatar_upload_id", data.result.upload_id);
|
||||
|
||||
// display a warning whenever the image is not a square
|
||||
self.set("imageIsNotASquare", data.result.width !== data.result.height);
|
||||
// in order to be as much responsive as possible, we're cheating a bit here
|
||||
// indeed, the server gives us back the url to the file we've just uploaded
|
||||
// often, this file is not a square, so we need to crop it properly
|
||||
// this will also capture the first frame of animated avatars when they're not allowed
|
||||
Discourse.Utilities.cropAvatar(data.result.url, data.files[0].type).then(function(avatarTemplate) {
|
||||
self.set("uploadedAvatarTemplate", avatarTemplate);
|
||||
});
|
||||
} else {
|
||||
bootbox.alert(I18n.t('post.errors.upload'));
|
||||
}
|
||||
});
|
||||
|
||||
// when there has been an error with the upload
|
||||
$upload.on("fileuploadfail", function (e, data) {
|
||||
Discourse.Utilities.displayErrorForUpload(data);
|
||||
});
|
||||
|
||||
// when the upload is done
|
||||
$upload.on("fileuploadalways", function () {
|
||||
self.setProperties({ uploading: false, uploadProgress: 0 });
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement: function() {
|
||||
$("#fake-avatar-input").off("click");
|
||||
$("#avatar-input").fileupload("destroy");
|
||||
},
|
||||
|
||||
// *HACK* used to select the proper radio button, cause {{action}}
|
||||
// stops the default behavior
|
||||
selectedChanged: function() {
|
||||
|
@ -104,8 +24,4 @@ Discourse.AvatarSelectorView = Discourse.ModalBodyView.extend({
|
|||
});
|
||||
}.observes('controller.selected'),
|
||||
|
||||
uploadButtonText: function() {
|
||||
return this.get("uploading") ? I18n.t("uploading") : I18n.t("user.change_avatar.upload_picture");
|
||||
}.property("uploading")
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue