client/posts: tweak upload appearance and UX
This commit is contained in:
		
							parent
							
								
									4cb613a5c9
								
							
						
					
					
						commit
						d1bb33ecf0
					
				@ -1,5 +1,6 @@
 | 
			
		||||
@import colors
 | 
			
		||||
 | 
			
		||||
$upload-header-background-color = $top-navigation-color
 | 
			
		||||
$upload-border-color = #DDD
 | 
			
		||||
$cancel-button-color = tomato
 | 
			
		||||
 | 
			
		||||
#post-upload
 | 
			
		||||
@ -35,7 +36,7 @@ $cancel-button-color = tomato
 | 
			
		||||
    .skip-duplicates
 | 
			
		||||
        margin-left: 1em
 | 
			
		||||
 | 
			
		||||
    .messages
 | 
			
		||||
    form>.messages
 | 
			
		||||
        margin-top: 1em
 | 
			
		||||
 | 
			
		||||
    .uploadables-container
 | 
			
		||||
@ -43,48 +44,78 @@ $cancel-button-color = tomato
 | 
			
		||||
        margin: 0
 | 
			
		||||
        padding: 0
 | 
			
		||||
 | 
			
		||||
        li
 | 
			
		||||
        .uploadable-container
 | 
			
		||||
            clear: both
 | 
			
		||||
            margin: 0 0 1.2em 0
 | 
			
		||||
 | 
			
		||||
        .uploadable
 | 
			
		||||
            .file
 | 
			
		||||
                margin: 0.3em 0
 | 
			
		||||
                overflow: hidden
 | 
			
		||||
                white-space: nowrap
 | 
			
		||||
                text-align: left
 | 
			
		||||
                text-overflow: ellipsis
 | 
			
		||||
 | 
			
		||||
            .anonymous
 | 
			
		||||
                margin: 0.3em 0
 | 
			
		||||
 | 
			
		||||
            .safety
 | 
			
		||||
                margin: 0.3em 0
 | 
			
		||||
                label
 | 
			
		||||
                    display: inline-block
 | 
			
		||||
                    margin-right: 1em
 | 
			
		||||
 | 
			
		||||
            .options div
 | 
			
		||||
                display: inline-block
 | 
			
		||||
                margin: 0 1em 0 0
 | 
			
		||||
            padding-left: 13em
 | 
			
		||||
 | 
			
		||||
            .thumbnail-wrapper
 | 
			
		||||
                float: left
 | 
			
		||||
                width: 12.5em
 | 
			
		||||
                height: 7em
 | 
			
		||||
                margin: 0.2em 1em 0 0
 | 
			
		||||
 | 
			
		||||
                width: 12em
 | 
			
		||||
                height: 8em
 | 
			
		||||
                margin: 0 0 0 -13em
 | 
			
		||||
                .thumbnail
 | 
			
		||||
                    width: 100%
 | 
			
		||||
                    height: 100%
 | 
			
		||||
 | 
			
		||||
            .controls
 | 
			
		||||
                float: right
 | 
			
		||||
                a
 | 
			
		||||
                    color: $inactive-link-color
 | 
			
		||||
                    margin-left: 0.5em
 | 
			
		||||
            .uploadable
 | 
			
		||||
                border: 1px solid $upload-border-color
 | 
			
		||||
                min-height: 8em
 | 
			
		||||
                box-sizing: border-box
 | 
			
		||||
 | 
			
		||||
            div:last-child:after
 | 
			
		||||
                display: block
 | 
			
		||||
                content: ' '
 | 
			
		||||
                height: 1px
 | 
			
		||||
                clear: both
 | 
			
		||||
                header
 | 
			
		||||
                    line-height: 1.5em
 | 
			
		||||
                    padding: 0.25em 1em
 | 
			
		||||
                    text-align: left
 | 
			
		||||
                    background: $upload-header-background-color
 | 
			
		||||
                    border-bottom: 1px solid $upload-border-color
 | 
			
		||||
 | 
			
		||||
                    nav
 | 
			
		||||
                        &:first-of-type
 | 
			
		||||
                            float: left
 | 
			
		||||
                            a
 | 
			
		||||
                                margin: 0 0.5em 0 0
 | 
			
		||||
                        &:last-of-type
 | 
			
		||||
                            float: right
 | 
			
		||||
                            a
 | 
			
		||||
                                margin: 0 0 0 0.5em
 | 
			
		||||
 | 
			
		||||
                        ul
 | 
			
		||||
                            list-style-type: none
 | 
			
		||||
                        ul, li
 | 
			
		||||
                            display: inline-block
 | 
			
		||||
                            margin: 0
 | 
			
		||||
                            padding: 0
 | 
			
		||||
 | 
			
		||||
                    span.filename
 | 
			
		||||
                        padding: 0 0.5em
 | 
			
		||||
                        display: block
 | 
			
		||||
                        overflow: hidden
 | 
			
		||||
                        white-space: nowrap
 | 
			
		||||
                        text-overflow: ellipsis
 | 
			
		||||
 | 
			
		||||
                .body
 | 
			
		||||
                    margin: 1em
 | 
			
		||||
 | 
			
		||||
                    .anonymous
 | 
			
		||||
                        margin: 0.3em 0
 | 
			
		||||
 | 
			
		||||
                    .safety
 | 
			
		||||
                        margin: 0.3em 0
 | 
			
		||||
                        label
 | 
			
		||||
                            display: inline-block
 | 
			
		||||
                            margin-right: 1em
 | 
			
		||||
 | 
			
		||||
                    .options div
 | 
			
		||||
                        display: inline-block
 | 
			
		||||
                        margin: 0 1em 0 0
 | 
			
		||||
 | 
			
		||||
                    .messages
 | 
			
		||||
                        margin-top: 1em
 | 
			
		||||
                        .message:last-child
 | 
			
		||||
                            margin-bottom: 0
 | 
			
		||||
 | 
			
		||||
            &:first-child .move-up
 | 
			
		||||
                color: $inactive-link-color
 | 
			
		||||
            &:last-child .move-down
 | 
			
		||||
                color: $inactive-link-color
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,4 @@
 | 
			
		||||
<li class='uploadable'>
 | 
			
		||||
    <div class='controls'>
 | 
			
		||||
        <a href class='move-up'><i class='fa fa-chevron-up'></i></a>
 | 
			
		||||
        <a href class='move-down'><i class='fa fa-chevron-down'></i></a>
 | 
			
		||||
        <a href class='remove'><i class='fa fa-remove'></i></a>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
<li class='uploadable-container'>
 | 
			
		||||
    <div class='thumbnail-wrapper'>
 | 
			
		||||
        <% if (['image'].includes(ctx.uploadable.type)) { %>
 | 
			
		||||
 | 
			
		||||
@ -29,40 +23,58 @@
 | 
			
		||||
        <% } %>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class='file'>
 | 
			
		||||
        <strong><%= ctx.uploadable.name %></strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class='uploadable'>
 | 
			
		||||
        <header>
 | 
			
		||||
            <nav>
 | 
			
		||||
                <ul>
 | 
			
		||||
                    <li><a href class='move-up'><i class='fa fa-chevron-up'></i></a></li>
 | 
			
		||||
                    <li><a href class='move-down'><i class='fa fa-chevron-down'></i></a></li>
 | 
			
		||||
                </ul>
 | 
			
		||||
            </nav>
 | 
			
		||||
            <nav>
 | 
			
		||||
                <ul>
 | 
			
		||||
                    <li><a href class='remove'><i class='fa fa-remove'></i></a></li>
 | 
			
		||||
                </ul>
 | 
			
		||||
            </nav>
 | 
			
		||||
 | 
			
		||||
    <div class='safety'>
 | 
			
		||||
        <% for (let safety of ['safe', 'sketchy', 'unsafe']) { %>
 | 
			
		||||
            <%= ctx.makeRadio({
 | 
			
		||||
                name: 'safety-' + ctx.uploadable.key,
 | 
			
		||||
                value: safety,
 | 
			
		||||
                text: safety[0].toUpperCase() + safety.substr(1),
 | 
			
		||||
                selectedValue: ctx.uploadable.safety,
 | 
			
		||||
            }) %>
 | 
			
		||||
        <% } %>
 | 
			
		||||
    </div>
 | 
			
		||||
            <span class='filename'><%= ctx.uploadable.name %></span>
 | 
			
		||||
        </header>
 | 
			
		||||
 | 
			
		||||
    <div class='options'>
 | 
			
		||||
        <% if (ctx.canUploadAnonymously) { %>
 | 
			
		||||
            <div class='anonymous'>
 | 
			
		||||
                <%= ctx.makeCheckbox({
 | 
			
		||||
                    text: 'Upload anonymously',
 | 
			
		||||
                    name: 'anonymous',
 | 
			
		||||
                    checked: ctx.uploadable.anonymous,
 | 
			
		||||
                }) %>
 | 
			
		||||
        <div class='body'>
 | 
			
		||||
            <div class='safety'>
 | 
			
		||||
                <% for (let safety of ['safe', 'sketchy', 'unsafe']) { %>
 | 
			
		||||
                    <%= ctx.makeRadio({
 | 
			
		||||
                        name: 'safety-' + ctx.uploadable.key,
 | 
			
		||||
                        value: safety,
 | 
			
		||||
                        text: safety[0].toUpperCase() + safety.substr(1),
 | 
			
		||||
                        selectedValue: ctx.uploadable.safety,
 | 
			
		||||
                    }) %>
 | 
			
		||||
                <% } %>
 | 
			
		||||
            </div>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
        <% if (['video'].includes(ctx.uploadable.type)) { %>
 | 
			
		||||
            <div class='loop-video'>
 | 
			
		||||
                <%= ctx.makeCheckbox({
 | 
			
		||||
                    text: 'Loop video',
 | 
			
		||||
                    name: 'loop-video',
 | 
			
		||||
                    checked: ctx.uploadable.flags.includes('loop'),
 | 
			
		||||
                }) %>
 | 
			
		||||
            <div class='options'>
 | 
			
		||||
                <% if (ctx.canUploadAnonymously) { %>
 | 
			
		||||
                    <div class='anonymous'>
 | 
			
		||||
                        <%= ctx.makeCheckbox({
 | 
			
		||||
                            text: 'Upload anonymously',
 | 
			
		||||
                            name: 'anonymous',
 | 
			
		||||
                            checked: ctx.uploadable.anonymous,
 | 
			
		||||
                        }) %>
 | 
			
		||||
                    </div>
 | 
			
		||||
                <% } %>
 | 
			
		||||
 | 
			
		||||
                <% if (['video'].includes(ctx.uploadable.type)) { %>
 | 
			
		||||
                    <div class='loop-video'>
 | 
			
		||||
                        <%= ctx.makeCheckbox({
 | 
			
		||||
                            text: 'Loop video',
 | 
			
		||||
                            name: 'loop-video',
 | 
			
		||||
                            checked: ctx.uploadable.flags.includes('loop'),
 | 
			
		||||
                        }) %>
 | 
			
		||||
                    </div>
 | 
			
		||||
                <% } %>
 | 
			
		||||
            </div>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
            <div class='messages'></div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</li>
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,13 @@ const Post = require('../models/post.js');
 | 
			
		||||
const PostUploadView = require('../views/post_upload_view.js');
 | 
			
		||||
const EmptyView = require('../views/empty_view.js');
 | 
			
		||||
 | 
			
		||||
const genericErrorMessage =
 | 
			
		||||
    'One of the posts needs your attention; ' +
 | 
			
		||||
    'click "resume upload" when you\'re ready.';
 | 
			
		||||
 | 
			
		||||
class PostUploadController {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this._lastPromise = null;
 | 
			
		||||
        this._lastCancellablePromise = null;
 | 
			
		||||
 | 
			
		||||
        if (!api.hasPrivilege('posts:create')) {
 | 
			
		||||
            this._view = new EmptyView();
 | 
			
		||||
@ -22,6 +26,7 @@ class PostUploadController {
 | 
			
		||||
        topNavigation.setTitle('Upload');
 | 
			
		||||
        this._view = new PostUploadView({
 | 
			
		||||
            canUploadAnonymously: api.hasPrivilege('posts:create:anonymous'),
 | 
			
		||||
            canViewPosts: api.hasPrivilege('posts:view'),
 | 
			
		||||
        });
 | 
			
		||||
        this._view.addEventListener('change', e => this._evtChange(e));
 | 
			
		||||
        this._view.addEventListener('submit', e => this._evtSubmit(e));
 | 
			
		||||
@ -33,13 +38,13 @@ class PostUploadController {
 | 
			
		||||
            misc.enableExitConfirmation();
 | 
			
		||||
        } else {
 | 
			
		||||
            misc.disableExitConfirmation();
 | 
			
		||||
            this._view.clearMessages();
 | 
			
		||||
        }
 | 
			
		||||
        this._view.clearMessages();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtCancel(e) {
 | 
			
		||||
        if (this._lastPromise) {
 | 
			
		||||
            this._lastPromise.abort();
 | 
			
		||||
        if (this._lastCancellablePromise) {
 | 
			
		||||
            this._lastCancellablePromise.abort();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -47,46 +52,57 @@ class PostUploadController {
 | 
			
		||||
        this._view.disableForm();
 | 
			
		||||
        this._view.clearMessages();
 | 
			
		||||
 | 
			
		||||
        e.detail.uploadables.reduce((promise, uploadable) => {
 | 
			
		||||
            return promise.then(() => {
 | 
			
		||||
                let post = new Post();
 | 
			
		||||
                post.safety = uploadable.safety;
 | 
			
		||||
                post.flags = uploadable.flags;
 | 
			
		||||
                if (uploadable.url) {
 | 
			
		||||
                    post.newContentUrl = uploadable.url;
 | 
			
		||||
                } else {
 | 
			
		||||
                    post.newContent = uploadable.file;
 | 
			
		||||
                }
 | 
			
		||||
        e.detail.uploadables.reduce(
 | 
			
		||||
            (promise, uploadable) =>
 | 
			
		||||
                promise.then(() =>
 | 
			
		||||
                    this._uploadSinglePost(
 | 
			
		||||
                        uploadable, e.detail.skipDuplicates)),
 | 
			
		||||
            Promise.resolve())
 | 
			
		||||
                .then(() => {
 | 
			
		||||
                    this._view.clearMessages();
 | 
			
		||||
                    misc.disableExitConfirmation();
 | 
			
		||||
                    const ctx = router.show('/posts');
 | 
			
		||||
                    ctx.controller.showSuccess('Posts uploaded.');
 | 
			
		||||
                }, errorContext => {
 | 
			
		||||
                    if (errorContext.constructor === Array) {
 | 
			
		||||
                        const [errorMessage, uploadable] = errorContext;
 | 
			
		||||
                        this._view.showError(genericErrorMessage);
 | 
			
		||||
                        this._view.showError(errorMessage, uploadable);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this._view.showError(errorContext);
 | 
			
		||||
                    }
 | 
			
		||||
                    this._view.enableForm();
 | 
			
		||||
                    return Promise.reject();
 | 
			
		||||
                });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                let modelPromise = post.save(uploadable.anonymous);
 | 
			
		||||
                this._lastPromise = modelPromise;
 | 
			
		||||
    _uploadSinglePost(uploadable, skipDuplicates) {
 | 
			
		||||
        let post = new Post();
 | 
			
		||||
        post.safety = uploadable.safety;
 | 
			
		||||
        post.flags = uploadable.flags;
 | 
			
		||||
 | 
			
		||||
                return modelPromise
 | 
			
		||||
                    .then(() => {
 | 
			
		||||
                        this._view.removeUploadable(uploadable);
 | 
			
		||||
                        return Promise.resolve();
 | 
			
		||||
                    }).catch(errorMessage => {
 | 
			
		||||
                        // XXX:
 | 
			
		||||
                        // lame, API eats error codes so we need to match
 | 
			
		||||
                        // messages instead
 | 
			
		||||
                        if (e.detail.skipDuplicates &&
 | 
			
		||||
                                errorMessage.match(/already uploaded/)) {
 | 
			
		||||
                            return Promise.resolve();
 | 
			
		||||
                        }
 | 
			
		||||
                        return Promise.reject(errorMessage);
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
        }, Promise.resolve())
 | 
			
		||||
        if (uploadable.url) {
 | 
			
		||||
            post.newContentUrl = uploadable.url;
 | 
			
		||||
        } else {
 | 
			
		||||
            post.newContent = uploadable.file;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let savePromise = post.save(uploadable.anonymous)
 | 
			
		||||
            .then(() => {
 | 
			
		||||
                misc.disableExitConfirmation();
 | 
			
		||||
                const ctx = router.show('/posts');
 | 
			
		||||
                ctx.controller.showSuccess('Posts uploaded.');
 | 
			
		||||
                this._view.removeUploadable(uploadable);
 | 
			
		||||
                return Promise.resolve();
 | 
			
		||||
            }, errorMessage => {
 | 
			
		||||
                this._view.showError(errorMessage);
 | 
			
		||||
                this._view.enableForm();
 | 
			
		||||
                return Promise.reject();
 | 
			
		||||
                // XXX:
 | 
			
		||||
                // lame, API eats error codes so we need to match
 | 
			
		||||
                // messages instead
 | 
			
		||||
                if (skipDuplicates &&
 | 
			
		||||
                        errorMessage.match(/already uploaded/)) {
 | 
			
		||||
                    return Promise.resolve();
 | 
			
		||||
                }
 | 
			
		||||
                return Promise.reject([errorMessage, uploadable, null]);
 | 
			
		||||
            });
 | 
			
		||||
        this._lastCancellablePromise = savePromise;
 | 
			
		||||
        return savePromise;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -267,25 +267,26 @@ function showMessage(target, message, className) {
 | 
			
		||||
    if (!message) {
 | 
			
		||||
        message = 'Unknown message';
 | 
			
		||||
    }
 | 
			
		||||
    const messagesHolder = target.querySelector('.messages');
 | 
			
		||||
    if (!messagesHolder) {
 | 
			
		||||
    const messagesHolderNode = target.querySelector('.messages');
 | 
			
		||||
    if (!messagesHolderNode) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    /* TODO: animate this */
 | 
			
		||||
    const node = document.createElement('div');
 | 
			
		||||
    node.innerHTML = message.replace(/\n/g, '<br/>');
 | 
			
		||||
    node.classList.add('message');
 | 
			
		||||
    node.classList.add(className);
 | 
			
		||||
    const wrapper = document.createElement('div');
 | 
			
		||||
    wrapper.classList.add('message-wrapper');
 | 
			
		||||
    wrapper.appendChild(node);
 | 
			
		||||
    messagesHolder.appendChild(wrapper);
 | 
			
		||||
    const textNode = document.createElement('div');
 | 
			
		||||
    textNode.innerHTML = message.replace(/\n/g, '<br/>');
 | 
			
		||||
    textNode.classList.add('message');
 | 
			
		||||
    textNode.classList.add(className);
 | 
			
		||||
    const wrapperNode = document.createElement('div');
 | 
			
		||||
    wrapperNode.classList.add('message-wrapper');
 | 
			
		||||
    wrapperNode.appendChild(textNode);
 | 
			
		||||
    messagesHolderNode.appendChild(wrapperNode);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function showError(target, message) {
 | 
			
		||||
    document.oldTitle = document.title;
 | 
			
		||||
    document.title = `! ${document.title}`;
 | 
			
		||||
    if (!document.title.startsWith('!')) {
 | 
			
		||||
        document.oldTitle = document.title;
 | 
			
		||||
        document.title = `! ${document.title}`;
 | 
			
		||||
    }
 | 
			
		||||
    return showMessage(target, misc.formatInlineMarkdown(message), 'error');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -302,9 +303,9 @@ function clearMessages(target) {
 | 
			
		||||
        document.title = document.oldTitle;
 | 
			
		||||
        document.oldTitle = null;
 | 
			
		||||
    }
 | 
			
		||||
    const messagesHolder = target.querySelector('.messages');
 | 
			
		||||
    /* TODO: animate that */
 | 
			
		||||
    emptyContent(messagesHolder);
 | 
			
		||||
    for (let messagesHolderNode of target.querySelectorAll('.messages')) {
 | 
			
		||||
        emptyContent(messagesHolderNode);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function htmlToDom(html) {
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,6 @@ function _mimeTypeToPostType(mimeType) {
 | 
			
		||||
    }[mimeType] || 'unknown';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _listen(rootNode, selector, eventType, handler) {
 | 
			
		||||
    for (let node of rootNode.querySelectorAll(selector)) {
 | 
			
		||||
        node.addEventListener(eventType, e => handler(e));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Uploadable extends events.EventTarget {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
@ -193,8 +187,16 @@ class PostUploadView extends events.EventTarget {
 | 
			
		||||
        views.showSuccess(this._hostNode, message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showError(message) {
 | 
			
		||||
        views.showError(this._hostNode, message);
 | 
			
		||||
    showError(message, uploadable) {
 | 
			
		||||
        this._showMessage(views.showError, message, uploadable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showInfo(message, uploadable) {
 | 
			
		||||
        this._showMessage(views.showInfo, message, uploadable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showMessage(functor, message, uploadable) {
 | 
			
		||||
        functor(uploadable ? uploadable.rowNode : this._hostNode, message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addUploadables(uploadables) {
 | 
			
		||||
@ -207,9 +209,9 @@ class PostUploadView extends events.EventTarget {
 | 
			
		||||
            }
 | 
			
		||||
            this._uploadables.set(uploadable.key, uploadable);
 | 
			
		||||
            this._emit('change');
 | 
			
		||||
            this._createRowNode(uploadable);
 | 
			
		||||
            this._renderRowNode(uploadable);
 | 
			
		||||
            uploadable.addEventListener(
 | 
			
		||||
                'finish', e => this._updateRowNode(e.detail.uploadable));
 | 
			
		||||
                'finish', e => this._updateThumbnailNode(e.detail.uploadable));
 | 
			
		||||
        }
 | 
			
		||||
        if (duplicatesFound) {
 | 
			
		||||
            let message = null;
 | 
			
		||||
@ -236,6 +238,7 @@ class PostUploadView extends events.EventTarget {
 | 
			
		||||
        this._emit('change');
 | 
			
		||||
        if (!this._uploadables.size) {
 | 
			
		||||
            this._formNode.classList.add('inactive');
 | 
			
		||||
            this._submitButtonNode.value = 'Upload all';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -254,9 +257,24 @@ class PostUploadView extends events.EventTarget {
 | 
			
		||||
 | 
			
		||||
    _evtFormSubmit(e) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        for (let uploadable of this._uploadables.values()) {
 | 
			
		||||
            this._updateUploadableFromDom(uploadable);
 | 
			
		||||
        }
 | 
			
		||||
        this._submitButtonNode.value = 'Resume upload';
 | 
			
		||||
        this._emit('submit');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateUploadableFromDom(uploadable) {
 | 
			
		||||
        const rowNode = uploadable.rowNode;
 | 
			
		||||
        uploadable.safety =
 | 
			
		||||
            rowNode.querySelector('.safety input:checked').value;
 | 
			
		||||
        uploadable.anonymous =
 | 
			
		||||
            rowNode.querySelector('.anonymous input').checked;
 | 
			
		||||
        if (rowNode.querySelector('.loop-video input:checked')) {
 | 
			
		||||
            uploadable.flags.push('loop');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtRemoveClick(e, uploadable) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        if (this._uploading) {
 | 
			
		||||
@ -265,48 +283,23 @@ class PostUploadView extends events.EventTarget {
 | 
			
		||||
        this.removeUploadable(uploadable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtMoveUpClick(e, uploadable) {
 | 
			
		||||
    _evtMoveClick(e, uploadable, delta) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        if (this._uploading) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let sortedUploadables = this._getSortedUploadables();
 | 
			
		||||
        if (uploadable.order > 0) {
 | 
			
		||||
            uploadable.order--;
 | 
			
		||||
            const prevUploadable = sortedUploadables[uploadable.order];
 | 
			
		||||
            prevUploadable.order++;
 | 
			
		||||
            uploadable.rowNode.parentNode.insertBefore(
 | 
			
		||||
                uploadable.rowNode, prevUploadable.rowNode);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtMoveDownClick(e, uploadable) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        if (this._uploading) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let sortedUploadables = this._getSortedUploadables();
 | 
			
		||||
        if (uploadable.order + 1 < sortedUploadables.length) {
 | 
			
		||||
            uploadable.order++;
 | 
			
		||||
            const nextUploadable = sortedUploadables[uploadable.order];
 | 
			
		||||
            nextUploadable.order--;
 | 
			
		||||
            uploadable.rowNode.parentNode.insertBefore(
 | 
			
		||||
                nextUploadable.rowNode, uploadable.rowNode);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtSafetyRadioboxChange(e, uploadable) {
 | 
			
		||||
        uploadable.safety = e.target.value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtAnonymityCheckboxChange(e, uploadable) {
 | 
			
		||||
        uploadable.anonymous = e.target.checked;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtLoopVideoCheckboxChange(e, uploadable) {
 | 
			
		||||
        uploadable.flags = uploadable.flags.filter(f => f !== 'loop');
 | 
			
		||||
        if (e.target.checked) {
 | 
			
		||||
            uploadable.flags.push('loop');
 | 
			
		||||
        if ((uploadable.order + delta).between(-1, sortedUploadables.length)) {
 | 
			
		||||
            uploadable.order += delta;
 | 
			
		||||
            const otherUploadable = sortedUploadables[uploadable.order];
 | 
			
		||||
            otherUploadable.order -= delta;
 | 
			
		||||
            if (delta === 1) {
 | 
			
		||||
                uploadable.rowNode.parentNode.insertBefore(
 | 
			
		||||
                    otherUploadable.rowNode, uploadable.rowNode);
 | 
			
		||||
            } else {
 | 
			
		||||
                uploadable.rowNode.parentNode.insertBefore(
 | 
			
		||||
                    uploadable.rowNode, otherUploadable.rowNode);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -333,28 +326,27 @@ class PostUploadView extends events.EventTarget {
 | 
			
		||||
                }}));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createRowNode(uploadable) {
 | 
			
		||||
    _renderRowNode(uploadable) {
 | 
			
		||||
        const rowNode = rowTemplate(Object.assign(
 | 
			
		||||
            {}, this._ctx, {uploadable: uploadable}));
 | 
			
		||||
        this._listNode.appendChild(rowNode);
 | 
			
		||||
        if (uploadable.rowNode) {
 | 
			
		||||
            uploadable.rowNode.parentNode.replaceChild(
 | 
			
		||||
                rowNode, uploadable.rowNode);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._listNode.appendChild(rowNode);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _listen(rowNode, '.safety input', 'change',
 | 
			
		||||
            e => this._evtSafetyRadioboxChange(e, uploadable));
 | 
			
		||||
        _listen(rowNode, '.anonymous input', 'change',
 | 
			
		||||
            e => this._evtAnonymityCheckboxChange(e, uploadable));
 | 
			
		||||
        _listen(rowNode, '.loop-video input', 'change',
 | 
			
		||||
            e => this._evtLoopVideoCheckboxChange(e, uploadable));
 | 
			
		||||
 | 
			
		||||
        _listen(rowNode, 'a.remove', 'click',
 | 
			
		||||
            e => this._evtRemoveClick(e, uploadable));
 | 
			
		||||
        _listen(rowNode, 'a.move-up', 'click',
 | 
			
		||||
            e => this._evtMoveUpClick(e, uploadable));
 | 
			
		||||
        _listen(rowNode, 'a.move-down', 'click',
 | 
			
		||||
            e => this._evtMoveDownClick(e, uploadable));
 | 
			
		||||
        uploadable.rowNode = rowNode;
 | 
			
		||||
 | 
			
		||||
        rowNode.querySelector('a.remove').addEventListener('click',
 | 
			
		||||
            e => this._evtRemoveClick(e, uploadable));
 | 
			
		||||
        rowNode.querySelector('a.move-up').addEventListener('click',
 | 
			
		||||
            e => this._evtMoveClick(e, uploadable, -1));
 | 
			
		||||
        rowNode.querySelector('a.move-down').addEventListener('click',
 | 
			
		||||
            e => this._evtMoveClick(e, uploadable, 1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateRowNode(uploadable) {
 | 
			
		||||
    _updateThumbnailNode(uploadable) {
 | 
			
		||||
        const rowNode = rowTemplate(Object.assign(
 | 
			
		||||
            {}, this._ctx, {uploadable: uploadable}));
 | 
			
		||||
        views.replaceContent(
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user