diff --git a/framework/core/js/forum/src/components/AvatarEditor.js b/framework/core/js/forum/src/components/AvatarEditor.js
index 8d7f6c525..869904b33 100644
--- a/framework/core/js/forum/src/components/AvatarEditor.js
+++ b/framework/core/js/forum/src/components/AvatarEditor.js
@@ -23,6 +23,13 @@ export default class AvatarEditor extends Component {
* @type {Boolean}
*/
this.loading = false;
+
+ /**
+ * Whether or not an image has been dragged over the dropzone.
+ *
+ * @type {Boolean}
+ */
+ this.isDraggedOver = false;
}
static initProps(props) {
@@ -35,12 +42,17 @@ export default class AvatarEditor extends Component {
const user = this.props.user;
return (
-
+
{avatar(user)}
+ 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'))}
@@ -62,7 +74,7 @@ export default class AvatarEditor extends Component {
Button.component({
icon: 'upload',
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;
}
+ /**
+ * 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
* 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()) {
e.preventDefault();
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;
// 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.appendTo('body').hide().click().on('change', e => {
- const data = new FormData();
- 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)
- );
+ this.upload($(e.target)[0].files[0]);
});
}
+ /**
+ * 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.
*/
diff --git a/framework/core/less/forum/AvatarEditor.less b/framework/core/less/forum/AvatarEditor.less
index 72fc55eda..7f6dc7a9b 100644
--- a/framework/core/less/forum/AvatarEditor.less
+++ b/framework/core/less/forum/AvatarEditor.less
@@ -17,7 +17,10 @@
.AvatarEditor--noAvatar {
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;
}
.LoadingIndicator {