From 76b1e9b0dc4431b4a779e3321e4cb0d34e7573f1 Mon Sep 17 00:00:00 2001 From: movilla Date: Sun, 24 Feb 2013 03:38:24 +0100 Subject: [PATCH] Add the ability to upload photos from the mobile site --- Changelog.md | 1 + app/assets/javascripts/mobile.js | 84 +++++++++- app/assets/stylesheets/mobile.css.scss | 143 +++++++++++++++++- app/controllers/status_messages_controller.rb | 2 + app/views/profiles/_edit_public.mobile.haml | 1 + app/views/shared/_publisher.mobile.haml | 25 ++- config/locales/javascript/javascript.en.yml | 3 + features/posts_from_main_page_mobile.feature | 60 ++++++++ features/step_definitions/custom_web_steps.rb | 13 ++ features/step_definitions/mobile_steps.rb | 12 ++ features/step_definitions/stream_steps.rb | 2 +- .../assets/javascripts/fileuploader-custom.js | 1 + 12 files changed, 338 insertions(+), 9 deletions(-) create mode 100644 features/posts_from_main_page_mobile.feature rename {vendor => lib}/assets/javascripts/fileuploader-custom.js (99%) diff --git a/Changelog.md b/Changelog.md index 207b81ef70f..cde3325dddd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ * Deleting a post that was shared to Facebook now deletes it from Facebook too [#3980]( https://github.com/diaspora/diaspora/pull/3980) * Include reshares in a users public atom feed [#1781](https://github.com/diaspora/diaspora/issues/1781) +* Add the ability to upload photos from the mobile site. [#4004](https://github.com/diaspora/diaspora/issues/4004) ## Bug Fixes diff --git a/app/assets/javascripts/mobile.js b/app/assets/javascripts/mobile.js index d1c9b111da7..64ff4d9f454 100644 --- a/app/assets/javascripts/mobile.js +++ b/app/assets/javascripts/mobile.js @@ -7,7 +7,7 @@ //= require mbp-respond.min //= require mbp-helper //= require jquery.autoSuggest.custom -//= require fileuploader-custom + $(document).ready(function(){ @@ -273,3 +273,85 @@ $(document).ready(function(){ }); }); + +function createUploader(){ + + var aspectIds = gon.aspect_ids; + + var uploader = new qq.FileUploaderBasic({ + element: document.getElementById('file-upload-publisher'), + params: {'photo' : {'pending' : 'true', 'aspect_ids' : aspectIds},}, + allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'tiff'], + action: "/photos", + debug: true, + button: document.getElementById('file-upload-publisher'), + sizeLimit: 4194304, + + onProgress: function(id, fileName, loaded, total){ + var progress = Math.round(loaded / total * 100 ); + $('#fileInfo-publisher').text(fileName + ' ' + progress + '%'); + }, + + messages: { + typeError: Diaspora.I18n.t("photo_uploader.invalid_ext"), + sizeError: Diaspora.I18n.t("photos.new_photo.size_error"), + emptyError: Diaspora.I18n.t("photos.new_photo.empty") + }, + + onSubmit: function(id, fileName){ + $('#file-upload-publisher').addClass("loading"); + $('#publisher_textarea_wrapper').addClass("with_attachments"); + $('#photodropzone').append( + "
  • " + + "\"Ajax-loader2\"" + + "
  • " + ); + }, + + onComplete: function(id, fileName, responseJSON) { + $('#fileInfo-publisher').text(fileName + ' completed'); + var id = responseJSON.data.photo.id, + url = responseJSON.data.photo.unprocessed_image.url, + currentPlaceholder = $('li.loading').first(); + + $('#publisher_textarea_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'); + + $('.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){ + $('#publisher_textarea_wrapper').removeClass("with_attachments"); + } + }); + } + }); + }); + }, + + onAllComplete: function(completed_files){ + } + + }); +} +createUploader(); diff --git a/app/assets/stylesheets/mobile.css.scss b/app/assets/stylesheets/mobile.css.scss index 275ce4f78c6..6f4783505e7 100644 --- a/app/assets/stylesheets/mobile.css.scss +++ b/app/assets/stylesheets/mobile.css.scss @@ -553,7 +553,7 @@ footer { left: 0; border: none; margin: 10px 0; - font-size: larger; + font-size: 14px; padding: 0; min-width: 100%; } @@ -1008,7 +1008,11 @@ form#update_profile_form { } select#user_language, #user_auto_follow_back_aspect_id, #aspect_ids_ { - padding: 3px; + padding: 3px; +} + +select#aspect_ids_ { + width: auto !important; } #file-upload-spinner { @@ -1033,4 +1037,137 @@ select#user_language, #user_auto_follow_back_aspect_id, #aspect_ids_ { input#q.search { margin-bottom: 20px; -} +} + +#file-upload-publisher { + bottom: 10px !important; + display: inline-block; + padding: 3px 12px; + position: absolute !important; + left: 20px; + cursor: pointer; + img { + @include opacity(1); + vertical-align: bottom; + } + &:hover { + color: #666; + cursor: pointer; + + img { + @include opacity(0.4); + } + } + &:active { + color: #444; + text-shadow: 0 1px 0 #fafafa; + + img { + @include opacity(1); + } + } + &.loading { + @include opacity(1); + } +} + +#publisher_textarea_wrapper { + #hide_publisher { + @include opacity(0.3); + z-index: 5; + padding: 3px; + position: absolute; + right: 6px; + top: 0; + + &:hover { + @include opacity(1); + } + } + + @include border-radius(2px); + + background: #fff; + + &.active { + border: 1px solid #999; + } + position: relative; + padding-right: 10px; + + textarea { + z-index: 2; + border: none; + + &:focus { + outline: 0; + background: transparent; + } + } + + &.with_attachments { + padding-bottom: 55px; + border: 1px solid #CCCCCC; + } + + #photodropzone { + z-index: 3; + position: absolute; + bottom: 0; + left: 0; + padding: 0; + margin: 0 !important; + + li { + display: table-cell; + padding-right: 4px; + img { + max-height: 55px; + } + .circle { + @include border-radius(20px); + display: none; + z-index: 1; + position: absolute; + right: -7px; + top: -5px; + background-color: #333; + width: 20px; + max-width: 20px; + height: 20px; + max-height: 20px; + + border: 1px solid #fff; + } + .x { + display: none; + z-index: 2; + position: absolute; + top: -4px; + right: -1px; + font-size: small; + font-weight: bold; + color: #FFFFFF; + } + + &:hover { + cursor: default; + .circle { + display: block; + } + .x { + display: block; + } + } + } + } +} + +#fileInfo-publisher { + font-size: small; + margin: 5px 2px; + position: absolute; + right: 10px; + text-align: right; + bottom: 40px; +} diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index 1b0e4589ba3..202920a8164 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -23,6 +23,7 @@ def new if @contact @aspects_with_person = @contact.aspects @aspect_ids = @aspects_with_person.map{|x| x.id} + gon.aspect_ids = @aspect_ids @contacts_of_contact = @contact.contacts render :layout => nil end @@ -30,6 +31,7 @@ def new @aspect = :all @aspects = current_user.aspects @aspect_ids = @aspects.map{ |a| a.id } + gon.aspect_ids = @aspect_ids end end diff --git a/app/views/profiles/_edit_public.mobile.haml b/app/views/profiles/_edit_public.mobile.haml index eab701ffe26..e750a8bd618 100644 --- a/app/views/profiles/_edit_public.mobile.haml +++ b/app/views/profiles/_edit_public.mobile.haml @@ -4,6 +4,7 @@ - content_for :head do = javascript_include_tag :jquery + = javascript_include_tag 'fileuploader-custom' :javascript $(document).ready(function () { diff --git a/app/views/shared/_publisher.mobile.haml b/app/views/shared/_publisher.mobile.haml index a2a0b5fbb21..9a8ec63b8ae 100644 --- a/app/views/shared/_publisher.mobile.haml +++ b/app/views/shared/_publisher.mobile.haml @@ -2,15 +2,18 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. +- content_for :head do + = jquery_include_tag + = javascript_include_tag :main + = load_javascript_locales + = include_gon + - content_for :custom_css do :css body { background-color: #eee; } -- content_for :header_action do - = submit_tag t('.share'), :class => 'btn primary', :id => "submit_new_message" - = form_for StatusMessage.new, {:data => {:ajax => false}} do |status| #message_container = status.hidden_field :provider_display_name, :value => 'mobile' @@ -31,4 +34,18 @@ - current_user.aspects.each do |aspect| %option{:value => aspect.id} - = "· #{aspect.name}" \ No newline at end of file + = "· #{aspect.name}" + + %br + %br + + #fileInfo-publisher + #publisher_textarea_wrapper + %ul#photodropzone + + #file-upload-publisher{:title => t('.upload_photos'), :class => 'btn'} + = image_tag 'icons/camera.png', :style => "height: 14px; width: 19px;", :alt => t('.upload_photos').titleize + #publisher_mobile + = submit_tag t('.share'), :class => 'btn primary', :id => "submit_new_message", :style => "position: absolute; right: 20px; bottom: 10px" + + #publisher_photo_upload diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml index 0adf0643278..5fd479acaa4 100644 --- a/config/locales/javascript/javascript.en.yml +++ b/config/locales/javascript/javascript.en.yml @@ -78,6 +78,9 @@ en: photo_uploader: looking_good: "OMG, you look awesome!" completed: "<%= file %> completed" + invalid_ext: "{file} has invalid extension. Only {extensions} are allowed." + size_error: "{file} is too large, maximum file size is {sizeLimit}." + empty: "{file} is empty, please select files again without it." tags: wasnt_that_interesting: "OK, I suppose #<%= tagName %> wasn't all that interesting..." people: diff --git a/features/posts_from_main_page_mobile.feature b/features/posts_from_main_page_mobile.feature new file mode 100644 index 00000000000..14fc2c77294 --- /dev/null +++ b/features/posts_from_main_page_mobile.feature @@ -0,0 +1,60 @@ +@javascript +Feature: posting from the main page + In order to navigate Diaspora* + As a mobile user + I want to tell the world I am eating a yogurt + + Background: + Given following users exist: + | username | + | bob | + | alice | + And I visit the mobile home page + And I sign in as "bob@bob.bob" + And a user with username "bob" is connected with "alice" + Given I have following aspects: + | PostingTo | + | NotPostingThingsHere | + And I have user with username "alice" in an aspect called "PostingTo" + And I have user with username "alice" in an aspect called "NotPostingThingsHere" + + Scenario: posting some text + Given I publisher mobile page + And I append "I am eating yogurt" to the publisher mobile + And I select "Unicorns" from "aspect_ids_" + And I press "Share" + When I visit the mobile stream page + Then I should see "I am eating yogurt" + + Scenario: post a photo without text + Given I publisher mobile page + When I attach the file "spec/fixtures/button.png" to hidden element "file" within "#file-upload-publisher" + And I wait for the ajax to finish + Then I should see an uploaded image within the photo drop zone + When I press "Share" + And I wait for the ajax to finish + When I visit the mobile stream page + Then I should see a "img" within ".stream_element div.photo_attachments" + When I log out + And I sign in as "alice@alice.alice" + When I visit the mobile stream page + Then I should see a "img" within ".stream_element div.photo_attachments" + + Scenario: back out of posting a photo-only post + Given I publisher mobile page + When I attach the file "spec/fixtures/button.png" to hidden element "file" within "#file-upload-publisher" + And I wait for the ajax to finish + And I click to delete the first uploaded photo + And I wait for the ajax to finish + Then I should not see an uploaded image within the photo drop zone + + Scenario: back out of uploading a picture when another has been attached + Given I publisher mobile page + And I append "I am eating yogurt" to the publisher mobile + And I attach the file "spec/fixtures/button.gif" to hidden element "file" within "#file-upload-publisher" + And I attach the file "spec/fixtures/button.png" to hidden element "file" within "#file-upload-publisher" + And I wait for the ajax to finish + And I click to delete the first uploaded photo + And I wait for the ajax to finish + Then I should see an uploaded image within the photo drop zone + And the text area wrapper mobile should be with attachments diff --git a/features/step_definitions/custom_web_steps.rb b/features/step_definitions/custom_web_steps.rb index fac961d1e07..c561ca5da98 100644 --- a/features/step_definitions/custom_web_steps.rb +++ b/features/step_definitions/custom_web_steps.rb @@ -96,6 +96,10 @@ def take_screenshots_with_login find("#publisher")["class"].should_not include("closed") end +Then /^the text area wrapper mobile should be with attachments$/ do + find("#publisher_textarea_wrapper")["class"].should include("with_attachments") +end + When /^I append "([^"]*)" to the publisher$/ do |stuff| elem = find('#status_message_fake_text') elem.native.send_keys(' ' + stuff) @@ -105,6 +109,15 @@ def take_screenshots_with_login end end +When /^I append "([^"]*)" to the publisher mobile$/ do |stuff| + elem = find('#status_message_text') + elem.native.send_keys(' ' + stuff) + + wait_until do + find('#status_message_text').value.include?(stuff) + end +end + And /^I want to mention (?:him|her) from the profile$/ do click_link("Mention") wait_for_ajax_to_finish diff --git a/features/step_definitions/mobile_steps.rb b/features/step_definitions/mobile_steps.rb index 8c356913b7d..bd009bd251a 100644 --- a/features/step_definitions/mobile_steps.rb +++ b/features/step_definitions/mobile_steps.rb @@ -1,3 +1,15 @@ When /^I visit the mobile aspects page$/ do visit('/aspects.mobile') end + +When /^I visit the mobile home page$/ do + visit('/users/sign_in.mobile') +end + +Given /^I publisher mobile page$/ do + visit('/status_messages/new.mobile') +end + +When /^I visit the mobile stream page$/ do + visit('/stream.mobile') +end diff --git a/features/step_definitions/stream_steps.rb b/features/step_definitions/stream_steps.rb index a3ae86712a5..a8a4cebea7e 100644 --- a/features/step_definitions/stream_steps.rb +++ b/features/step_definitions/stream_steps.rb @@ -22,4 +22,4 @@ within(find_post_by_text(post_text)) do find("time").click end -end \ No newline at end of file +end diff --git a/vendor/assets/javascripts/fileuploader-custom.js b/lib/assets/javascripts/fileuploader-custom.js similarity index 99% rename from vendor/assets/javascripts/fileuploader-custom.js rename to lib/assets/javascripts/fileuploader-custom.js index 1840b14fa8b..cfdb366803e 100644 --- a/vendor/assets/javascripts/fileuploader-custom.js +++ b/lib/assets/javascripts/fileuploader-custom.js @@ -1216,6 +1216,7 @@ qq.extend(qq.UploadHandlerXhr.prototype, { xhr.setRequestHeader("X-File-Name", encodeURIComponent(name)); xhr.setRequestHeader("Content-Type", "application/octet-stream"); xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content")); + xhr.setRequestHeader("Accept", "application/json"); xhr.send(file); }, _onComplete: function(id, xhr){