diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 137590998..4860a53b7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -137,4 +137,21 @@ class ApplicationController < ActionController::Base @tags ||= current_user.followed_tags end + def save_sort_order + if params[:sort_order].present? + session[:sort_order] = (params[:sort_order] == 'created_at') ? 'created_at' : 'updated_at' + elsif session[:sort_order].blank? + session[:sort_order] = 'updated_at' + else + session[:sort_order] = (session[:sort_order] == 'created_at') ? 'created_at' : 'updated_at' + end + end + + def sort_order + is_mobile_device? ? 'created_at' : session[:sort_order] + end + + def max_time + params[:max_time] ? Time.at(params[:max_time].to_i) : Time.now + end end diff --git a/app/controllers/apps_controller.rb b/app/controllers/apps_controller.rb index d964ed530..74b746b04 100644 --- a/app/controllers/apps_controller.rb +++ b/app/controllers/apps_controller.rb @@ -1,12 +1,8 @@ class AppsController < ApplicationController def show @app = 'cubbies' - @posts = ActivityStreams::Photo max_time = params[:max_time] ? Time.at(params[:max_time].to_i) : Time.now - @posts = @posts.where(ActivityStreams::Photo.arel_table[:created_at].lt(max_time) - ).where(:public => true - ).order('posts.created_at DESC' - ).includes(:author => :profile).limit(30) + @posts = ActivityStreams::Photo.where(:public => true).for_a_stream(max_time, 'created_at') @commenting_disabled = true @people = [] @people_count = 0 diff --git a/app/controllers/aspects_controller.rb b/app/controllers/aspects_controller.rb index 66ebf6940..051fb97e2 100644 --- a/app/controllers/aspects_controller.rb +++ b/app/controllers/aspects_controller.rb @@ -2,7 +2,7 @@ # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. -require File.join(Rails.root, "lib", "aspect_stream") +require File.join(Rails.root, "lib", 'stream', "aspect_stream") class AspectsController < ApplicationController before_filter :authenticate_user! @@ -138,18 +138,4 @@ class AspectsController < ApplicationController end private - def save_sort_order - if params[:sort_order].present? - session[:sort_order] = (params[:sort_order] == 'created_at') ? 'created_at' : 'updated_at' - elsif session[:sort_order].blank? - session[:sort_order] = 'created_at' - else - session[:sort_order] = (session[:sort_order] == 'created_at') ? 'created_at' : 'updated_at' - end - end - - def sort_order - is_mobile_device? ? 'created_at' : session[:sort_order] - end - end diff --git a/app/controllers/mentions_controller.rb b/app/controllers/mentions_controller.rb new file mode 100644 index 000000000..16c1ea16d --- /dev/null +++ b/app/controllers/mentions_controller.rb @@ -0,0 +1,20 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require File.join(Rails.root, 'lib','stream', 'mention_stream') + +class MentionsController < ApplicationController + before_filter :authenticate_user! + before_filter :save_sort_order, :only => :index + + def index + @stream = MentionStream.new(current_user, :max_time => params[:max_time], :order => sort_order) + + if params[:only_posts] + render :partial => 'shared/stream', :locals => {:posts => @stream.posts} + else + render 'aspects/index' + end + end +end diff --git a/app/controllers/tag_followings_controller.rb b/app/controllers/tag_followings_controller.rb index 327c7c541..308478523 100644 --- a/app/controllers/tag_followings_controller.rb +++ b/app/controllers/tag_followings_controller.rb @@ -1,12 +1,23 @@ -require File.join(Rails.root, '/lib/tag_stream') +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. +# +require File.join(Rails.root, 'lib', 'stream', 'tag_stream') + class TagFollowingsController < ApplicationController before_filter :authenticate_user! + before_filter :save_sort_order, :only => :index def index - @stream = TagStream.new(current_user) + @stream = TagStream.new(current_user, :max_time => params[:max_time], :order => sort_order) - render 'aspects/index', :locals => {:posts => @stream.posts} + if params[:only_posts] + render :partial => 'shared/stream', :locals => {:posts => @stream.posts} + else + render 'aspects/index' + end end + # POST /tag_followings # POST /tag_followings.xml def create diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index df448d52b..e6cec16a3 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -47,27 +47,17 @@ class TagsController < ApplicationController def show params[:name].downcase! @aspect = :tag + if current_user - @posts = StatusMessage. - joins("LEFT OUTER JOIN post_visibilities ON post_visibilities.post_id = posts.id"). - joins("LEFT OUTER JOIN contacts ON contacts.id = post_visibilities.contact_id"). - where(Contact.arel_table[:user_id].eq(current_user.id).or( - StatusMessage.arel_table[:public].eq(true).or( - StatusMessage.arel_table[:author_id].eq(current_user.person.id) - ) - )).select('DISTINCT posts.*') + @posts = StatusMessage.owned_or_visible_by_user(current_user) else @posts = StatusMessage.all_public end - params[:prefill] = "##{params[:name]} " - @posts = @posts.tagged_with(params[:name]) - - max_time = params[:max_time] ? Time.at(params[:max_time].to_i) : Time.now - @posts = @posts.where(StatusMessage.arel_table[:created_at].lt(max_time)) - @posts = @posts.includes({:author => :profile}, :comments, :photos).order('posts.created_at DESC').limit(15) + @posts = @posts.tagged_with(params[:name]).for_a_stream(max_time, 'created_at') @commenting_disabled = true + params[:prefill] = "##{params[:name]} " if params[:only_posts] render :partial => 'shared/stream', :locals => {:posts => @posts} @@ -84,4 +74,5 @@ class TagsController < ApplicationController end @tag_followed end + end diff --git a/app/helpers/stream_helper.rb b/app/helpers/stream_helper.rb index f747e5fe8..68ef837cd 100644 --- a/app/helpers/stream_helper.rb +++ b/app/helpers/stream_helper.rb @@ -11,20 +11,24 @@ module StreamHelper elsif controller.instance_of?(PeopleController) person_path(@person, :max_time => @posts.last.created_at.to_i) elsif controller.instance_of?(TagFollowingsController) - tag_followings_path(:max_time => @stream.posts.last.created_at.to_i) + tag_followings_path(:max_time => time_for_scroll(opts[:ajax_stream], @stream), :sort_order => session[:sort_order]) + elsif controller.instance_of?(MentionsController) + mentions_path(:max_time => time_for_scroll(opts[:ajax_stream], @stream), :sort_order => session[:sort_order]) elsif controller.instance_of?(AspectsController) - if opts[:ajax_stream] - time = (Time.now() + 1).to_i - - else - time = @stream.posts.last.send(@stream.order.to_sym).to_i - end - aspects_path(:max_time => time, :sort_order => session[:sort_order], :a_ids => @stream.aspect_ids) + aspects_path(:max_time => time_for_scroll(opts[:ajax_stream], @stream), :a_ids => @stream.aspect_ids, :sort_order => session[:sort_order]) else raise 'in order to use pagination for this new controller, update next_page_path in stream helper' end end + def time_for_scroll(ajax_stream, stream) + if ajax_stream || stream.posts.empty? + (Time.now() + 1).to_i + else + stream.posts.last.send(stream.order.to_sym).to_i + end + end + def time_for_sort post if controller.instance_of?(AspectsController) post.send(session[:sort_order].to_sym) diff --git a/app/models/post.rb b/app/models/post.rb index 4743ff48e..b4037e4d6 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -32,7 +32,20 @@ class Post < ActiveRecord::Base validates :guid, :uniqueness => true + #scopes scope :all_public, where(:public => true, :pending => false) + scope :includes_for_a_stream, includes({:author => :profile}, :mentions => {:person => :profile}) #note should include root and photos, but i think those are both on status_message + + def self.for_a_stream(max_time, order) + by_max_time(max_time, order). + includes_for_a_stream. + limit(15) + end + + def self.by_max_time(max_time, order='created_at') + where("posts.#{order} < ?", max_time).order("posts.#{order} desc") + end + ############# def diaspora_handle read_attribute(:diaspora_handle) || self.author.diaspora_handle @@ -140,4 +153,3 @@ class Post < ActiveRecord::Base update_all(:comments_count => self.comments.count) end end - diff --git a/app/models/status_message.rb b/app/models/status_message.rb index 284cb517f..1c0f6b0be 100644 --- a/app/models/status_message.rb +++ b/app/models/status_message.rb @@ -26,6 +26,19 @@ class StatusMessage < Post after_create :create_mentions + #scopes + scope :where_person_is_mentioned, lambda{|person| joins(:mentions).where(:mentions => {:person_id => person.id})} + + def self.owned_or_visible_by_user(user) + joins("LEFT OUTER JOIN post_visibilities ON post_visibilities.post_id = posts.id"). + joins("LEFT OUTER JOIN contacts ON contacts.id = post_visibilities.contact_id"). + where(Contact.arel_table[:user_id].eq(user.id).or( + StatusMessage.arel_table[:public].eq(true).or( + StatusMessage.arel_table[:author_id].eq(user.person.id) + ) + )).select('DISTINCT posts.*') + end + def text(opts = {}) self.formatted_message(opts) end diff --git a/app/views/aspects/_aspect_stream.haml b/app/views/aspects/_aspect_stream.haml index 01791295c..08954106b 100644 --- a/app/views/aspects/_aspect_stream.haml +++ b/app/views/aspects/_aspect_stream.haml @@ -6,15 +6,12 @@ #sort_by = t('.recently') %span.controls - = link_to_if(session[:sort_order] == 'updated_at', t('.posted'), aspects_path(:a_ids => stream.aspect_ids, :sort_order => 'created_at' )) + = link_to_if(session[:sort_order] == 'created_at', t('.commented_on'), stream.link(:sort_order => 'updated_at')) ยท - = link_to_if(session[:sort_order] == 'created_at', t('.commented_on'), aspects_path(:a_ids => stream.aspect_ids, :sort_order => 'updated_at')) + = link_to_if(session[:sort_order] == 'updated_at', t('.posted'), stream.link(:sort_order => 'created_at' )) %h3 - - if stream.for_all_aspects? - = t('.stream') - - else - = stream.aspects.to_sentence + = stream.title = render 'shared/publisher', :selected_aspects => stream.aspects, :aspect_ids => stream.aspect_ids, :for_all_aspects => stream.for_all_aspects?, :aspect => stream.aspect = render 'aspects/no_posts_message' @@ -23,11 +20,7 @@ = render 'aspects/no_contacts_message' #main_stream.stream{:data => {:guids => stream.aspect_ids.join(',')}} - - if stream.ajax_stream? - #pagination - =link_to(t('more'), next_page_path(:ajax_stream => true), :class => 'paginate') - - - elsif stream.posts.length > 0 + - if !stream.ajax_stream? && stream.posts.length > 0 = render 'shared/stream', :posts => stream.posts - #pagination - =link_to(t('more'), next_page_path, :class => 'paginate') + #pagination + =link_to(t('more'), next_page_path(:ajax_stream => stream.ajax_stream?), :class => 'paginate') diff --git a/app/views/aspects/_selected_contacts.html.haml b/app/views/aspects/_selected_contacts.html.haml index 26ab71d88..04291e3a2 100644 --- a/app/views/aspects/_selected_contacts.html.haml +++ b/app/views/aspects/_selected_contacts.html.haml @@ -1,23 +1,13 @@ #selected_aspect_contacts.section .title.no_icon %h5 - - if @stream.for_all_aspects? || @stream.aspect_ids.size > 1 - = "#{t('_contacts')}" - - else - = @stream.aspect.name - = "(#{@stream.people.size})" - + = @stream.contacts_title .content - if @stream.people.size > 0 - for person in @stream.people.sample(15) = person_image_link(person) - - - if @stream.for_all_aspects? || @stream.aspect_ids.size > 1 - = link_to t('.view_all_contacts'), contacts_link, :id => "view_all_contacts_link" - - else - = link_to t('.view_all_contacts'), contacts_path(:a_id => @stream.aspect.id), :id => "view_all_contacts_link" - + = link_to t('.view_all_contacts'), @stream.contacts_link, :id => "view_all_contacts_link" - else = t('.no_contacts') = link_to t('.manage_your_aspects'), contacts_link diff --git a/app/views/aspects/index.html.haml b/app/views/aspects/index.html.haml index d2b59a43f..005764d9a 100644 --- a/app/views/aspects/index.html.haml +++ b/app/views/aspects/index.html.haml @@ -21,6 +21,11 @@ .section = render 'aspects/aspect_listings' + .section + %ul.left_nav + .li + %b= link_to t('.mentions'), mentions_path, :class => 'home_selector' + .section#followed_tags_listing = render 'tags/followed_tags_listings' diff --git a/app/views/tags/_followed_tags_listings.haml b/app/views/tags/_followed_tags_listings.haml index 0b9703666..7bd2e56e9 100644 --- a/app/views/tags/_followed_tags_listings.haml +++ b/app/views/tags/_followed_tags_listings.haml @@ -5,8 +5,7 @@ - if user_signed_in? %ul.left_nav %li - %div.root_element - = t('aspects.index.tags_following') + %b=link_to t('aspects.index.tags_following'), tag_followings_path, :class => 'home_selector' %ul.sub_nav - if tags.size > 0 diff --git a/config/environments/development.rb b/config/environments/development.rb index 3f7a38e3d..18345ee44 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -21,7 +21,6 @@ Diaspora::Application.configure do config.action_mailer.raise_delivery_errors = false config.active_support.deprecation = :log #config.threadsafe! - # Monkeypatch around the nasty "2.5MB exception page" issue, caused by very large environment vars # This snippet via: http://stackoverflow.com/questions/3114993/exception-pages-in-development-mode-take-upwards-of-15-30-seconds-to-render-why # Relevant Rails ticket: https://rails.lighthouseapp.com/projects/8994/tickets/5027-_request_and_responseerb-and-diagnosticserb-take-an-increasingly-long-time-to-render-in-development-with-multiple-show-tables-calls diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index fbe2ed6b7..c5a01e04f 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -106,6 +106,7 @@ en: done_editing: "done editing" aspect_stream: stream: "Stream" + mentions: "Mentions" recently: "recently:" commented_on: "commented on" posted: "posted" @@ -155,6 +156,7 @@ en: acquaintances: "Acquaintances" friends: "Friends" index: + mentions: "Mentions" donate: "Donate" keep_us_running: "Keep %{pod} running fast and buy servers their coffee fix with a monthly donation!" your_aspects: "Your Aspects" @@ -824,7 +826,15 @@ en: index: revoke_access: "Revoke Access" no_applications: "You haven't registered any applications yet." - + + streams: + mentions: + title: "Your Mentions" + contacts_title: "People who mentioned you" + tags: + title: "Posts tagged: %{tags}" + contacts_title: "People who dig these tags" + users: logged_out: diff --git a/config/routes.rb b/config/routes.rb index 78c7cf23f..ed1338728 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -53,7 +53,8 @@ Diaspora::Application.routes.draw do end - # get "tag_followings" => "tag_followings#index", :as => 'tag_followings' + get "tag_followings" => "tag_followings#index", :as => 'tag_followings' + resources :mentions, :only => [:index] get 'tags/:name' => 'tags#show', :as => 'tag' diff --git a/features/step_definitions/custom_web_steps.rb b/features/step_definitions/custom_web_steps.rb index e37235364..17b87b19b 100644 --- a/features/step_definitions/custom_web_steps.rb +++ b/features/step_definitions/custom_web_steps.rb @@ -181,6 +181,7 @@ And /^I scroll down$/ do evaluate_script("window.scrollBy(0,3000000)") sleep 1 wait_until(30) { evaluate_script('$("#infscr-loading:visible").length') == 0 } + And "I wait for the ajax to finish" end Then /^the notification dropdown should be visible$/ do diff --git a/features/step_definitions/session_steps.rb b/features/step_definitions/session_steps.rb index df84adf9b..6ca42600a 100644 --- a/features/step_definitions/session_steps.rb +++ b/features/step_definitions/session_steps.rb @@ -25,6 +25,7 @@ When /^I sign in as "([^"]*)"$/ do |email| @me = User.find_by_email(email) @me.password ||= 'password' Given 'I am signed in' + And 'I wait for the ajax to finish' end When /^I sign in with password "([^"]*)"$/ do |password| diff --git a/lib/base_stream.rb b/lib/base_stream.rb new file mode 100644 index 000000000..d8c0cefcf --- /dev/null +++ b/lib/base_stream.rb @@ -0,0 +1,70 @@ +class BaseStream + attr_accessor :max_time, :order, :user + + def initialize(user, opts={}) + self.user = user + self.max_time = opts[:max_time] + self.order = opts[:order] + end + + + + #requied to implement said stream + def link(opts={}) + Rails.application.routes.url_helpers.mentions_path(opts) + end + + def title + 'a title' + end + + def posts + [] + end + + def people + [] + end + + def contacts_title + "title for a stream" + end + + def contacts_link + '#' + end + + #helpers + def ajax_stream? + false + end + + def for_all_aspects? + true + end + + + #NOTE: MBS bad bad methods the fact we need these means our views are foobared. please kill them and make them + #private methods on the streams that need them + def aspects + @user.aspects + end + + def aspect + aspects.first + end + + def aspect_ids + aspects.map{|x| x.id} + end + + def max_time=(time_string) + @max_time = Time.at(time_string.to_i) unless time_string.blank? + @max_time ||= (Time.now + 1) + end + + def order=(order_string) + @order = order_string + @order ||= 'created_at' + end +end diff --git a/lib/aspect_stream.rb b/lib/stream/aspect_stream.rb similarity index 61% rename from lib/aspect_stream.rb rename to lib/stream/aspect_stream.rb index c12a7e9e4..1ec9b30a6 100644 --- a/lib/aspect_stream.rb +++ b/lib/stream/aspect_stream.rb @@ -2,9 +2,8 @@ # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. -class AspectStream - - attr_reader :max_time, :order +class AspectStream < BaseStream + TYPES_OF_POST_IN_STREAM = ['StatusMessage', 'Reshare', 'ActivityStreams::Photo'] # @param user [User] # @param inputted_aspect_ids [Array] Ids of aspects for given stream @@ -13,10 +12,8 @@ class AspectStream # @opt order [String] Order of posts (i.e. 'created_at', 'updated_at') # @return [void] def initialize(user, inputted_aspect_ids, opts={}) - @user = user + super(user, opts) @inputted_aspect_ids = inputted_aspect_ids - @max_time = opts[:max_time] - @order = opts[:order] end # Filters aspects given the stream's aspect ids on initialization and the user. @@ -26,7 +23,7 @@ class AspectStream # @return [ActiveRecord::Association] Filtered aspects given the stream's user def aspects @aspects ||= lambda do - a = @user.aspects + a = user.aspects a = a.where(:id => @inputted_aspect_ids) if @inputted_aspect_ids.length > 0 a end.call @@ -42,16 +39,20 @@ class AspectStream # @return [ActiveRecord::Association] AR association of posts def posts # NOTE(this should be something like Post.all_for_stream(@user, aspect_ids, {}) that calls visible_posts - @posts ||= @user.visible_posts(:by_members_of => aspect_ids, - :type => ['StatusMessage', 'Reshare', 'ActivityStreams::Photo'], - :order => "#{@order} DESC", - :max_time => @max_time - ).includes(:mentions => {:person => :profile}, :author => :profile) + @posts ||= user.visible_posts(:by_members_of => aspect_ids, + :type => TYPES_OF_POST_IN_STREAM, + :order => "#{order} DESC", + :max_time => max_time + ).for_a_stream(max_time, order) end # @return [ActiveRecord::Association] AR association of people within stream's given aspects def people - @people ||= Person.all_from_aspects(aspect_ids, @user).includes(:profile) + @people ||= Person.all_from_aspects(aspect_ids, user).includes(:profile) + end + + def link(opts={}) + Rails.application.routes.url_helpers.aspects_path(opts.merge(:a_ids => aspect_ids)) end # The first aspect in #aspects, given the stream is not for all aspects, or #aspects size is 1 @@ -67,11 +68,35 @@ class AspectStream for_all_aspects? end + def title + if self.for_all_aspects? + I18n.t('aspects.aspect_stream.stream') + else + self.aspects.to_sentence + end + end + # Determine whether or not the stream is displaying across # all of the user's aspects. # # @return [Boolean] def for_all_aspects? - @all_aspects ||= aspect_ids.length == @user.aspects.size + @all_aspects ||= aspect_ids.length == user.aspects.size + end + + def contacts_title + if self.for_all_aspects? || self.aspect_ids.size > 1 + I18n.t('_contacts') + else + "#{self.aspect.name}(#{self.people.size})" + end + end + + def contacts_link + if for_all_aspects? || aspect_ids.size > 1 + Rails.application.routes.url_helpers.contacts_path + else + Rails.application.routes.url_helpers.contacts_path(:a_id => aspect.id) + end end end diff --git a/lib/stream/mention_stream.rb b/lib/stream/mention_stream.rb new file mode 100644 index 000000000..fbf954ebb --- /dev/null +++ b/lib/stream/mention_stream.rb @@ -0,0 +1,28 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +class MentionStream< BaseStream + def link(opts={}) + Rails.application.routes.url_helpers.mentions_path(opts) + end + + def title + I18n.translate("streams.mentions.title") + end + + + # @return [ActiveRecord::Association] AR association of posts + def posts + @posts ||= StatusMessage.where_person_is_mentioned(self.user.person).for_a_stream(max_time, order) + end + + # @return [ActiveRecord::Association] AR association of people within stream's given aspects + def people + @people ||= posts.map{|p| p.author}.uniq + end + + def contacts_title + I18n.translate('streams.mentions.contacts_title') + end +end diff --git a/lib/stream/tag_stream.rb b/lib/stream/tag_stream.rb new file mode 100644 index 000000000..bf1855157 --- /dev/null +++ b/lib/stream/tag_stream.rb @@ -0,0 +1,49 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +class TagStream < BaseStream + + def link(opts={}) + Rails.application.routes.url_helpers.tag_followings_path(opts) + end + + def title + tags_titleized + end + + # @return [ActiveRecord::Association] AR association of posts + def posts + if tag_string.empty? + [] + else + @posts ||= StatusMessage.owned_or_visible_by_user(user). + tagged_with([tag_string], :any => true). + where(:public => true). + for_a_stream(@max_time, @order) + end + end + + # @return [ActiveRecord::Association] AR association of people within stream's given aspects + def people + @people ||= posts.map{|p| p.author}.uniq + end + + def contacts_title + I18n.translate('streams.tags.contacts_title') + end + + private + + def tag_string + @tag_string ||= tags.join(', '){|tag| tag.name}.to_s + end + + def tags + @tags = user.followed_tags + end + + def tags_titleized + tag_string.split(',').map{|x| "##{x.strip}"}.to_sentence + end +end diff --git a/lib/tag_stream.rb b/lib/tag_stream.rb deleted file mode 100644 index 3ec31f303..000000000 --- a/lib/tag_stream.rb +++ /dev/null @@ -1,66 +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. - -class TagStream - - attr_reader :max_time, :order - - # @param user [User] - # @param inputted_aspect_ids [Array] Ids of aspects for given stream - # @param aspect_ids [Array] Aspects this stream is responsible for - # @opt max_time [Integer] Unix timestamp of stream's post ceiling - # @opt order [String] Order of posts (i.e. 'created_at', 'updated_at') - # @return [void] - def initialize(user, opts={}) - @tags = user.followed_tags - @tag_string = @tags.join(', '){|tag| tag.name}.to_sym - @user = user - @max_time = opts[:max_time] - @order = opts[:order] - end - - # Filters aspects given the stream's aspect ids on initialization and the user. - # Will disclude aspects from inputted aspect ids if user is not associated with their - # target aspects. - # - # @return [ActiveRecord::Association] Filtered aspects given the stream's user - def aspects - [@tag_string] - end - - # Maps ids into an array from #aspects - # - # @return [Array] Aspect ids - def aspect_ids - [] - end - - # @return [ActiveRecord::Association] AR association of posts - def posts - # NOTE(this should be something like Post.all_for_stream(@user, aspect_ids, {}) that calls visible_posts - @posts ||= StatusMessage.tagged_with([@tag_string], :any => true) - - - end - - # @return [ActiveRecord::Association] AR association of people within stream's given aspects - def people - @people ||= posts.map{|p| p.author}.uniq - end - - # The first aspect in #aspects, given the stream is not for all aspects, or #aspects size is 1 - # @note aspects.first is used for mobile. NOTE(this is a hack and should be fixed) - # @return [Aspect,Symbol] - def aspect - @tags_string - end - - # Determine whether or not the stream is displaying across - # all of the user's aspects. - # - # @return [Boolean] - def for_all_aspects? - true - end -end diff --git a/public/javascripts/pages/mentions-index.js b/public/javascripts/pages/mentions-index.js new file mode 100644 index 000000000..41fb16713 --- /dev/null +++ b/public/javascripts/pages/mentions-index.js @@ -0,0 +1,8 @@ +Diaspora.Pages.MentionsIndex = function() { + var self = this; + + this.subscribe("page/ready", function(evt, document) { + self.stream = self.instantiate("Stream", document.find("#aspect_stream_container")); + self.infiniteScroll = self.instantiate("InfiniteScroll"); + }); +}; diff --git a/public/javascripts/pages/tag-followings-index.js b/public/javascripts/pages/tag-followings-index.js new file mode 100644 index 000000000..0dad173df --- /dev/null +++ b/public/javascripts/pages/tag-followings-index.js @@ -0,0 +1,8 @@ +Diaspora.Pages.TagFollowingsIndex = function() { + var self = this; + + this.subscribe("page/ready", function(evt, document) { + self.stream = self.instantiate("Stream", document.find("#aspect_stream_container")); + self.infiniteScroll = self.instantiate("InfiniteScroll"); + }); +}; diff --git a/spec/lib/base_stream_spec.rb b/spec/lib/base_stream_spec.rb new file mode 100644 index 000000000..1083821a3 --- /dev/null +++ b/spec/lib/base_stream_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' +require File.join(Rails.root, 'spec', 'shared_behaviors', 'stream') +describe BaseStream do + before do + @stream = BaseStream.new(stub) + end + + describe 'shared behaviors' do + it_should_behave_like 'it is a stream' + end +end diff --git a/spec/lib/aspect_stream_spec.rb b/spec/lib/stream/aspect_stream_spec.rb similarity index 94% rename from spec/lib/aspect_stream_spec.rb rename to spec/lib/stream/aspect_stream_spec.rb index f08acdc47..381af67f0 100644 --- a/spec/lib/aspect_stream_spec.rb +++ b/spec/lib/stream/aspect_stream_spec.rb @@ -2,7 +2,7 @@ # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. -require 'aspect_stream' +require 'spec_helper' describe AspectStream do describe '#aspects' do @@ -70,7 +70,7 @@ describe AspectStream do it 'respects max_time' do stream = AspectStream.new(@alice, [1,2], :max_time => 123) - @alice.should_receive(:visible_posts).with(hash_including(:max_time => 123)).and_return(stub.as_null_object) + @alice.should_receive(:visible_posts).with(hash_including(:max_time => instance_of(Time))).and_return(stub.as_null_object) stream.posts end end @@ -138,4 +138,10 @@ describe AspectStream do @stream.ajax_stream?.should be_false end end + describe 'shared behaviors' do + before do + @stream = AspectStream.new(alice, alice.aspects.map(&:id)) + end + it_should_behave_like 'it is a stream' + end end diff --git a/spec/lib/stream/mention_stream_spec.rb b/spec/lib/stream/mention_stream_spec.rb new file mode 100644 index 000000000..d92b3f8c3 --- /dev/null +++ b/spec/lib/stream/mention_stream_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' +require File.join(Rails.root, 'spec', 'shared_behaviors', 'stream') + +describe MentionStream do + before do + @stream = MentionStream.new(Factory(:user), :max_time => Time.now, :order => 'updated_at') + end + + describe 'shared behaviors' do + it_should_behave_like 'it is a stream' + end +end diff --git a/spec/lib/stream/tag_stream_spec.rb b/spec/lib/stream/tag_stream_spec.rb new file mode 100644 index 000000000..93050c839 --- /dev/null +++ b/spec/lib/stream/tag_stream_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' +require File.join(Rails.root, 'spec', 'shared_behaviors', 'stream') + +describe TagStream do + before do + @stream = TagStream.new(Factory(:user), :max_time => Time.now, :order => 'updated_at') + end + + describe 'shared behaviors' do + it_should_behave_like 'it is a stream' + end +end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 07701aeb1..27ee97472 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -10,6 +10,47 @@ describe Post do @aspect = @user.aspects.create(:name => "winners") end + describe 'scopes' do + describe '.for_a_stream' do + before do + time_interval = 1000 + time_past = 1000000 + @posts = (1..3).map do |n| + aspect_to_post = alice.aspects.where(:name => "generic").first + post = alice.post :status_message, :text => "#{alice.username} - #{n}", :to => aspect_to_post.id + post.created_at = (post.created_at-time_past) - time_interval + post.updated_at = (post.updated_at-time_past) + time_interval + post.save + time_interval += 1000 + post + end + end + + it 'returns the posts ordered and limited by unix time' do + Post.for_a_stream(Time.now + 1, "created_at").should == @posts + Post.for_a_stream(Time.now + 1, "updated_at").should == @posts.reverse + end + + it 'includes everything in .includes_for_a_stream' do + Post.should_receive(:includes_for_a_stream) + Post.for_a_stream(Time.now + 1, "created_at") + end + it 'is limited to 15 posts' do + Post.stub(:by_max_time).and_return(Post) + Post.stub(:includes_for_a_stream).and_return(Post) + Post.should_receive(:limit) + Post.for_a_stream(Time.now + 1, "created_at") + end + end + + describe 'includes for a stream' do + it 'inclues author profile and mentions' + it 'should include photos and root of reshares(but does not)' + end + + end + + describe 'validations' do it 'validates uniqueness of guid and does not throw a db error' do message = Factory(:status_message) diff --git a/spec/models/status_message_spec.rb b/spec/models/status_message_spec.rb index 826dcd4e8..b4d653488 100644 --- a/spec/models/status_message_spec.rb +++ b/spec/models/status_message_spec.rb @@ -17,6 +17,51 @@ describe StatusMessage do @aspect = @user.aspects.first end + describe 'scopes' do + describe '.where_person_is_mentioned' do + it 'returns status messages where the given person is mentioned' do + @bo = bob.person + @test_string = "@{Daniel; #{@bo.diaspora_handle}} can mention people like Raph" + + Factory.create(:status_message, :text => @test_string ) + Factory.create(:status_message, :text => @test_string ) + Factory(:status_message) + + StatusMessage.where_person_is_mentioned(@bo).count.should == 2 + end + end + + describe '.owned_or_visible_by_user' do + before do + @you = bob + @public_post = Factory(:status_message, :public => true) + @your_post = Factory(:status_message, :author => @you.person) + @post_from_contact = eve.post(:status_message, :text => 'wooo', :to => eve.aspects.where(:name => 'generic').first) + @post_from_stranger = Factory(:status_message, :public => false) + end + + it 'returns post from your contacts' do + StatusMessage.owned_or_visible_by_user(@you).should include(@post_from_contact) + end + + it 'returns your posts' do + StatusMessage.owned_or_visible_by_user(@you).should include(@your_post) + end + + it 'returns public posts' do + StatusMessage.owned_or_visible_by_user(@you).should include(@public_post) + end + + it 'does not return non contacts, non-public post' do + StatusMessage.owned_or_visible_by_user(@you).should_not include(@post_from_stranger) + end + + it 'should return the three visible posts' do + StatusMessage.owned_or_visible_by_user(@you).count.should == 3 + end + end + end + describe '.before_create' do it 'calls build_tags' do status = Factory.build(:status_message) diff --git a/spec/shared_behaviors/stream.rb b/spec/shared_behaviors/stream.rb new file mode 100644 index 000000000..01af8b497 --- /dev/null +++ b/spec/shared_behaviors/stream.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe 'Streams' do + shared_examples_for 'it is a stream' do + context 'required methods for display' do + it '#title' do + @stream.title.should_not be_nil + end + + it '#posts' do + @stream.posts.should_not be_nil + end + + it '#people' do + @stream.people.should_not be_nil + end + + it 'has a #contacts title' do + @stream.contacts_title.should_not be_nil + end + + it 'has a contacts link' do + @stream.contacts_link.should_not be_nil + end + + it 'responds to ajax_stream' do + @stream.ajax_stream?.should_not be_nil + end + + it 'responds to ajax_stream' do + @stream.ajax_stream?.should_not be_nil + end + + it 'should make the stream a time object' do + @stream.max_time = 123 + @stream.max_time.should be_a(Time) + end + + it 'should default order to created_at' do + @stream.order=nil + @stream.order.should == 'created_at' + end + end + end +end