mirror of
https://github.com/flarum/framework.git
synced 2025-04-30 16:44:04 +08:00
Merge pull request #1261 from josephnle/drag-and-drop-avatar-upload
Add drag and drop avatar uploading
This commit is contained in:
commit
55a09a2f57
@ -23,6 +23,13 @@ export default class AvatarEditor extends Component {
|
|||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
*/
|
*/
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not an image has been dragged over the dropzone.
|
||||||
|
*
|
||||||
|
* @type {Boolean}
|
||||||
|
*/
|
||||||
|
this.isDraggedOver = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static initProps(props) {
|
static initProps(props) {
|
||||||
@ -35,12 +42,17 @@ export default class AvatarEditor extends Component {
|
|||||||
const user = this.props.user;
|
const user = this.props.user;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'AvatarEditor Dropdown ' + this.props.className + (this.loading ? ' loading' : '')}>
|
<div className={'AvatarEditor Dropdown ' + this.props.className + (this.loading ? ' loading' : '') + (this.isDraggedOver ? ' dragover' : '')}>
|
||||||
{avatar(user)}
|
{avatar(user)}
|
||||||
<a className={ user.avatarUrl() ? "Dropdown-toggle" : "Dropdown-toggle AvatarEditor--noAvatar" }
|
<a className={ user.avatarUrl() ? "Dropdown-toggle" : "Dropdown-toggle AvatarEditor--noAvatar" }
|
||||||
title={app.translator.trans('core.forum.user.avatar_upload_tooltip')}
|
title={app.translator.trans('core.forum.user.avatar_upload_tooltip')}
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
onclick={this.quickUpload.bind(this)}>
|
onclick={this.quickUpload.bind(this)}
|
||||||
|
ondragover={this.enableDragover.bind(this)}
|
||||||
|
ondragenter={this.enableDragover.bind(this)}
|
||||||
|
ondragleave={this.disableDragover.bind(this)}
|
||||||
|
ondragend={this.disableDragover.bind(this)}
|
||||||
|
ondrop={this.dropUpload.bind(this)}>
|
||||||
{this.loading ? LoadingIndicator.component() : (user.avatarUrl() ? icon('pencil') : icon('plus-circle'))}
|
{this.loading ? LoadingIndicator.component() : (user.avatarUrl() ? icon('pencil') : icon('plus-circle'))}
|
||||||
</a>
|
</a>
|
||||||
<ul className="Dropdown-menu Menu">
|
<ul className="Dropdown-menu Menu">
|
||||||
@ -62,7 +74,7 @@ export default class AvatarEditor extends Component {
|
|||||||
Button.component({
|
Button.component({
|
||||||
icon: 'upload',
|
icon: 'upload',
|
||||||
children: app.translator.trans('core.forum.user.avatar_upload_button'),
|
children: app.translator.trans('core.forum.user.avatar_upload_button'),
|
||||||
onclick: this.upload.bind(this)
|
onclick: this.openPicker.bind(this)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -77,6 +89,40 @@ export default class AvatarEditor extends Component {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable dragover style
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
enableDragover(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.isDraggedOver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable dragover style
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
disableDragover(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.isDraggedOver = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload avatar when file is dropped into dropzone.
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
dropUpload(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.isDraggedOver = false;
|
||||||
|
this.upload(e.dataTransfer.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the user doesn't have an avatar, there's no point in showing the
|
* If the user doesn't have an avatar, there's no point in showing the
|
||||||
* controls dropdown, because only one option would be viable: uploading.
|
* controls dropdown, because only one option would be viable: uploading.
|
||||||
@ -89,14 +135,14 @@ export default class AvatarEditor extends Component {
|
|||||||
if (!this.props.user.avatarUrl()) {
|
if (!this.props.user.avatarUrl()) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.upload();
|
this.openPicker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt the user to upload a new avatar.
|
* Upload avatar using file picker
|
||||||
*/
|
*/
|
||||||
upload() {
|
openPicker() {
|
||||||
if (this.loading) return;
|
if (this.loading) return;
|
||||||
|
|
||||||
// Create a hidden HTML input element and click on it so the user can select
|
// Create a hidden HTML input element and click on it so the user can select
|
||||||
@ -105,24 +151,36 @@ export default class AvatarEditor extends Component {
|
|||||||
const $input = $('<input type="file">');
|
const $input = $('<input type="file">');
|
||||||
|
|
||||||
$input.appendTo('body').hide().click().on('change', e => {
|
$input.appendTo('body').hide().click().on('change', e => {
|
||||||
const data = new FormData();
|
this.upload($(e.target)[0].files[0]);
|
||||||
data.append('avatar', $(e.target)[0].files[0]);
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
m.redraw();
|
|
||||||
|
|
||||||
app.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
|
||||||
serialize: raw => raw,
|
|
||||||
data
|
|
||||||
}).then(
|
|
||||||
this.success.bind(this),
|
|
||||||
this.failure.bind(this)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload avatar
|
||||||
|
*
|
||||||
|
* @param {File} file
|
||||||
|
*/
|
||||||
|
upload(file) {
|
||||||
|
if (this.loading) return;
|
||||||
|
|
||||||
|
const user = this.props.user;
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('avatar', file);
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
m.redraw();
|
||||||
|
|
||||||
|
app.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
||||||
|
serialize: raw => raw,
|
||||||
|
data
|
||||||
|
}).then(
|
||||||
|
this.success.bind(this),
|
||||||
|
this.failure.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the user's avatar.
|
* Remove the user's avatar.
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
.AvatarEditor--noAvatar {
|
.AvatarEditor--noAvatar {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
&:hover .Dropdown-toggle, &.open .Dropdown-toggle, &.loading .Dropdown-toggle {
|
&:hover .Dropdown-toggle,
|
||||||
|
&.open .Dropdown-toggle,
|
||||||
|
&.loading .Dropdown-toggle,
|
||||||
|
&.dragover .Dropdown-toggle {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.LoadingIndicator {
|
.LoadingIndicator {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user