1
0
Fork 0
mirror of https://github.com/TryGhost/Ghost-Admin.git synced 2023-12-14 02:33:04 +01:00
Ghost-Admin/app/components/gh-file-uploader.js
Kevin Ansfield 7eefbba69f 💄🐷 sort-imports eslint rule (#712)
no issue

- adds `eslint-plugin-sort-imports-es6-autofix` dependency
  - implements ESLint's base `sort-imports` rule but has a distinction in that `import {foo} from 'bar';` is considered `multiple` rather than `single`
  - fixes ESLint's autofix behaviour so `eslint --fix` will actually fix the sort order
- updates all unordered import rules by using `eslint --fix`

With the increased number of `import` statements since Ember+ecosystem started moving towards es6 modules I've found it frustrating at times trying to search through randomly ordered import statements. Recently I've been sorting imports manually when I've added new code or touched old code so I thought I'd add an ESLint rule to codify it.
2017-05-29 20:50:03 +02:00

242 lines
6.8 KiB
JavaScript

import Component from 'ember-component';
import computed from 'ember-computed';
import injectService from 'ember-service/inject';
import run from 'ember-runloop';
import {
UnsupportedMediaTypeError,
isRequestEntityTooLargeError,
isUnsupportedMediaTypeError,
isVersionMismatchError
} from 'ghost-admin/services/ajax';
import {htmlSafe} from 'ember-string';
import {invokeAction} from 'ember-invoke-action';
import {isBlank} from 'ember-utils';
import {isEmberArray} from 'ember-array/utils';
export default Component.extend({
tagName: 'section',
classNames: ['gh-image-uploader'],
classNameBindings: ['dragClass'],
labelText: 'Select or drag-and-drop a file',
url: null,
paramName: 'file',
accept: ['text/csv'],
extensions: ['csv'],
validate: null,
file: null,
response: null,
dragClass: null,
failureMessage: null,
uploadPercentage: 0,
ajax: injectService(),
eventBus: injectService(),
notifications: injectService(),
formData: computed('file', function () {
let paramName = this.get('paramName');
let file = this.get('file');
let formData = new FormData();
formData.append(paramName, file);
return formData;
}),
progressStyle: computed('uploadPercentage', function () {
let percentage = this.get('uploadPercentage');
let width = '';
if (percentage > 0) {
width = `${percentage}%`;
} else {
width = '0';
}
return htmlSafe(`width: ${width}`);
}),
// we can optionally listen to a named event bus channel so that the upload
// process can be triggered externally
init() {
this._super(...arguments);
let listenTo = this.get('listenTo');
if (listenTo) {
this.get('eventBus').subscribe(`${listenTo}:upload`, this, function (file) {
if (file) {
this.set('file', file);
}
this.send('upload');
});
}
},
didReceiveAttrs() {
this._super(...arguments);
let accept = this.get('accept');
let extensions = this.get('extensions');
this._accept = (!isBlank(accept) && !isEmberArray(accept)) ? accept.split(',') : accept;
this._extensions = (!isBlank(extensions) && !isEmberArray(extensions)) ? extensions.split(',') : extensions;
},
willDestroyElement() {
let listenTo = this.get('listenTo');
this._super(...arguments);
if (listenTo) {
this.get('eventBus').unsubscribe(`${listenTo}:upload`);
}
},
dragOver(event) {
if (!event.dataTransfer) {
return;
}
// this is needed to work around inconsistencies with dropping files
// from Chrome's downloads bar
let eA = event.dataTransfer.effectAllowed;
event.dataTransfer.dropEffect = (eA === 'move' || eA === 'linkMove') ? 'move' : 'copy';
event.stopPropagation();
event.preventDefault();
this.set('dragClass', '-drag-over');
},
dragLeave(event) {
event.preventDefault();
this.set('dragClass', null);
},
drop(event) {
event.preventDefault();
this.set('dragClass', null);
if (event.dataTransfer.files) {
this.send('fileSelected', event.dataTransfer.files);
}
},
generateRequest() {
let ajax = this.get('ajax');
let formData = this.get('formData');
let url = this.get('url');
invokeAction(this, 'uploadStarted');
ajax.post(url, {
data: formData,
processData: false,
contentType: false,
dataType: 'text',
xhr: () => {
let xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
this._uploadProgress(event);
}, false);
return xhr;
}
}).then((response) => {
this._uploadSuccess(JSON.parse(response));
}).catch((error) => {
this._uploadFailed(error);
}).finally(() => {
invokeAction(this, 'uploadFinished');
});
},
_uploadProgress(event) {
if (event.lengthComputable) {
run(() => {
let percentage = Math.round((event.loaded / event.total) * 100);
this.set('uploadPercentage', percentage);
});
}
},
_uploadSuccess(response) {
invokeAction(this, 'uploadSuccess', response);
this.send('reset');
},
_uploadFailed(error) {
let message;
if (isVersionMismatchError(error)) {
this.get('notifications').showAPIError(error);
}
if (isUnsupportedMediaTypeError(error)) {
message = 'The file type you uploaded is not supported.';
} else if (isRequestEntityTooLargeError(error)) {
message = 'The file you uploaded was larger than the maximum file size your server allows.';
} else if (error.errors && !isBlank(error.errors[0].message)) {
message = htmlSafe(error.errors[0].message);
} else {
message = 'Something went wrong :(';
}
this.set('failureMessage', message);
invokeAction(this, 'uploadFailed', error);
},
_validate(file) {
if (this.get('validate')) {
return invokeAction(this, 'validate', file);
} else {
return this._defaultValidator(file);
}
},
_defaultValidator(file) {
let [, extension] = (/(?:\.([^.]+))?$/).exec(file.name);
let extensions = this._extensions;
if (!extension || extensions.indexOf(extension.toLowerCase()) === -1) {
return new UnsupportedMediaTypeError();
}
return true;
},
actions: {
fileSelected(fileList) {
// can't use array destructuring here as FileList is not a strict
// array and fails in Safari
// eslint-disable-next-line ember-suave/prefer-destructuring
let file = fileList[0];
let validationResult = this._validate(file);
this.set('file', file);
invokeAction(this, 'fileSelected', file);
if (validationResult === true) {
run.schedule('actions', this, function () {
this.generateRequest();
});
} else {
this._uploadFailed(validationResult);
}
},
upload() {
if (this.get('file')) {
this.generateRequest();
}
},
reset() {
this.set('file', null);
this.set('uploadPercentage', 0);
this.set('failureMessage', null);
}
}
});