separate story hiding from voting

A story downvote is considered a flag, just meaning the story has
problems and not necessarily that the user wants to ignore it.  By
moving hiding out of Vote and into a new HiddenStory model, a user
can now both downvote/flag and hide separately, or just one or the
other.
This commit is contained in:
joshua stein 2015-02-11 11:37:03 -06:00
parent fe159cc3f7
commit f9b309d342
9 changed files with 76 additions and 29 deletions

View file

@ -237,8 +237,7 @@ class StoriesController < ApplicationController
return render :text => "can't find story", :status => 400 return render :text => "can't find story", :status => 400
end end
Vote.vote_thusly_on_story_or_comment_for_user_because(0, story.id, HiddenStory.hide_story_for_user(story.id, @user.id)
nil, @user.id, "H")
render :text => "ok" render :text => "ok"
end end
@ -248,8 +247,7 @@ class StoriesController < ApplicationController
return render :text => "can't find story", :status => 400 return render :text => "can't find story", :status => 400
end end
Vote.vote_thusly_on_story_or_comment_for_user_because(0, story.id, HiddenStory.where(:user_id => @user.id, :story_id => story.id).delete_all
nil, @user.id, nil)
render :text => "ok" render :text => "ok"
end end
@ -299,9 +297,11 @@ private
if @user if @user
if v = Vote.where(:user_id => @user.id, :story_id => @story.id, if v = Vote.where(:user_id => @user.id, :story_id => @story.id,
:comment_id => nil).first :comment_id => nil).first
@story.vote = v.vote @story.vote = { :vote => v.vote, :reason => v.reason }
end end
@story.is_hidden_by_cur_user = @story.is_hidden_by_user?(@user)
@votes = Vote.comment_votes_by_user_for_story_hash(@user.id, @story.id) @votes = Vote.comment_votes_by_user_for_story_hash(@user.id, @story.id)
@comments.each do |c| @comments.each do |c|
if @votes[c.id] if @votes[c.id]

View file

@ -0,0 +1,11 @@
class HiddenStory < ActiveRecord::Base
belongs_to :user
belongs_to :story
validates_presence_of :user_id, :story_id
def self.hide_story_for_user(story_id, user_id)
HiddenStory.where(:user_id => user_id, :story_id =>
story_id).first_or_initialize.save!
end
end

View file

@ -26,10 +26,16 @@ private
if @user if @user
votes = Vote.votes_by_user_for_stories_hash(@user.id, scope.map(&:id)) votes = Vote.votes_by_user_for_stories_hash(@user.id, scope.map(&:id))
hs = HiddenStory.where(:user_id => @user.id, :story_id =>
scope.map(&:id)).map(&:story_id)
scope.each do |s| scope.each do |s|
if votes[s.id] if votes[s.id]
s.vote = votes[s.id] s.vote = votes[s.id]
end end
if hs.include?(s.id)
s.is_hidden_by_cur_user = true
end
end end
end end
scope scope

View file

@ -31,7 +31,7 @@ class Story < ActiveRecord::Base
RECENT_DAYS = 30 RECENT_DAYS = 30
attr_accessor :vote, :already_posted_story, :fetched_content, :previewing, attr_accessor :vote, :already_posted_story, :fetched_content, :previewing,
:seen_previous :seen_previous, :is_hidden_by_cur_user
attr_accessor :editor, :moderation_reason, :merge_story_short_id attr_accessor :editor, :moderation_reason, :merge_story_short_id
before_validation :assign_short_id_and_upvote, before_validation :assign_short_id_and_upvote,
@ -298,11 +298,11 @@ class Story < ActiveRecord::Base
end end
def hider_count def hider_count
@hider_count ||= Vote.where(:story_id => self.id, :comment_id => nil, @hider_count ||= HiddenStory.where(:story_id => self.id).count
:vote => 0).count
end end
def is_downvotable? def is_downvotable?
return true
if self.created_at if self.created_at
Time.now - self.created_at <= DOWNVOTABLE_DAYS.days Time.now - self.created_at <= DOWNVOTABLE_DAYS.days
else else
@ -328,6 +328,10 @@ class Story < ActiveRecord::Base
is_expired? is_expired?
end end
def is_hidden_by_user?(user)
!!HiddenStory.where(:user_id => user.id, :story_id => self.id).first
end
def is_recent? def is_recent?
self.created_at >= RECENT_DAYS.days.ago self.created_at >= RECENT_DAYS.days.ago
end end

View file

@ -11,7 +11,7 @@ class StoryRepository
def hottest def hottest
hottest = positive_ranked base_scope hottest = positive_ranked base_scope
hottest = filter_downvoted_and_tags hottest hottest = filter_hidden_and_tags hottest
hottest.order('hotness') hottest.order('hotness')
end end
@ -27,7 +27,7 @@ class StoryRepository
end end
def newest def newest
newest = filter_downvoted_and_tags base_scope newest = filter_hidden_and_tags base_scope
newest.order("stories.id DESC") newest.order("stories.id DESC")
end end
@ -94,9 +94,9 @@ private
Story.unmerged.where(is_expired: false) Story.unmerged.where(is_expired: false)
end end
def filter_downvoted_and_tags(scope) def filter_hidden_and_tags(scope)
if @user if @user
scope = filter_downvoted scope scope = filter_hidden scope
end end
if @params[:exclude_tags].try(:any?) if @params[:exclude_tags].try(:any?)
scope = filter_tags scope, @params[:exclude_tags] scope = filter_tags scope, @params[:exclude_tags]
@ -104,20 +104,16 @@ private
scope scope
end end
def filter_downvoted(scope) def filter_hidden(scope)
scope.where(Story.arel_table[:id].not_in(hidden_arel)) scope.where(Story.arel_table[:id].not_in(hidden_arel))
end end
def hidden_arel def hidden_arel
if @user if @user
hidden_arel = Vote.arel_table.where( hidden_arel = HiddenStory.arel_table.where(
Vote.arel_table[:user_id].eq(@user.id) HiddenStory.arel_table[:user_id].eq(@user.id)
).where(
Vote.arel_table[:vote].lteq(0)
).where(
Vote.arel_table[:comment_id].eq(nil)
).project( ).project(
Vote.arel_table[:story_id] HiddenStory.arel_table[:story_id]
) )
end end
end end

View file

@ -28,7 +28,7 @@ class Vote < ActiveRecord::Base
Vote.where(:user_id => user, :story_id => stories, Vote.where(:user_id => user, :story_id => stories,
:comment_id => nil).each do |v| :comment_id => nil).each do |v|
votes[v.story_id] = v.vote votes[v.story_id] = { :vote => v.vote, :reason => v.reason }
end end
votes votes
@ -84,7 +84,7 @@ class Vote < ActiveRecord::Base
v = Vote.where(:user_id => user_id, :story_id => story_id, v = Vote.where(:user_id => user_id, :story_id => story_id,
:comment_id => comment_id).first_or_initialize :comment_id => comment_id).first_or_initialize
if !v.new_record? && v.vote == vote && vote != 0 if !v.new_record? && v.vote == vote
return return
end end
@ -93,7 +93,7 @@ class Vote < ActiveRecord::Base
Vote.transaction do Vote.transaction do
# unvote # unvote
if vote == 0 && reason == nil if vote == 0
# neutralize previous vote # neutralize previous vote
upvote = (v.vote == 1 ? -1 : 0) upvote = (v.vote == 1 ? -1 : 0)
downvote = (v.vote == -1 ? -1 : 0) downvote = (v.vote == -1 ? -1 : 0)

View file

@ -1,7 +1,8 @@
<li id="story_<%= story.short_id %>" data-shortid="<%= story.short_id %>" <li id="story_<%= story.short_id %>" data-shortid="<%= story.short_id %>"
class="story <%= story.vote == 1 ? "upvoted" : "" %> <%= story.vote == -1 ? class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
"downvoted" : "" %> <%= story.vote == 0 ? "hidden" : "" %> <%= story.is_expired? ? <%= story.vote && story.vote[:vote] == -1 ? "downvoted" : "" %>
"expired" : "" %>"> <%= story.is_hidden_by_cur_user ? "hidden" : "" %>
<%= story.is_expired? ? "expired" : "" %>">
<div class="story_liner"> <div class="story_liner">
<div class="voters"> <div class="voters">
<% if @user %> <% if @user %>
@ -100,10 +101,13 @@ class="story <%= story.vote == 1 ? "upvoted" : "" %> <%= story.vote == -1 ?
<% end %> <% end %>
<% end %> <% end %>
<% if !story.is_gone? && @user %> <% if !story.is_gone? && @user %>
<% if @user && @user.can_downvote?(story) %> <% if @user && story.vote && story.vote[:vote] == -1 %>
| <a class="flagger">unflag (<%=
Vote::STORY_REASONS[story.vote[:reason]].to_s.downcase %>)</a>
<% elsif @user && @user.can_downvote?(story) %>
| <a class="flagger">flag</a> | <a class="flagger">flag</a>
<% end %> <% end %>
<% if story.vote == 0 %> <% if story.is_hidden_by_cur_user %>
| <%= link_to "unhide", story_unhide_path(story.short_id), | <%= link_to "unhide", story_unhide_path(story.short_id),
:class => "hider" %> :class => "hider" %>
<% else %> <% else %>

View file

@ -0,0 +1,19 @@
class MoveHiddenVotesToHiddenStory < ActiveRecord::Migration
def up
create_table :hidden_stories do |t|
t.integer :user_id
t.integer :story_id
end
add_index "hidden_stories", ["user_id", "story_id"], :unique => true
Vote.where(:vote => 0).each do |v|
hs = HiddenStory.new
hs.user_id = v.user_id
hs.story_id = v.story_id
hs.save!
end
Vote.where(:vote => 0).delete_all
end
end

View file

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150127180326) do ActiveRecord::Schema.define(version: 20150211170052) do
create_table "comments", force: true do |t| create_table "comments", force: true do |t|
t.datetime "created_at", null: false t.datetime "created_at", null: false
@ -46,6 +46,13 @@ ActiveRecord::Schema.define(version: 20150127180326) do
t.string "link" t.string "link"
end end
create_table "hidden_stories", force: true do |t|
t.integer "user_id"
t.integer "story_id"
end
add_index "hidden_stories", ["user_id", "story_id"], name: "index_hidden_stories_on_user_id_and_story_id", unique: true, using: :btree
create_table "invitation_requests", force: true do |t| create_table "invitation_requests", force: true do |t|
t.string "code" t.string "code"
t.boolean "is_verified", default: false t.boolean "is_verified", default: false