diff --git a/features/show_more.feature b/features/show_more.feature new file mode 100644 index 000000000..e842267e7 --- /dev/null +++ b/features/show_more.feature @@ -0,0 +1,24 @@ +@javascript +Feature: collapsing and expanding long posts + In order to tame the lengths of posts in my stream + As a rocket scientist + I want long posts to be collapsed and expand on click + + Background: + Given a user with username "bob" + And I sign in as "bob@bob.bob" + And I am on the home page + + Scenario: post a very long message + Given I post an extremely long status message + And I go to the home page + + Then the post should be collapsed + + Scenario: expand a very long message + Given I post an extremely long status message + And I go to the home page + And I expand the post + + Then the post should be expanded + diff --git a/features/step_definitions/posts_steps.rb b/features/step_definitions/posts_steps.rb index 3e81c8840..ee7e2854f 100644 --- a/features/step_definitions/posts_steps.rb +++ b/features/step_definitions/posts_steps.rb @@ -2,6 +2,14 @@ Then /^the post "([^"]*)" should be marked nsfw$/ do |text| assert_nsfw(text) end +Then /^the post should be collapsed$/ do + first_post_collapsed? +end + +Then /^the post should be expanded$/ do + first_post_expanded? +end + Then /^I should see an uploaded image within the photo drop zone$/ do find("#photodropzone img")["src"].should include("uploads/images") end @@ -32,6 +40,9 @@ When /^I click on the first block button/ do find(".block_user").click end +When /^I expand the post$/ do + expand_first_post +end Then /^I should see "([^"]*)" as the first post in my stream$/ do |text| first_post_text.should include(text) @@ -43,4 +54,8 @@ end When /^I click the publisher and post "([^"]*)"$/ do |text| click_and_post(text) -end \ No newline at end of file +end + +When /^I post an extremely long status message$/ do + click_and_post("I am a very interesting message " * 64) +end diff --git a/features/support/publishing_cuke_helpers.rb b/features/support/publishing_cuke_helpers.rb index 9895813a4..8f1762434 100644 --- a/features/support/publishing_cuke_helpers.rb +++ b/features/support/publishing_cuke_helpers.rb @@ -17,6 +17,22 @@ module PublishingCukeHelpers ') end + def expand_first_post + find(".stream_element:first .expander").click + wait_until{ !find(".expander").visible? } + end + + def first_post_collapsed? + find(".stream_element:first .collapsible").should have_css(".expander") + find(".stream_element:first .collapsible").has_selector?(".collapsed") + end + + def first_post_expanded? + find(".stream_element:first .expander").should_not be_visible + find(".stream_element:first .collapsible").has_no_selector?(".collapsed") + find(".stream_element:first .collapsible").has_selector?(".opened") + end + def first_post_text find('.stream_element:first .post-content').text() end diff --git a/public/javascripts/app/views/comment_view.js b/public/javascripts/app/views/comment_view.js index 5c04227cb..c85faf7a2 100644 --- a/public/javascripts/app/views/comment_view.js +++ b/public/javascripts/app/views/comment_view.js @@ -4,8 +4,10 @@ app.views.Comment = app.views.Content.extend({ className : "comment media", - events : { - "click .comment_delete": "destroyModel" + events : function() { + return _.extend(app.views.Content.prototype.events, { + "click .comment_delete": "destroyModel" + }); }, presenter : function() { diff --git a/public/javascripts/app/views/content_view.js b/public/javascripts/app/views/content_view.js index 3bc7f0760..bb66c08f1 100644 --- a/public/javascripts/app/views/content_view.js +++ b/public/javascripts/app/views/content_view.js @@ -1,7 +1,8 @@ app.views.Content = app.views.StreamObject.extend({ events: { - "click .oembed .thumb": "showOembedContent" + "click .oembed .thumb": "showOembedContent", + "click .expander": "expandPost" }, presenter : function(){ @@ -41,7 +42,17 @@ app.views.Content = app.views.StreamObject.extend({ var paramSeparator = ( /\?/.test(insertHTML.attr("src")) ) ? "&" : "?"; insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1"); oembed.html( insertHTML ); + }, + + expandPost: function(evt) { + var el = $(this.el).find('.collapsible'); + el.removeClass('collapsed').addClass('opened'); + el.animate({'height':el.data('orig-height')}, 550, function() { + el.css('height','auto'); + }); + $(evt.currentTarget).hide(); } + }); app.views.StatusMessage = app.views.Content.extend({ diff --git a/public/javascripts/app/views/stream_object_view.js b/public/javascripts/app/views/stream_object_view.js index 054cdee88..1f46e732b 100644 --- a/public/javascripts/app/views/stream_object_view.js +++ b/public/javascripts/app/views/stream_object_view.js @@ -1,34 +1,5 @@ app.views.StreamObject = app.views.Base.extend({ - postRenderTemplate : function() { - // collapse long posts - this.$(".collapsible").expander({ - slicePoint: 400, - widow: 12, - expandPrefix: "", - expandText: Diaspora.I18n.t("show_more"), - userCollapse: false, - beforeExpand: function() { - if ($(this).find('.summary').length == 0) { // Sigh. See comments in the spec. - var readMoreDiv = $(this).find('.read-more'); - var lastElementBeforeReadMore = readMoreDiv.prev(); - var firstElementAfterReadMore = readMoreDiv.next().children().first(); - - if (lastElementBeforeReadMore.is('p')) { - lastElementBeforeReadMore.append(firstElementAfterReadMore.html()); - firstElementAfterReadMore.remove(); - - } else if (lastElementBeforeReadMore.is('ul') && firstElementAfterReadMore.is('ul')) { - var firstBullet = firstElementAfterReadMore.children().first(); - lastElementBeforeReadMore.find('li').last().append(firstBullet.html()); - firstBullet.remove(); - } - readMoreDiv.remove(); - } - } - }); - }, - destroyModel: function(evt) { if (evt) { evt.preventDefault(); diff --git a/public/javascripts/app/views/stream_view.js b/public/javascripts/app/views/stream_view.js index 0c3b71d74..b3095b540 100644 --- a/public/javascripts/app/views/stream_view.js +++ b/public/javascripts/app/views/stream_view.js @@ -16,6 +16,7 @@ app.views.Stream = Backbone.View.extend({ setupEvents : function(){ this.stream.bind("fetched", this.removeLoader, this) + this.stream.bind("fetched", this.postRender, this) this.stream.bind("allPostsLoaded", this.unbindInfScroll, this) this.collection.bind("add", this.addPost, this); if(window.app.user()) { @@ -52,6 +53,35 @@ app.views.Stream = Backbone.View.extend({ return this; }, + + postRender : function() { + // collapse long posts + var collHeight = 190, + collElem = $(this.el).find(".collapsible"); + + _.each(collElem, function(elem) { + var elem = $(elem), + oembed = elem.find(".oembed"), + addHeight = 0; + + if( $.trim(oembed.html()) != "" ) { + addHeight = oembed.height(); + } + + // only collapse if height exceeds collHeight+20% + if( elem.height() > ((collHeight*1.2)+addHeight) && !elem.is(".opened") ) { + elem.data("orig-height", elem.height() ); + elem + .height( Math.max(collHeight, addHeight) ) + .addClass("collapsed") + .append( + $('
') + .addClass('expander') + .text( Diaspora.I18n.t("show_more") ) + ); + } + }); + }, appendLoader: function(){ $("#paginate").html($("", { diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 2a48d9bed..03cc59f24 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -1828,6 +1828,26 @@ ul#press_logos :color #777 .collapsible + :overflow hidden + :position relative + + .expander + :position absolute + :bottom 0 + :left 0 + :right 0 + :height 30px + :text-align center + :line-height 48px + :font-size .8em + :color $grey + :text-shadow 0 0 7px #FFF + :cursor pointer + :border-bottom 2px solid #DDD + @include border-radius(0, 0, 3px, 3px) + @include linear-gradient(rgba(255,255,255,0) , #EEE, 0%, 95%) + :background-color transparent + .oembed :background url('/images/ajax-loader2.gif') no-repeat center center :display inline-block diff --git a/spec/javascripts/app/views/stream_view_spec.js b/spec/javascripts/app/views/stream_view_spec.js index b1b8b1630..7f14ae523 100644 --- a/spec/javascripts/app/views/stream_view_spec.js +++ b/spec/javascripts/app/views/stream_view_spec.js @@ -39,73 +39,6 @@ describe("app.views.Stream", function() { }); }); - describe('clicking read more', function() { - var readMoreLink; - - beforeEach(function() { - this.statusMessage = this.stream.posts.models[0]; - this.statusElement = $(this.view.$(".stream_element")[0]); - readMoreLink = this.statusElement.find('.read-more a'); - readMoreLink.text("read more"); - }); - - it('expands the post', function() { - expect(this.statusElement.find('.collapsible .details').attr('style')).toContain('display: none;'); - readMoreLink.click(); - expect(this.statusElement.find('.collapsible .details').attr('style')).not.toContain('display: none;'); - }); - - describe('differences between firefox and webkit/IE', function() { - // Firefox creates 2 divs - one with the summary and one with the whole post. - // It hides the summary and shows the whole post when you click show more. Works great! - // Webkit and IE also create 2 divs, but they split the post - the 1st has the summary and the 2nd has the rest - // of the post. When you click read more, it just shows the 2nd div. This leaves whitespace in odd places. - // So there's a callback that this is testing, that fixes the whitespace on webkit & IE. - var weAreOnFirefox; - - beforeEach(function() { - weAreOnFirefox = this.statusElement.find('.collapsible .summary').length > 0; - }); - - it('removes the read-more div on webkit/IE but leaves it on firefox', function() { - expect(this.statusElement.find('.read-more').length).toEqual(1); - readMoreLink.click(); - if (weAreOnFirefox === true) { - expect(this.statusElement.find('.read-more').length).toEqual(1); - } else { - expect(this.statusElement.find('.read-more').length).toEqual(0); - } - }); - - it('collapses p elements on webkit/IE but leaves them alone on firefox', function() { - expect(this.statusElement.find('.collapsible p').length).toEqual(2); - readMoreLink.click(); - if (weAreOnFirefox === true) { - expect(this.statusElement.find('.collapsible p').length).toEqual(2); - } else { - expect(this.statusElement.find('.collapsible p').length).toEqual(1); - } - }); - - it('collapses li elements on webkit/IE but leaves them alone on firefox', function() { - this.statusMessage = this.stream.posts.models[3]; - this.statusElement = $(this.view.$(".stream_element")[3]); - readMoreLink = this.statusElement.find('.read-more a'); - readMoreLink.text("read more"); - - if (weAreOnFirefox === true) { - expect(this.statusElement.find('.collapsible li').length).toEqual(12); - readMoreLink.click(); - expect(this.statusElement.find('.collapsible li').length).toEqual(12); - } else { - expect(this.statusElement.find('.collapsible li').length).toEqual(9); - readMoreLink.click(); - expect(this.statusElement.find('.collapsible li').length).toEqual(8); - } - }); - }); - }); - describe("infScroll", function() { // NOTE: inf scroll happens at 500px