client/posts: add upload cancelling
This commit is contained in:
parent
049a0dc351
commit
7862fecbc9
|
@ -1,5 +1,7 @@
|
||||||
@import colors
|
@import colors
|
||||||
|
|
||||||
|
$cancel-button-color = tomato
|
||||||
|
|
||||||
#post-upload
|
#post-upload
|
||||||
form
|
form
|
||||||
width: 100%
|
width: 100%
|
||||||
|
@ -19,6 +21,17 @@
|
||||||
|
|
||||||
input[type=submit]
|
input[type=submit]
|
||||||
margin-top: 1em
|
margin-top: 1em
|
||||||
|
&[disabled]
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.cancel
|
||||||
|
margin-top: 1em
|
||||||
|
background: $cancel-button-color
|
||||||
|
border-color: $cancel-button-color
|
||||||
|
&[disabled]
|
||||||
|
display: none
|
||||||
|
&:focus
|
||||||
|
border: 2px solid $text-color
|
||||||
|
|
||||||
.messages
|
.messages
|
||||||
margin-top: 1em
|
margin-top: 1em
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<div class='dropper-container'></div>
|
<div class='dropper-container'></div>
|
||||||
|
|
||||||
<input type='submit' value='Upload all' class='submit'/>
|
<input type='submit' value='Upload all' class='submit'/>
|
||||||
|
<input type='button' value='Cancel' class='cancel'/>
|
||||||
|
|
||||||
<div class='messages'></div>
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
|
|
@ -64,11 +64,13 @@ class Api extends events.EventTarget {
|
||||||
_process(url, requestFactory, data, files, options) {
|
_process(url, requestFactory, data, files, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
const [fullUrl, query] = this._getFullUrl(url);
|
const [fullUrl, query] = this._getFullUrl(url);
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!options.noProgress) {
|
let abortFunction = null;
|
||||||
nprogress.start();
|
|
||||||
}
|
let promise = new Promise((resolve, reject) => {
|
||||||
let req = requestFactory(fullUrl);
|
let req = requestFactory(fullUrl);
|
||||||
|
|
||||||
|
req.set('Accept', 'application/json');
|
||||||
if (query) {
|
if (query) {
|
||||||
req.query(query);
|
req.query(query);
|
||||||
}
|
}
|
||||||
|
@ -94,8 +96,21 @@ class Api extends events.EventTarget {
|
||||||
title: 'Authentication error',
|
title: 'Authentication error',
|
||||||
description: 'Malformed credentials'});
|
description: 'Malformed credentials'});
|
||||||
}
|
}
|
||||||
req.set('Accept', 'application/json')
|
|
||||||
.end((error, response) => {
|
if (!options.noProgress) {
|
||||||
|
nprogress.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
abortFunction = () => {
|
||||||
|
req.abort(); // does *NOT* call the callback passed in .end()
|
||||||
|
nprogress.done();
|
||||||
|
reject({
|
||||||
|
title: 'Cancelled',
|
||||||
|
description:
|
||||||
|
'The request was aborted due to user cancel.'});
|
||||||
|
};
|
||||||
|
|
||||||
|
req.end((error, response) => {
|
||||||
nprogress.done();
|
nprogress.done();
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(response && response.body ? response.body : {
|
reject(response && response.body ? response.body : {
|
||||||
|
@ -106,6 +121,10 @@ class Api extends events.EventTarget {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
promise.abort = () => abortFunction();
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPrivilege(lookup) {
|
hasPrivilege(lookup) {
|
||||||
|
|
|
@ -10,6 +10,8 @@ const EmptyView = require('../views/empty_view.js');
|
||||||
|
|
||||||
class PostUploadController {
|
class PostUploadController {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this._lastPromise = null;
|
||||||
|
|
||||||
if (!api.hasPrivilege('posts:create')) {
|
if (!api.hasPrivilege('posts:create')) {
|
||||||
this._view = new EmptyView();
|
this._view = new EmptyView();
|
||||||
this._view.showError('You don\'t have privileges to upload posts.');
|
this._view.showError('You don\'t have privileges to upload posts.');
|
||||||
|
@ -23,6 +25,7 @@ class PostUploadController {
|
||||||
});
|
});
|
||||||
this._view.addEventListener('change', e => this._evtChange(e));
|
this._view.addEventListener('change', e => this._evtChange(e));
|
||||||
this._view.addEventListener('submit', e => this._evtSubmit(e));
|
this._view.addEventListener('submit', e => this._evtSubmit(e));
|
||||||
|
this._view.addEventListener('cancel', e => this._evtCancel(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
_evtChange(e) {
|
_evtChange(e) {
|
||||||
|
@ -34,6 +37,12 @@ class PostUploadController {
|
||||||
this._view.clearMessages();
|
this._view.clearMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evtCancel(e) {
|
||||||
|
if (this._lastPromise) {
|
||||||
|
this._lastPromise.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_evtSubmit(e) {
|
_evtSubmit(e) {
|
||||||
this._view.disableForm();
|
this._view.disableForm();
|
||||||
this._view.clearMessages();
|
this._view.clearMessages();
|
||||||
|
@ -48,7 +57,9 @@ class PostUploadController {
|
||||||
} else {
|
} else {
|
||||||
post.newContent = uploadable.file;
|
post.newContent = uploadable.file;
|
||||||
}
|
}
|
||||||
return post.save(uploadable.anonymous)
|
let modelPromise = post.save(uploadable.anonymous);
|
||||||
|
this._lastPromise = modelPromise;
|
||||||
|
return modelPromise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._view.removeUploadable(uploadable);
|
this._view.removeUploadable(uploadable);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
|
@ -135,11 +135,11 @@ class Post extends events.EventTarget {
|
||||||
files.thumbnail = this._newThumbnail;
|
files.thumbnail = this._newThumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
let promise = this._id ?
|
let apiPromise = this._id ?
|
||||||
api.put('/post/' + this._id, detail, files) :
|
api.put('/post/' + this._id, detail, files) :
|
||||||
api.post('/posts', detail, files);
|
api.post('/posts', detail, files);
|
||||||
|
|
||||||
return promise.then(response => {
|
let returnedPromise = apiPromise.then(response => {
|
||||||
this._updateFromResponse(response);
|
this._updateFromResponse(response);
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('change', {detail: {post: this}}));
|
new CustomEvent('change', {detail: {post: this}}));
|
||||||
|
@ -159,6 +159,12 @@ class Post extends events.EventTarget {
|
||||||
}
|
}
|
||||||
return Promise.reject(response.description);
|
return Promise.reject(response.description);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
returnedPromise.abort = () => {
|
||||||
|
apiPromise.abort();
|
||||||
|
};
|
||||||
|
|
||||||
|
return returnedPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
feature() {
|
feature() {
|
||||||
|
|
|
@ -133,10 +133,13 @@ class PostUploadView extends events.EventTarget {
|
||||||
super();
|
super();
|
||||||
this._ctx = ctx;
|
this._ctx = ctx;
|
||||||
this._hostNode = document.getElementById('content-holder');
|
this._hostNode = document.getElementById('content-holder');
|
||||||
this._enabled = true;
|
|
||||||
views.replaceContent(this._hostNode, template());
|
views.replaceContent(this._hostNode, template());
|
||||||
views.syncScrollPosition();
|
views.syncScrollPosition();
|
||||||
|
|
||||||
|
this._enabled = true;
|
||||||
|
this._cancelButtonNode.disabled = true;
|
||||||
|
|
||||||
this._uploadables = new Map();
|
this._uploadables = new Map();
|
||||||
this._contentFileDropper = new FileDropperControl(
|
this._contentFileDropper = new FileDropperControl(
|
||||||
this._contentInputNode,
|
this._contentInputNode,
|
||||||
|
@ -150,18 +153,22 @@ class PostUploadView extends events.EventTarget {
|
||||||
this._contentFileDropper.addEventListener(
|
this._contentFileDropper.addEventListener(
|
||||||
'urladd', e => this._evtUrlsAdded(e));
|
'urladd', e => this._evtUrlsAdded(e));
|
||||||
|
|
||||||
|
this._cancelButtonNode.addEventListener(
|
||||||
|
'click', e => this._evtCancelButtonClick(e));
|
||||||
this._formNode.addEventListener('submit', e => this._evtFormSubmit(e));
|
this._formNode.addEventListener('submit', e => this._evtFormSubmit(e));
|
||||||
this._formNode.classList.add('inactive');
|
this._formNode.classList.add('inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
enableForm() {
|
enableForm() {
|
||||||
this._enabled = true;
|
|
||||||
views.enableForm(this._formNode);
|
views.enableForm(this._formNode);
|
||||||
|
this._cancelButtonNode.disabled = true;
|
||||||
|
this._enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
disableForm() {
|
disableForm() {
|
||||||
this._enabled = false;
|
|
||||||
views.disableForm(this._formNode);
|
views.disableForm(this._formNode);
|
||||||
|
this._cancelButtonNode.disabled = false;
|
||||||
|
this._enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearMessages() {
|
clearMessages() {
|
||||||
|
@ -226,6 +233,11 @@ class PostUploadView extends events.EventTarget {
|
||||||
this.addUploadables(e.detail.urls.map(url => new Url(url)));
|
this.addUploadables(e.detail.urls.map(url => new Url(url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evtCancelButtonClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this._emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
_evtFormSubmit(e) {
|
_evtFormSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._emit('submit');
|
this._emit('submit');
|
||||||
|
@ -342,6 +354,10 @@ class PostUploadView extends events.EventTarget {
|
||||||
return this._hostNode.querySelector('form [type=submit]');
|
return this._hostNode.querySelector('form [type=submit]');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get _cancelButtonNode() {
|
||||||
|
return this._hostNode.querySelector('form .cancel');
|
||||||
|
}
|
||||||
|
|
||||||
get _contentInputNode() {
|
get _contentInputNode() {
|
||||||
return this._formNode.querySelector('.dropper-container');
|
return this._formNode.querySelector('.dropper-container');
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue