From 253ab23f5e773150271093d03454a80809c2274c Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Thu, 29 Aug 2013 13:05:46 +0200 Subject: [PATCH 1/7] publisher: extract services into subview --- .../app/views/publisher/services.js | 87 ++++++++++--------- .../javascripts/app/views/publisher_view.js | 20 +++-- 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/app/assets/javascripts/app/views/publisher/services.js b/app/assets/javascripts/app/views/publisher/services.js index f8f250093..fd2a8c534 100644 --- a/app/assets/javascripts/app/views/publisher/services.js +++ b/app/assets/javascripts/app/views/publisher/services.js @@ -3,49 +3,58 @@ * the COPYRIGHT file. */ -(function(){ - // mixin-object, used in conjunction with the publisher to provide the - // functionality for selecting services for cross-posting - app.views.PublisherServices = { +// Services view for the publisher. +// Provides the ability for selecting services for cross-posting +app.views.PublisherServices = Backbone.View.extend({ - // visually toggle the icon and kick-off all other actions for cross-posting - toggleService: function(evt) { - var el = $(evt.target); - var provider = el.attr('id'); + events: { + 'click .service_icon': 'toggleService' + }, - el.toggleClass("dim"); + tooltipSelector: '.service_icon', - this._createCounter(); - this._toggleServiceField(provider); - }, + initialize: function() { + // init tooltip plugin + this.$(this.tooltipSelector).tooltip(); + }, - // keep track of character count - _createCounter: function() { - // remove obsolete counter - this.$('.counter').remove(); + // visually toggle the icon and handle all other actions for cross-posting + toggleService: function(evt) { + var el = $(evt.target); + var provider = el.attr('id'); - // create new counter - var min = 40000; - var a = this.$('.service_icon:not(.dim)'); - if(a.length > 0){ - $.each(a, function(index, value){ - var num = parseInt($(value).attr('maxchar')); - if (min > num) { min = num; } - }); - this.el_input.charCount({allowed: min, warning: min/10 }); - } - }, + el.toggleClass("dim"); - // add or remove the input containing the selected service - _toggleServiceField: function(provider) { - var hidden_field = this.$('input[name="services[]"][value="'+provider+'"]'); - if(hidden_field.length > 0){ - hidden_field.remove(); - } else { - var uid = _.uniqueId('services_'); - this.$(".content_creation form").append( - ''); - } + this._createCounter(); + this._toggleServiceField(provider); + }, + + // keep track of character count + _createCounter: function() { + // remove any obsolete counters + this.options.input.siblings('.counter').remove(); + + // create new counter + var min = 40000; + var a = this.$('.service_icon:not(.dim)'); + if(a.length > 0){ + $.each(a, function(index, value){ + var num = parseInt($(value).attr('maxchar')); + if (min > num) { min = num; } + }); + this.options.input.charCount({allowed: min, warning: min/10 }); } - }; -})(); \ No newline at end of file + }, + + // add or remove the input containing the selected service + _toggleServiceField: function(provider) { + var hidden_field = this.options.form.find('input[name="services[]"][value="'+provider+'"]'); + if(hidden_field.length > 0){ + hidden_field.remove(); + } else { + var uid = _.uniqueId('services_'); + this.options.form.append( + ''); + } + } +}); diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index 85a3af762..62260fada 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -9,7 +9,6 @@ //= require jquery.textchange app.views.Publisher = Backbone.View.extend(_.extend( - app.views.PublisherServices, app.views.PublisherAspectsSelector, app.views.PublisherGettingStarted, { @@ -21,7 +20,6 @@ app.views.Publisher = Backbone.View.extend(_.extend( "click #hide_publisher" : "clear", "submit form" : "createStatusMessage", "click .post_preview_button" : "createPostPreview", - "click .service_icon": "toggleService", "textchange #status_message_fake_text": "handleTextchange", "click .dropdown .dropdown_list li": "toggleAspect", "click #locator" : "showLocation", @@ -29,8 +27,6 @@ app.views.Publisher = Backbone.View.extend(_.extend( "keypress #location_address" : "avoidEnter" }, - tooltipSelector: ".service_icon", - initialize : function(){ // init shortcut references to the various elements this.el_input = this.$('#status_message_fake_text'); @@ -46,9 +42,6 @@ app.views.Publisher = Backbone.View.extend(_.extend( // init autoresize plugin this.el_input.autoResize({ 'extraSpace' : 10, 'maxHeight' : Infinity }); - // init tooltip plugin - this.$(this.tooltipSelector).tooltip(); - // sync textarea content if( this.el_hiddenInput.val() == "" ) { this.el_hiddenInput.val( this.el_input.val() ); @@ -73,10 +66,19 @@ app.views.Publisher = Backbone.View.extend(_.extend( } }); + this.initSubviews(); return this; }, + initSubviews: function() { + this.view_services = new app.views.PublisherServices({ + el: this.$('#publisher_service_icons'), + input: this.el_input, + form: this.$('.content_creation form') + }); + }, + createStatusMessage : function(evt) { if(evt){ evt.preventDefault(); } @@ -260,11 +262,11 @@ app.views.Publisher = Backbone.View.extend(_.extend( }, tryClose : function(){ - // if it is not submittable, close it. + // if it is not submittable, close it. if( !this._submittable() ){ this.close() } - }, + }, open : function() { // visually 'open' the publisher From 9560a8ec61d6beb56979ea1f60e08a9c53508807 Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Fri, 30 Aug 2013 01:56:26 +0200 Subject: [PATCH 2/7] create actual backbone view for publisher aspect selection --- app/assets/javascripts/app/router.js | 2 +- .../app/views/publisher/aspects_selector.js | 83 ------------------ .../publisher/publisher_aspect_selector.js | 87 +++++++++++++++++++ .../{services.js => publisher_services.js} | 0 .../javascripts/app/views/publisher_view.js | 20 +++-- 5 files changed, 103 insertions(+), 89 deletions(-) delete mode 100644 app/assets/javascripts/app/views/publisher/aspects_selector.js create mode 100644 app/assets/javascripts/app/views/publisher/publisher_aspect_selector.js rename app/assets/javascripts/app/views/publisher/{services.js => publisher_services.js} (100%) diff --git a/app/assets/javascripts/app/router.js b/app/assets/javascripts/app/router.js index b4b74eb7f..169c1e32c 100644 --- a/app/assets/javascripts/app/router.js +++ b/app/assets/javascripts/app/router.js @@ -99,7 +99,7 @@ app.Router = Backbone.Router.extend({ app.page = new app.views.Stream({model : app.stream}); app.publisher = app.publisher || new app.views.Publisher({collection : app.stream.items}); - app.publisher.updateAspectsSelector(ids); + app.publisher.setSelectedAspects(ids); var streamFacesView = new app.views.StreamFaces({collection : app.stream.items}); diff --git a/app/assets/javascripts/app/views/publisher/aspects_selector.js b/app/assets/javascripts/app/views/publisher/aspects_selector.js deleted file mode 100644 index d3b364265..000000000 --- a/app/assets/javascripts/app/views/publisher/aspects_selector.js +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright (c) 2010-2012, Diaspora Inc. This file is - * licensed under the Affero General Public License version 3 or later. See - * the COPYRIGHT file. - */ - -(function(){ - // mixin-object, used in conjunction with the publisher to provide the - // functionality for selecting aspects - app.views.PublisherAspectsSelector = { - - // event handler for aspect selection - toggleAspect: function(evt) { - var el = $(evt.target); - var btn = el.parent('.dropdown').find('.button'); - - // visually toggle the aspect selection - if( el.is('.radio') ) { - AspectsDropdown.toggleRadio(el); - } else { - AspectsDropdown.toggleCheckbox(el); - } - - // update the selection summary - this._updateAspectsNumber(el); - - this._updateSelectedAspectIds(); - }, - - updateAspectsSelector: function(ids){ - var el = this.$("ul.dropdown_list"); - this.$('.dropdown_list > li').each(function(){ - var el = $(this); - var aspectId = el.data('aspect_id'); - if (_.contains(ids, aspectId)) { - el.addClass('selected'); - } - else { - el.removeClass('selected'); - } - }); - - this._updateAspectsNumber(el); - this._updateSelectedAspectIds(); - }, - - // take care of the form fields that will indicate the selected aspects - _updateSelectedAspectIds: function() { - var self = this; - - // remove previous selection - this.$('input[name="aspect_ids[]"]').remove(); - - // create fields for current selection - this.$('.dropdown .dropdown_list li.selected').each(function() { - var el = $(this); - var aspectId = el.data('aspect_id'); - - self._addHiddenAspectInput(aspectId); - - // close the dropdown when a radio item was selected - if( el.is('.radio') ) { - el.closest('.dropdown').removeClass('active'); - } - }); - }, - - _updateAspectsNumber: function(el){ - AspectsDropdown.updateNumber( - el.closest(".dropdown_list"), - null, - el.parent().find('li.selected').length, - '' - ); - }, - - _addHiddenAspectInput: function(id) { - var uid = _.uniqueId('aspect_ids_'); - this.$('.content_creation form').append( - '' - ); - } - }; -})(); diff --git a/app/assets/javascripts/app/views/publisher/publisher_aspect_selector.js b/app/assets/javascripts/app/views/publisher/publisher_aspect_selector.js new file mode 100644 index 000000000..49c0f6567 --- /dev/null +++ b/app/assets/javascripts/app/views/publisher/publisher_aspect_selector.js @@ -0,0 +1,87 @@ +/* Copyright (c) 2010-2012, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + +// Aspects view for the publisher. +// Provides the ability to specify the visibility of posted content as public +// or limited to selected aspects +app.views.PublisherAspectSelector = Backbone.View.extend({ + + events: { + "click .dropdown_list > li": "toggleAspect" + }, + + // event handler for aspect selection + toggleAspect: function(evt) { + var el = $(evt.target); + var btn = el.parent('.dropdown').find('.button'); + + // visually toggle the aspect selection + if( el.is('.radio') ) { + AspectsDropdown.toggleRadio(el); + } else { + AspectsDropdown.toggleCheckbox(el); + } + + // update the selection summary + this._updateAspectsNumber(el); + + this._updateSelectedAspectIds(); + }, + + // select a (list of) aspects in the dropdown selector by the given list of ids + updateAspectsSelector: function(ids){ + var el = this.$("ul.dropdown_list"); + this.$('.dropdown_list > li').each(function(){ + var el = $(this); + var aspectId = el.data('aspect_id'); + if (_.contains(ids, aspectId)) { + el.addClass('selected'); + } + else { + el.removeClass('selected'); + } + }); + + this._updateAspectsNumber(el); + this._updateSelectedAspectIds(); + }, + + // take care of the form fields that will indicate the selected aspects + _updateSelectedAspectIds: function() { + var self = this; + + // remove previous selection + this.options.form.find('input[name="aspect_ids[]"]').remove(); + + // create fields for current selection + this.$('.dropdown_list li.selected').each(function() { + var el = $(this); + var aspectId = el.data('aspect_id'); + + self._addHiddenAspectInput(aspectId); + + // close the dropdown when a radio item was selected + if( el.is('.radio') ) { + el.closest('.dropdown').removeClass('active'); + } + }); + }, + + _updateAspectsNumber: function(el){ + AspectsDropdown.updateNumber( + el.closest(".dropdown_list"), + null, + el.parent().find('li.selected').length, + '' + ); + }, + + _addHiddenAspectInput: function(id) { + var uid = _.uniqueId('aspect_ids_'); + this.options.form.append( + '' + ); + } +}); diff --git a/app/assets/javascripts/app/views/publisher/services.js b/app/assets/javascripts/app/views/publisher/publisher_services.js similarity index 100% rename from app/assets/javascripts/app/views/publisher/services.js rename to app/assets/javascripts/app/views/publisher/publisher_services.js diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index 62260fada..9a8cd2ee3 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -3,13 +3,12 @@ * the COPYRIGHT file. */ -//= require ./publisher/services -//= require ./publisher/aspects_selector +//= require ./publisher/publisher_services +//= require ./publisher/publisher_aspect_selector //= require ./publisher/getting_started //= require jquery.textchange app.views.Publisher = Backbone.View.extend(_.extend( - app.views.PublisherAspectsSelector, app.views.PublisherGettingStarted, { el : "#publisher", @@ -21,7 +20,6 @@ app.views.Publisher = Backbone.View.extend(_.extend( "submit form" : "createStatusMessage", "click .post_preview_button" : "createPostPreview", "textchange #status_message_fake_text": "handleTextchange", - "click .dropdown .dropdown_list li": "toggleAspect", "click #locator" : "showLocation", "click #hide_location" : "destroyLocation", "keypress #location_address" : "avoidEnter" @@ -72,11 +70,23 @@ app.views.Publisher = Backbone.View.extend(_.extend( }, initSubviews: function() { + var form = this.$('.content_creation form'); + this.view_services = new app.views.PublisherServices({ el: this.$('#publisher_service_icons'), input: this.el_input, - form: this.$('.content_creation form') + form: form }); + + this.view_aspect_selector = new app.views.PublisherAspectSelector({ + el: this.$('.public_toggle > .dropdown'), + form: form + }); + }, + + // set the selected aspects in the dropdown by their ids + setSelectedAspects: function(ids) { + this.view_aspect_selector.updateAspectsSelector(ids); }, createStatusMessage : function(evt) { From 4090f134f287476051a3ac48326bebb0835aeaa6 Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Fri, 30 Aug 2013 14:51:52 +0200 Subject: [PATCH 3/7] port 'getting started' to a backbone view, revert filename changes --- ..._aspect_selector.js => aspect_selector.js} | 0 .../app/views/publisher/getting_started.js | 110 +++++++++--------- .../{publisher_services.js => services.js} | 0 .../javascripts/app/views/publisher_view.js | 15 ++- 4 files changed, 68 insertions(+), 57 deletions(-) rename app/assets/javascripts/app/views/publisher/{publisher_aspect_selector.js => aspect_selector.js} (100%) rename app/assets/javascripts/app/views/publisher/{publisher_services.js => services.js} (100%) diff --git a/app/assets/javascripts/app/views/publisher/publisher_aspect_selector.js b/app/assets/javascripts/app/views/publisher/aspect_selector.js similarity index 100% rename from app/assets/javascripts/app/views/publisher/publisher_aspect_selector.js rename to app/assets/javascripts/app/views/publisher/aspect_selector.js diff --git a/app/assets/javascripts/app/views/publisher/getting_started.js b/app/assets/javascripts/app/views/publisher/getting_started.js index 815f05585..cee509884 100644 --- a/app/assets/javascripts/app/views/publisher/getting_started.js +++ b/app/assets/javascripts/app/views/publisher/getting_started.js @@ -3,63 +3,63 @@ * the COPYRIGHT file. */ -(function(){ - // mixin-object, used in conjunction with the publisher to provide the - // functionality for displaying 'getting-started' information - app.views.PublisherGettingStarted = { +// Getting started view for the publisher. +// Provides "getting started" popups around the elements of the publisher +// for describing their use to new users. +app.views.PublisherGettingStarted = Backbone.View.extend({ - // initiate all the popover message boxes - triggerGettingStarted: function() { - this._addPopover(this.el_input, { - trigger: 'manual', - offset: 30, - id: 'first_message_explain', - placement: 'right', - html: true - }, 600); - this._addPopover(this.$('.dropdown'), { - trigger: 'manual', - offset: 10, - id: 'message_visibility_explain', - placement: 'bottom', - html: true - }, 1000); - this._addPopover($('#gs-shim'), { - trigger: 'manual', - offset: -5, - id: 'stream_explain', - placement: 'left', - html: true - }, 1400); + // initiate all the popover message boxes + show: function() { + this._addPopover(this.options.el_first_msg, { + trigger: 'manual', + offset: 30, + id: 'first_message_explain', + placement: 'right', + html: true + }, 600); + this._addPopover(this.options.el_visibility, { + trigger: 'manual', + offset: 10, + id: 'message_visibility_explain', + placement: 'bottom', + html: true + }, 1000); + this._addPopover(this.options.el_stream, { + trigger: 'manual', + offset: -5, + id: 'stream_explain', + placement: 'left', + html: true + }, 1400); - // hide some popovers when a post is created - this.$('.button.creation').click(function() { - this.$('.dropdown').popover('hide'); - this.el_input.popover('hide'); - }); - }, + // hide some popovers when a post is created + this.$('.button.creation').click(function() { + this.options.el_visibility.popover('hide'); + this.options.el_first_msg.popover('hide'); + }); + }, - _addPopover: function(el, opts, timeout) { - el.popover(opts); - el.click(function() { + _addPopover: function(el, opts, timeout) { + el.popover(opts); + el.click(function() { + el.popover('hide'); + }); + + // show the popover after the given timeout + setTimeout(function() { + el.popover('show'); + + // disable 'getting started' when the last popover is closed + var popup = el.data('popover').$tip[0]; + var close = $(popup).find('.close'); + + close.click(function() { + if( $('.popover').length==1 ) { + $.get('/getting_started_completed'); + } el.popover('hide'); + return false; }); - - // show the popover after the given timeout - setTimeout(function() { - el.popover('show'); - - // disable 'getting started' when the last popover is closed - var popup = el.data('popover').$tip[0]; - var close = $(popup).find('.close'); - - close.click(function() { - if( $('.popover').length==1 ) { - $.get('/getting_started_completed'); - } - el.popover('hide'); - }); - }, timeout); - } - }; -})(); \ No newline at end of file + }, timeout); + } +}); diff --git a/app/assets/javascripts/app/views/publisher/publisher_services.js b/app/assets/javascripts/app/views/publisher/services.js similarity index 100% rename from app/assets/javascripts/app/views/publisher/publisher_services.js rename to app/assets/javascripts/app/views/publisher/services.js diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index 9a8cd2ee3..cf1f2fda0 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -3,8 +3,8 @@ * the COPYRIGHT file. */ -//= require ./publisher/publisher_services -//= require ./publisher/publisher_aspect_selector +//= require ./publisher/services +//= require ./publisher/aspect_selector //= require ./publisher/getting_started //= require jquery.textchange @@ -82,6 +82,12 @@ app.views.Publisher = Backbone.View.extend(_.extend( el: this.$('.public_toggle > .dropdown'), form: form }); + + this.view_getting_started = new app.views.PublisherGettingStarted({ + el_first_msg: this.el_input, + el_visibility: this.$('.public_toggle > .dropdown'), + el_stream: $('#gs-shim') + }); }, // set the selected aspects in the dropdown by their ids @@ -89,6 +95,11 @@ app.views.Publisher = Backbone.View.extend(_.extend( this.view_aspect_selector.updateAspectsSelector(ids); }, + // show the "getting started" popups around the publisher + triggerGettingStarted: function() { + this.view_getting_started.show(); + }, + createStatusMessage : function(evt) { if(evt){ evt.preventDefault(); } From 4da696253a1354e79cc74c41ecb707254f8edae0 Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Fri, 30 Aug 2013 22:07:06 +0200 Subject: [PATCH 4/7] implement publisher uploader as backbone view --- .../app/views/publisher/uploader.js | 122 ++++++++++++++++++ .../javascripts/app/views/publisher_view.js | 20 ++- app/views/photos/_new_photo.haml | 77 +---------- 3 files changed, 140 insertions(+), 79 deletions(-) create mode 100644 app/assets/javascripts/app/views/publisher/uploader.js diff --git a/app/assets/javascripts/app/views/publisher/uploader.js b/app/assets/javascripts/app/views/publisher/uploader.js new file mode 100644 index 000000000..ef221a802 --- /dev/null +++ b/app/assets/javascripts/app/views/publisher/uploader.js @@ -0,0 +1,122 @@ + +// Uploader view for the publisher. +// Initializes the file uploader plugin and handles callbacks for the upload +// progress. Attaches previews of finished uploads to the publisher. + +app.views.PublisherUploader = Backbone.View.extend({ + + allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'tif', 'tiff'], + sizeLimit: 4194304, // bytes + + initialize: function() { + this.uploader = new qq.FileUploaderBasic({ + element: this.el, + button: this.el, + + //debug: true, + + action: '/photos', + params: { photo: { pending: true }}, + allowedExtensions: this.allowedExtensions, + sizeLimit: this.sizeLimit, + messages: { + typeError: Diaspora.I18n.t('photo_uploader.invalid_ext'), + sizeError: Diaspora.I18n.t('photo_uploader.size_error'), + emptyError: Diaspora.I18n.t('photo_uploader.empty') + }, + onProgress: _.bind(this.progressHandler, this), + onSubmit: _.bind(this.submitHandler, this), + onComplete: _.bind(this.uploadCompleteHandler, this) + + }); + + this.options.publisher.el_photozone.on('click', '.x', _.bind(this._removePhoto, this)); + }, + + progressHandler: function(id, fileName, loaded, total) { + var progress = Math.round(loaded / total * 100); + this.options.el_info.text(fileName + ' ' + progress + '%').fadeTo(200, 1); + }, + + submitHandler: function(id, fileName) { + this.$el.addClass('loading'); + this._addPhotoPlaceholder(); + }, + + // add photo placeholders to the publisher to indicate an upload in progress + _addPhotoPlaceholder: function() { + var publisher = this.options.publisher; + publisher.el_submit.attr('disabled', 'disabled'); + publisher.el_preview.attr('disabled', 'disabled'); + + publisher.el_wrapper.addClass('with_attachments'); + publisher.el_photozone.append( + '
  • ' + + ' '+ + '
  • ' + ); + }, + + uploadCompleteHandler: function(id, fileName, response) { + this.options.el_info.text(Diaspora.I18n.t('photo_uploader.completed', {file: fileName})).fadeTo(2000, 0); + + var id = response.data.photo.id, + url = response.data.photo.unprocessed_image.url; + + this._addFinishedPhoto(id, url); + }, + + // replace the first photo placeholder with the finished uploaded image and + // add the id to the publishers form + _addFinishedPhoto: function(id, url) { + var publisher = this.options.publisher; + + // add form input element + publisher.$('.content_creation form').append( + '' + ); + + // replace placeholder + var placeholder = publisher.el_photozone.find('li.loading').first(); + placeholder + .removeClass('loading') + .append( + '
    X
    '+ + '
    ' + ) + .find('img').attr({'src': url, 'data-id': id}); + + // no more placeholders? enable buttons + if( publisher.el_photozone.find('li.loading').length == 0 ) { + this.$el.removeClass('loading'); + publisher.setButtonsEnabled(true); + } + }, + + // remove an already uploaded photo + _removePhoto: function(evt) { + var self = this; + var photo = $(evt.target).parents('.publisher_photo') + var img = photo.find('img'); + + photo.addClass('dim'); + $.ajax({ + url: '/photos/'+img.attr('data-id'), + dataType: 'json', + type: 'DELETE', + success: function() { + photo.fadeOut(400, function(){ + photo.remove(); + + if( self.options.publisher.$('.publisher_photo').length == 0 ) { + // no more photos left... + self.options.publisher.el_wrapper.removeClass('with_attachments'); + } + }); + } + }); + + return false; + } + +}); diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index cf1f2fda0..b0843becc 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -6,6 +6,7 @@ //= require ./publisher/services //= require ./publisher/aspect_selector //= require ./publisher/getting_started +//= require ./publisher/uploader //= require jquery.textchange app.views.Publisher = Backbone.View.extend(_.extend( @@ -88,6 +89,13 @@ app.views.Publisher = Backbone.View.extend(_.extend( el_visibility: this.$('.public_toggle > .dropdown'), el_stream: $('#gs-shim') }); + + this.view_uploader = new app.views.PublisherUploader({ + el: this.$('#file-upload'), + el_info: this.$('#fileInfo'), + publisher: this + }); + }, // set the selected aspects in the dropdown by their ids @@ -309,14 +317,18 @@ app.views.Publisher = Backbone.View.extend(_.extend( checkSubmitAvailability: function() { if( this._submittable() ) { - this.el_submit.removeAttr('disabled'); - this.el_preview.removeAttr('disabled'); + this.setButtonsEnabled(true); } else { - this.el_submit.attr('disabled','disabled'); - this.el_preview.attr('disabled','disabled'); + this.setButtonsEnabled(false); } }, + setButtonsEnabled: function(bool) { + bool = !bool; + this.el_submit.prop({disabled: bool}); + this.el_preview.prop({disabled: bool}); + }, + // determine submit availability _submittable: function() { var onlyWhitespaces = ($.trim(this.el_input.val()) === ''), diff --git a/app/views/photos/_new_photo.haml b/app/views/photos/_new_photo.haml index 8aa2996ed..b3c2cc201 100644 --- a/app/views/photos/_new_photo.haml +++ b/app/views/photos/_new_photo.haml @@ -8,84 +8,11 @@ var aspectIds = "#{aspect_ids}".split(','); var uploader = new qq.FileUploaderBasic({ - element: document.getElementById('file-upload'), + params: {'photo' : {'pending' : 'true', 'aspect_ids' : aspectIds}, 'set_profile_image' : "#{set_profile_image if defined?(set_profile_image)}"}, - allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'tiff'], - action: "#{photos_path}", - debug: true, - button: document.getElementById('file-upload'), - sizeLimit: 4194304, - onProgress: function(id, fileName, loaded, total){ - var progress = Math.round(loaded / total * 100 ); - $('#fileInfo').text(fileName + ' ' + progress + '%').fadeTo(200, 1); - }, - - messages: { - typeError: "#{t('.invalid_ext')}", - sizeError: "#{t('.size_error')}", - emptyError: "#{t('.empty')}" - }, - - onSubmit: function(id, fileName){ - $('#file-upload').addClass("loading"); - $('#publisher').find("input[type='submit']").attr('disabled','disabled'); - $('#publisher').find("button.post_preview_button").attr('disabled','disabled'); - - app.publisher.el_wrapper.addClass("with_attachments"); - $('#photodropzone').append( - "
  • " + - "#{escape_javascript(image_tag('ajax-loader2.gif'))}" + - "
  • " - ); - }, - - onComplete: function(id, fileName, responseJSON) { - $('#fileInfo').text(Diaspora.I18n.t("photo_uploader.completed", file=fileName)).fadeTo(2000, 0); - var id = responseJSON.data.photo.id, - url = responseJSON.data.photo.unprocessed_image.url, - currentPlaceholder = $('li.loading').first(); - - app.publisher.el_wrapper.addClass("with_attachments"); - $('#new_status_message').append(""); - - // replace image placeholders - var img = currentPlaceholder.find('img'); - img.attr('src', url); - img.attr('data-id', id); - currentPlaceholder.removeClass('loading'); - currentPlaceholder.append("
    X
    " + - "
    "); - //// - - var publisher = $('#publisher'), - textarea = publisher.find('textarea'); - - publisher.find("input[type='submit']").removeAttr('disabled'); - publisher.find("button.post_preview_button").removeAttr('disabled'); - - $('.x').bind('click', function(){ - var photo = $(this).closest('.publisher_photo'); - photo.addClass("dim"); - $.ajax({url: "/photos/" + photo.children('img').attr('data-id'), - dataType: 'json', - type: 'DELETE', - success: function() { - photo.fadeOut(400, function(){ - photo.remove(); - if ( $('.publisher_photo').length == 0){ - app.publisher.el_wrapper.removeClass("with_attachments"); - } - }); - } - }); - }); - }, - - onAllComplete: function(completed_files){ - } }); } - createUploader(); + // createUploader(); From bedfacecfd88af01772de8ef7faa23270a2a9072 Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Sun, 1 Sep 2013 13:11:47 +0200 Subject: [PATCH 5/7] fix jasmine tests --- .../javascripts/app/views/publisher_view.js | 12 +- .../app/views/publisher_view_spec.js | 134 ++++++------------ 2 files changed, 49 insertions(+), 97 deletions(-) diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index b0843becc..39212d244 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -9,8 +9,7 @@ //= require ./publisher/uploader //= require jquery.textchange -app.views.Publisher = Backbone.View.extend(_.extend( - app.views.PublisherGettingStarted, { +app.views.Publisher = Backbone.View.extend({ el : "#publisher", @@ -151,14 +150,15 @@ app.views.Publisher = Backbone.View.extend(_.extend( showLocation: function(){ if($('#location').length == 0){ $('#publisher_textarea_wrapper').after('
    '); - app.views.location = new app.views.Location(); + this.view_locator = new app.views.Location(); } }, // destroys the location destroyLocation: function(){ - if(app.views.location){ - app.views.location.remove(); + if(this.view_locator){ + this.view_locator.remove(); + delete this.view_locator; } }, @@ -346,7 +346,7 @@ app.views.Publisher = Backbone.View.extend(_.extend( }); } -})); +}); // jQuery helper for serializing a
    into JSON $.fn.serializeObject = function() diff --git a/spec/javascripts/app/views/publisher_view_spec.js b/spec/javascripts/app/views/publisher_view_spec.js index ed46a8a87..229fc35e3 100644 --- a/spec/javascripts/app/views/publisher_view_spec.js +++ b/spec/javascripts/app/views/publisher_view_spec.js @@ -18,7 +18,7 @@ describe("app.views.Publisher", function() { it("hides the close button in standalone mode", function() { expect(this.view.$('#hide_publisher').is(':visible')).toBeFalsy(); }); - + it("hides the post preview button in standalone mode", function() { expect(this.view.$('.post_preview_button').is(':visible')).toBeFalsy(); }); @@ -65,7 +65,7 @@ describe("app.views.Publisher", function() { this.view.clear($.Event()); expect(this.view.close).toHaveBeenCalled(); }) - + it("calls removePostPreview", function(){ spyOn(this.view, "removePostPreview"); @@ -121,18 +121,18 @@ describe("app.views.Publisher", function() { var form = this.view.$("form") var submitCallback = jasmine.createSpy().andReturn(false); form.submit(submitCallback); - + var e = $.Event("keydown", { keyCode: 13 }); e.ctrlKey = true; this.view.keyDown(e); - + expect(submitCallback).toHaveBeenCalled(); expect($(this.view.el)).not.toHaveClass("closed"); }) }) }); - context("#toggleService", function(){ + context("services", function(){ beforeEach( function(){ spec.loadFixture('aspects_index_services'); this.view = new app.views.Publisher(); @@ -156,52 +156,42 @@ describe("app.views.Publisher", function() { expect(second.hasClass('dim')).toBeTruthy(); }); - describe("#_createCounter", function() { - it("gets called in when you toggle service icons", function(){ - spyOn(this.view, '_createCounter'); - $(".service_icon").first().trigger('click'); - expect(this.view._createCounter).toHaveBeenCalled(); - }); - - it("removes the 'old' .counter span", function(){ - spyOn($.fn, "remove"); - $(".service_icon").first().trigger('click'); - expect($.fn.remove).toHaveBeenCalled(); - }); + it("creates a counter element", function(){ + expect(this.view.$('.counter').length).toBe(0); + $(".service_icon").first().trigger('click'); + expect(this.view.$('.counter').length).toBe(1); }); - describe("#_toggleServiceField", function() { - it("gets called when you toggle service icons", function(){ - spyOn(this.view, '_toggleServiceField'); - $(".service_icon").first().trigger('click'); - expect(this.view._toggleServiceField).toHaveBeenCalled(); - }); + it("removes any old counters", function(){ + spyOn($.fn, "remove"); + $(".service_icon").first().trigger('click'); + expect($.fn.remove).toHaveBeenCalled(); + }); - it("toggles the hidden input field", function(){ - expect($('input[name="services[]"]').length).toBe(0); - $(".service_icon").first().trigger('click'); - expect($('input[name="services[]"]').length).toBe(1); - $(".service_icon").first().trigger('click'); - expect($('input[name="services[]"]').length).toBe(0); - }); + it("toggles the hidden input field", function(){ + expect(this.view.$('input[name="services[]"]').length).toBe(0); + $(".service_icon").first().trigger('click'); + expect(this.view.$('input[name="services[]"]').length).toBe(1); + $(".service_icon").first().trigger('click'); + expect(this.view.$('input[name="services[]"]').length).toBe(0); + }); - it("toggles the correct input", function() { - var first = $(".service_icon").eq(0); - var second = $(".service_icon").eq(1); + it("toggles the correct input", function() { + var first = $(".service_icon").eq(0); + var second = $(".service_icon").eq(1); - first.trigger('click'); - second.trigger('click'); + first.trigger('click'); + second.trigger('click'); - expect($('input[name="services[]"]').length).toBe(2); + expect(this.view.$('input[name="services[]"]').length).toBe(2); - first.trigger('click'); + first.trigger('click'); - var prov1 = first.attr('id'); - var prov2 = second.attr('id'); + var prov1 = first.attr('id'); + var prov2 = second.attr('id'); - expect($('input[name="services[]"][value="'+prov1+'"]').length).toBe(0); - expect($('input[name="services[]"][value="'+prov2+'"]').length).toBe(1); - }); + expect(this.view.$('input[name="services[]"][value="'+prov1+'"]').length).toBe(0); + expect(this.view.$('input[name="services[]"][value="'+prov2+'"]').length).toBe(1); }); }); @@ -236,18 +226,12 @@ describe("app.views.Publisher", function() { expect(this.check_els.last().hasClass('selected')).toBeTruthy(); }); - describe("#_updateSelectedAspectIds", function(){ + describe("hidden form elements", function(){ beforeEach(function(){ this.li = $('
  • '); this.view.$('.dropdown_list').append(this.li); }); - it("gets called when aspects are selected", function(){ - spyOn(this.view, "_updateSelectedAspectIds"); - this.check_els.last().trigger('click'); - expect(this.view._updateSelectedAspectIds).toHaveBeenCalled(); - }); - it("removes a previous selection and inserts the current one", function() { var selected = this.view.$('input[name="aspect_ids[]"]'); expect(selected.length).toBe(1); @@ -261,13 +245,13 @@ describe("app.views.Publisher", function() { }); it("toggles the same item", function() { - expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(1); + expect(this.view.$('input[name="aspect_ids[]"][value="42"]').length).toBe(0); this.li.trigger('click'); - expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(1); + expect(this.view.$('input[name="aspect_ids[]"][value="42"]').length).toBe(1); this.li.trigger('click'); - expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(0); + expect(this.view.$('input[name="aspect_ids[]"][value="42"]').length).toBe(0); }); it("keeps other fields with different values", function() { @@ -275,30 +259,13 @@ describe("app.views.Publisher", function() { this.view.$('.dropdown_list').append(li2); this.li.trigger('click'); - expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(1); - li2.trigger('click'); - expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(2); + + expect(this.view.$('input[name="aspect_ids[]"][value="42"]').length).toBe(1); + expect(this.view.$('input[name="aspect_ids[]"][value="99"]').length).toBe(1); }); }); - describe("#_addHiddenAspectInput", function(){ - it("gets called when aspects are selected", function(){ - spyOn(this.view, "_addHiddenAspectInput"); - this.check_els.last().trigger('click'); - expect(this.view._addHiddenAspectInput).toHaveBeenCalled(); - }); - - it("adds a hidden input to the form", function(){ - var id = 42; - - this.view._addHiddenAspectInput(id); - var input = this.view.$('input[name="aspect_ids[]"][value="'+id+'"]'); - - expect(input.length).toBe(1); - expect(input.val()).toBe('42'); - }); - }); }); context("locator", function() { @@ -314,9 +281,9 @@ describe("app.views.Publisher", function() { it("Show location", function(){ // inserts location to the DOM; it is the location's view element - setFixtures('
    '); + setFixtures('
    '); - // creates a fake Locator + // creates a fake Locator OSM = {}; OSM.Locator = function(){return { getAddress:function(){}}}; @@ -333,26 +300,11 @@ describe("app.views.Publisher", function() { describe('#destroyLocation', function(){ it("Destroy location if exists", function(){ - - // inserts location to the DOM; it is the location's view element - setFixtures('
    '); - - //Backup original view - var original_location = app.views.Location; - - // creates a new Location view with the #location element - app.views.Location = new Backbone.View({el:"#location"}); - - // creates the mock - app.views.location = sinon.mock(app.views.Location).object; - - // calls the destroy function and test the expected result + setFixtures('
    '); + this.view.view_locator = new app.views.Location({el: "#location"}); this.view.destroyLocation(); expect($("#location").length).toBe(0); - - //Restore view - app.views.Location = original_location; }) }); From 41c0880a9d733f65332947c8a2d78dae3ca217a9 Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Mon, 2 Sep 2013 17:57:13 +0200 Subject: [PATCH 6/7] add jasmine specs for uploader, minor changes --- ...ct_selector.js => aspect_selector_view.js} | 0 ...ing_started.js => getting_started_view.js} | 0 .../{services.js => services_view.js} | 0 .../{uploader.js => uploader_view.js} | 12 +- .../javascripts/app/views/publisher_view.js | 9 +- app/views/shared/_publisher.html.haml | 1 - .../app/views/publisher_view_spec.js | 130 ++++++++++++++++++ 7 files changed, 141 insertions(+), 11 deletions(-) rename app/assets/javascripts/app/views/publisher/{aspect_selector.js => aspect_selector_view.js} (100%) rename app/assets/javascripts/app/views/publisher/{getting_started.js => getting_started_view.js} (100%) rename app/assets/javascripts/app/views/publisher/{services.js => services_view.js} (100%) rename app/assets/javascripts/app/views/publisher/{uploader.js => uploader_view.js} (90%) diff --git a/app/assets/javascripts/app/views/publisher/aspect_selector.js b/app/assets/javascripts/app/views/publisher/aspect_selector_view.js similarity index 100% rename from app/assets/javascripts/app/views/publisher/aspect_selector.js rename to app/assets/javascripts/app/views/publisher/aspect_selector_view.js diff --git a/app/assets/javascripts/app/views/publisher/getting_started.js b/app/assets/javascripts/app/views/publisher/getting_started_view.js similarity index 100% rename from app/assets/javascripts/app/views/publisher/getting_started.js rename to app/assets/javascripts/app/views/publisher/getting_started_view.js diff --git a/app/assets/javascripts/app/views/publisher/services.js b/app/assets/javascripts/app/views/publisher/services_view.js similarity index 100% rename from app/assets/javascripts/app/views/publisher/services.js rename to app/assets/javascripts/app/views/publisher/services_view.js diff --git a/app/assets/javascripts/app/views/publisher/uploader.js b/app/assets/javascripts/app/views/publisher/uploader_view.js similarity index 90% rename from app/assets/javascripts/app/views/publisher/uploader.js rename to app/assets/javascripts/app/views/publisher/uploader_view.js index ef221a802..b41475de6 100644 --- a/app/assets/javascripts/app/views/publisher/uploader.js +++ b/app/assets/javascripts/app/views/publisher/uploader_view.js @@ -30,12 +30,15 @@ app.views.PublisherUploader = Backbone.View.extend({ }); + this.el_info = $('
    '); + this.options.publisher.el_wrapper.before(this.el_info); + this.options.publisher.el_photozone.on('click', '.x', _.bind(this._removePhoto, this)); }, progressHandler: function(id, fileName, loaded, total) { var progress = Math.round(loaded / total * 100); - this.options.el_info.text(fileName + ' ' + progress + '%').fadeTo(200, 1); + this.el_info.text(fileName + ' ' + progress + '%').fadeTo(200, 1); }, submitHandler: function(id, fileName) { @@ -46,8 +49,7 @@ app.views.PublisherUploader = Backbone.View.extend({ // add photo placeholders to the publisher to indicate an upload in progress _addPhotoPlaceholder: function() { var publisher = this.options.publisher; - publisher.el_submit.attr('disabled', 'disabled'); - publisher.el_preview.attr('disabled', 'disabled'); + publisher.setButtonsEnabled(false); publisher.el_wrapper.addClass('with_attachments'); publisher.el_photozone.append( @@ -58,7 +60,7 @@ app.views.PublisherUploader = Backbone.View.extend({ }, uploadCompleteHandler: function(id, fileName, response) { - this.options.el_info.text(Diaspora.I18n.t('photo_uploader.completed', {file: fileName})).fadeTo(2000, 0); + this.el_info.text(Diaspora.I18n.t('photo_uploader.completed', {file: fileName})).fadeTo(2000, 0); var id = response.data.photo.id, url = response.data.photo.unprocessed_image.url; @@ -105,7 +107,7 @@ app.views.PublisherUploader = Backbone.View.extend({ dataType: 'json', type: 'DELETE', success: function() { - photo.fadeOut(400, function(){ + $.when(photo.fadeOut(400)).then(function(){ photo.remove(); if( self.options.publisher.$('.publisher_photo').length == 0 ) { diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index 39212d244..e0f24dbca 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -3,10 +3,10 @@ * the COPYRIGHT file. */ -//= require ./publisher/services -//= require ./publisher/aspect_selector -//= require ./publisher/getting_started -//= require ./publisher/uploader +//= require ./publisher/services_view +//= require ./publisher/aspect_selector_view +//= require ./publisher/getting_started_view +//= require ./publisher/uploader_view //= require jquery.textchange app.views.Publisher = Backbone.View.extend({ @@ -91,7 +91,6 @@ app.views.Publisher = Backbone.View.extend({ this.view_uploader = new app.views.PublisherUploader({ el: this.$('#file-upload'), - el_info: this.$('#fileInfo'), publisher: this }); diff --git a/app/views/shared/_publisher.html.haml b/app/views/shared/_publisher.html.haml index f892b4e44..0180a8108 100644 --- a/app/views/shared/_publisher.html.haml +++ b/app/views/shared/_publisher.html.haml @@ -14,7 +14,6 @@ = status.error_messages %div %params - #fileInfo #publisher_textarea_wrapper = link_to(content_tag(:div, nil, :class => 'icons-deletelabel'), "#", :id => "hide_publisher", :title => t('.discard_post')) %ul#photodropzone diff --git a/spec/javascripts/app/views/publisher_view_spec.js b/spec/javascripts/app/views/publisher_view_spec.js index 229fc35e3..a6a7135b3 100644 --- a/spec/javascripts/app/views/publisher_view_spec.js +++ b/spec/javascripts/app/views/publisher_view_spec.js @@ -320,5 +320,135 @@ describe("app.views.Publisher", function() { }); }); + context('uploader', function() { + beforeEach(function() { + jQuery.fx.off = true; + setFixtures( + '
    '+ + '
    '+ + '
    '+ + '
    '+ + ' '+ + '
    '+ + '
    ' + ); + }); + + it('initializes the file uploader plugin', function() { + spyOn(qq, 'FileUploaderBasic'); + var publisher = new app.views.Publisher(); + + expect(qq.FileUploaderBasic).toHaveBeenCalled(); + }); + + context('event handlers', function() { + beforeEach(function() { + this.view = new app.views.Publisher(); + + // replace the uploader plugin with a dummy object + var upload_view = this.view.view_uploader; + this.uploader = { + onProgress: _.bind(upload_view.progressHandler, upload_view), + onSubmit: _.bind(upload_view.submitHandler, upload_view), + onComplete: _.bind(upload_view.uploadCompleteHandler, upload_view) + }; + upload_view.uploader = this.uploader; + }); + + context('progress', function() { + it('shows progress in percent', function() { + this.uploader.onProgress(null, 'test.jpg', 20, 100); + + var info = this.view.view_uploader.el_info; + expect(info.text()).toContain('test.jpg'); + expect(info.text()).toContain('20%'); + }); + }); + + context('submitting', function() { + beforeEach(function() { + this.uploader.onSubmit(null, 'test.jpg'); + }); + + it('adds a placeholder', function() { + expect(this.view.el_wrapper.attr('class')).toContain('with_attachments'); + expect(this.view.el_photozone.find('li').length).toBe(1); + }); + + it('disables the publisher buttons', function() { + expect(this.view.el_submit.prop('disabled')).toBeTruthy(); + expect(this.view.el_preview.prop('disabled')).toBeTruthy(); + }); + }); + + context('completion', function() { + beforeEach(function() { + Diaspora.I18n.loadLocale({ photo_uploader: { completed: '<%= file %> completed' }}); + $('#photodropzone').html('
  • '); + + this.uploader.onComplete(null, 'test.jpg', { data: { photo: { + id: '987', + unprocessed_image: { url: 'test.jpg' } + }}}); + }); + + it('shows it in text form', function() { + var info = this.view.view_uploader.el_info; + expect(info.text()).toBe(Diaspora.I18n.t('photo_uploader.completed', {file: 'test.jpg'})) + }); + + it('adds a hidden input to the publisher', function() { + var input = this.view.$('input[type="hidden"][value="987"][name="photos[]"]'); + expect(input.length).toBe(1); + }); + + it('replaces the placeholder', function() { + var li = this.view.el_photozone.find('li'); + var img = li.find('img'); + + expect(li.attr('class')).not.toContain('loading'); + expect(img.attr('src')).toBe('test.jpg'); + expect(img.attr('data-id')).toBe('987'); + }); + + it('re-enables the buttons', function() { + expect(this.view.el_submit.prop('disabled')).toBeFalsy(); + expect(this.view.el_preview.prop('disabled')).toBeFalsy(); + }); + }); + }); + + context('photo removal', function() { + beforeEach(function() { + this.view = new app.views.Publisher(); + this.view.el_wrapper.addClass('with_attachments'); + this.view.el_photozone.html( + '
  • .'+ + ' '+ + '
    X
    '+ + '
    '+ + '
  • ' + ); + + spyOn(jQuery, 'ajax').andCallFake(function(opts) { opts.success(); }); + this.view.el_photozone.find('.x').click(); + }); + + it('removes the element', function() { + var photo = this.view.el_photozone.find('li.publisher_photo'); + expect(photo.length).toBe(0); + }); + + it('sends an ajax request', function() { + expect($.ajax).toHaveBeenCalled(); + }); + + it('removes class on wrapper element', function() { + expect(this.view.el_wrapper.attr('class')).not.toContain('with_attachments'); + }); + }); + }); + }); From 306890445e91afab9f483625eccbff4877b24f20 Mon Sep 17 00:00:00 2001 From: Florian Staudacher Date: Tue, 3 Sep 2013 23:15:12 +0200 Subject: [PATCH 7/7] remove obsolete view --- app/views/photos/_new_photo.haml | 18 ------------------ app/views/shared/_publisher.html.haml | 2 -- 2 files changed, 20 deletions(-) delete mode 100644 app/views/photos/_new_photo.haml diff --git a/app/views/photos/_new_photo.haml b/app/views/photos/_new_photo.haml deleted file mode 100644 index b3c2cc201..000000000 --- a/app/views/photos/_new_photo.haml +++ /dev/null @@ -1,18 +0,0 @@ --# Copyright (c) 2010-2011, Diaspora Inc. This file is --# licensed under the Affero General Public License version 3 or later. See --# the COPYRIGHT file. - -:javascript - function createUploader(){ - - var aspectIds = "#{aspect_ids}".split(','); - - var uploader = new qq.FileUploaderBasic({ - - params: {'photo' : {'pending' : 'true', 'aspect_ids' : aspectIds}, 'set_profile_image' : "#{set_profile_image if defined?(set_profile_image)}"}, - - - }); - } - - // createUploader(); diff --git a/app/views/shared/_publisher.html.haml b/app/views/shared/_publisher.html.haml index 0180a8108..f0a38fee6 100644 --- a/app/views/shared/_publisher.html.haml +++ b/app/views/shared/_publisher.html.haml @@ -89,5 +89,3 @@ #publisher_photo_upload - = render 'photos/new_photo', :aspect_ids => aspect_ids.join(',') -