Add the ability to upload photos from the mobile site
This commit is contained in:
parent
19b9b0edb8
commit
76b1e9b0dc
12 changed files with 338 additions and 9 deletions
|
|
@ -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)
|
* 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)
|
* 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
|
## Bug Fixes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
//= require mbp-respond.min
|
//= require mbp-respond.min
|
||||||
//= require mbp-helper
|
//= require mbp-helper
|
||||||
//= require jquery.autoSuggest.custom
|
//= require jquery.autoSuggest.custom
|
||||||
//= require fileuploader-custom
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(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(
|
||||||
|
"<li class='publisher_photo loading' style='position:relative;'>" +
|
||||||
|
"<img alt=\"Ajax-loader2\" src=\"/assets/ajax-loader2.gif\" />" +
|
||||||
|
"</li>"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
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("<input type='hidden' value='" + id + "' name='photos[]' />");
|
||||||
|
|
||||||
|
// replace image placeholders
|
||||||
|
var img = currentPlaceholder.find('img');
|
||||||
|
img.attr('src', url);
|
||||||
|
img.attr('data-id', id);
|
||||||
|
currentPlaceholder.removeClass('loading');
|
||||||
|
currentPlaceholder.append("<div class='x'>X</div>" +
|
||||||
|
"<div class='circle'></div>");
|
||||||
|
////
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,7 @@ footer {
|
||||||
left: 0;
|
left: 0;
|
||||||
border: none;
|
border: none;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
font-size: larger;
|
font-size: 14px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
@ -1008,7 +1008,11 @@ form#update_profile_form {
|
||||||
}
|
}
|
||||||
|
|
||||||
select#user_language, #user_auto_follow_back_aspect_id, #aspect_ids_ {
|
select#user_language, #user_auto_follow_back_aspect_id, #aspect_ids_ {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select#aspect_ids_ {
|
||||||
|
width: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#file-upload-spinner {
|
#file-upload-spinner {
|
||||||
|
|
@ -1033,4 +1037,137 @@ select#user_language, #user_auto_follow_back_aspect_id, #aspect_ids_ {
|
||||||
|
|
||||||
input#q.search {
|
input#q.search {
|
||||||
margin-bottom: 20px;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class StatusMessagesController < ApplicationController
|
||||||
if @contact
|
if @contact
|
||||||
@aspects_with_person = @contact.aspects
|
@aspects_with_person = @contact.aspects
|
||||||
@aspect_ids = @aspects_with_person.map{|x| x.id}
|
@aspect_ids = @aspects_with_person.map{|x| x.id}
|
||||||
|
gon.aspect_ids = @aspect_ids
|
||||||
@contacts_of_contact = @contact.contacts
|
@contacts_of_contact = @contact.contacts
|
||||||
render :layout => nil
|
render :layout => nil
|
||||||
end
|
end
|
||||||
|
|
@ -30,6 +31,7 @@ class StatusMessagesController < ApplicationController
|
||||||
@aspect = :all
|
@aspect = :all
|
||||||
@aspects = current_user.aspects
|
@aspects = current_user.aspects
|
||||||
@aspect_ids = @aspects.map{ |a| a.id }
|
@aspect_ids = @aspects.map{ |a| a.id }
|
||||||
|
gon.aspect_ids = @aspect_ids
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
- content_for :head do
|
- content_for :head do
|
||||||
= javascript_include_tag :jquery
|
= javascript_include_tag :jquery
|
||||||
|
= javascript_include_tag 'fileuploader-custom'
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,18 @@
|
||||||
-# licensed under the Affero General Public License version 3 or later. See
|
-# licensed under the Affero General Public License version 3 or later. See
|
||||||
-# the COPYRIGHT file.
|
-# the COPYRIGHT file.
|
||||||
|
|
||||||
|
- content_for :head do
|
||||||
|
= jquery_include_tag
|
||||||
|
= javascript_include_tag :main
|
||||||
|
= load_javascript_locales
|
||||||
|
= include_gon
|
||||||
|
|
||||||
- content_for :custom_css do
|
- content_for :custom_css do
|
||||||
:css
|
:css
|
||||||
body {
|
body {
|
||||||
background-color: #eee;
|
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|
|
= form_for StatusMessage.new, {:data => {:ajax => false}} do |status|
|
||||||
#message_container
|
#message_container
|
||||||
= status.hidden_field :provider_display_name, :value => 'mobile'
|
= status.hidden_field :provider_display_name, :value => 'mobile'
|
||||||
|
|
@ -31,4 +34,18 @@
|
||||||
|
|
||||||
- current_user.aspects.each do |aspect|
|
- current_user.aspects.each do |aspect|
|
||||||
%option{:value => aspect.id}
|
%option{:value => aspect.id}
|
||||||
= "· #{aspect.name}"
|
= "· #{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
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ en:
|
||||||
photo_uploader:
|
photo_uploader:
|
||||||
looking_good: "OMG, you look awesome!"
|
looking_good: "OMG, you look awesome!"
|
||||||
completed: "<%= file %> completed"
|
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:
|
tags:
|
||||||
wasnt_that_interesting: "OK, I suppose #<%= tagName %> wasn't all that interesting..."
|
wasnt_that_interesting: "OK, I suppose #<%= tagName %> wasn't all that interesting..."
|
||||||
people:
|
people:
|
||||||
|
|
|
||||||
60
features/posts_from_main_page_mobile.feature
Normal file
60
features/posts_from_main_page_mobile.feature
Normal file
|
|
@ -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
|
||||||
|
|
@ -96,6 +96,10 @@ Then /^the publisher should be expanded$/ do
|
||||||
find("#publisher")["class"].should_not include("closed")
|
find("#publisher")["class"].should_not include("closed")
|
||||||
end
|
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|
|
When /^I append "([^"]*)" to the publisher$/ do |stuff|
|
||||||
elem = find('#status_message_fake_text')
|
elem = find('#status_message_fake_text')
|
||||||
elem.native.send_keys(' ' + stuff)
|
elem.native.send_keys(' ' + stuff)
|
||||||
|
|
@ -105,6 +109,15 @@ When /^I append "([^"]*)" to the publisher$/ do |stuff|
|
||||||
end
|
end
|
||||||
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
|
And /^I want to mention (?:him|her) from the profile$/ do
|
||||||
click_link("Mention")
|
click_link("Mention")
|
||||||
wait_for_ajax_to_finish
|
wait_for_ajax_to_finish
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
When /^I visit the mobile aspects page$/ do
|
When /^I visit the mobile aspects page$/ do
|
||||||
visit('/aspects.mobile')
|
visit('/aspects.mobile')
|
||||||
end
|
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
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,4 @@ When /^I click the show page link for "([^"]*)"$/ do |post_text|
|
||||||
within(find_post_by_text(post_text)) do
|
within(find_post_by_text(post_text)) do
|
||||||
find("time").click
|
find("time").click
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1216,6 +1216,7 @@ qq.extend(qq.UploadHandlerXhr.prototype, {
|
||||||
xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
|
xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
|
||||||
xhr.setRequestHeader("Content-Type", "application/octet-stream");
|
xhr.setRequestHeader("Content-Type", "application/octet-stream");
|
||||||
xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"));
|
xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"));
|
||||||
|
xhr.setRequestHeader("Accept", "application/json");
|
||||||
xhr.send(file);
|
xhr.send(file);
|
||||||
},
|
},
|
||||||
_onComplete: function(id, xhr){
|
_onComplete: function(id, xhr){
|
||||||
Loading…
Reference in a new issue