bring back story downvoting, remove low quality option

story hiding is still here, so hopefully this will result in less
bogus story downvotes
This commit is contained in:
joshua stein 2014-03-18 00:06:43 -05:00
parent 48e5a0f130
commit 7f9c227ed0
9 changed files with 117 additions and 11 deletions

View file

@ -7,12 +7,18 @@
var _Lobsters = Class.extend({
curUser: null,
storyDownvoteReasons: { <%= Vote::STORY_REASONS.map{|k,v|
"#{k.inspect}: #{v.inspect}" }.join(", ") %> },
commentDownvoteReasons: { <%= Vote::COMMENT_REASONS.map{|k,v|
"#{k.inspect}: #{v.inspect}" }.join(", ") %> },
upvoteStory: function(voterEl) {
Lobsters.vote("story", voterEl, 1);
},
downvoteStory: function(voterEl) {
Lobsters._showDownvoteWhyAt("story", voterEl, function(k) {
Lobsters.vote("story", voterEl, -1, k); });
},
hideStory: function(hiderEl) {
if (!Lobsters.curUser)
return Lobsters.bounceToLogin();
@ -37,10 +43,17 @@ var _Lobsters = Class.extend({
Lobsters.vote("comment", voterEl, 1);
},
downvoteComment: function(voterEl) {
Lobsters._showDownvoteWhyAt("comment", voterEl, function(k) {
Lobsters.vote("comment", voterEl, -1, k); });
},
_showDownvoteWhyAt: function(thingType, voterEl, onChooseWhy) {
if (!Lobsters.curUser)
return Lobsters.bounceToLogin();
var li = $(voterEl).closest(".story, .comment");
if (li.hasClass("downvoted")) {
/* already upvoted, neutralize */
Lobsters.vote("comment", voterEl, -1, null);
Lobsters.vote(thingType, voterEl, -1, null);
return;
}
@ -58,7 +71,13 @@ var _Lobsters = Class.extend({
var d = $("<div id=\"downvote_why\"></div>");
$.each(Lobsters.commentDownvoteReasons, function(k, v) {
var reasons;
if (thingType == "comment")
reasons = Lobsters.commentDownvoteReasons;
else
reasons = Lobsters.storyDownvoteReasons;
$.each(reasons, function(k, v) {
var a = $("<a href=\"#\"" + (k == "" ? " class=\"cancelreason\"" : "") +
">" + v + "</a>");
@ -67,7 +86,7 @@ var _Lobsters = Class.extend({
$("#downvote_why_shadow").remove();
if (k != "")
Lobsters.vote("comment", voterEl, -1, k);
onChooseWhy(k);
return false;
});
@ -234,6 +253,10 @@ $(document).ready(function() {
return false;
});
$("li.story a.downvoter").click(function() {
Lobsters.downvoteStory(this);
return false;
});
$("li.story a.upvoter").click(function() {
Lobsters.upvoteStory(this);
return false;

View file

@ -368,6 +368,7 @@ ol.search_results li.story {
div.voters {
float: left;
margin-top: -4px;
width: 40px;
}
@ -423,8 +424,8 @@ li.story {
clear: both;
}
li.story div.story_liner {
padding-top: 0.3em;
padding-bottom: 0.3em;
padding-top: 0.4em;
padding-bottom: 0.4em;
}
.comment {
clear: both;

View file

@ -58,8 +58,9 @@
}
div.voters {
width: 31px;
margin-left: 0.25em;
margin-top: 0px;
width: 31px;
}
div.voters a.upvoter {
margin-top: -5px;
@ -133,7 +134,7 @@
color: #333;
display: block;
font-size: 9pt;
margin: -0.5em 0.5em 0 0.5em;
margin: 0 0.5em;
padding: 2px;
position: relative;
text-align: center;

View file

@ -1,6 +1,6 @@
class StoriesController < ApplicationController
before_filter :require_logged_in_user_or_400,
:only => [ :upvote, :unvote, :hide, :unhide, :preview ]
:only => [ :upvote, :downvote, :unvote, :hide, :unhide, :preview ]
before_filter :require_logged_in_user, :only => [ :destroy, :create, :edit,
:fetch_url_title, :new ]
@ -201,6 +201,25 @@ class StoriesController < ApplicationController
render :text => "ok"
end
def downvote
if !(story = find_story)
return render :text => "can't find story", :status => 400
end
if !Vote::STORY_REASONS[params[:reason]]
return render :text => "invalid reason", :status => 400
end
if !@user.can_downvote?(story)
return render :text => "not permitted to downvote", :status => 400
end
Vote.vote_thusly_on_story_or_comment_for_user_because(-1, story.id,
nil, @user.id, params[:reason])
render :text => "ok"
end
def hide
if !(story = find_story)
return render :text => "can't find story", :status => 400

View file

@ -10,6 +10,8 @@ class Story < ActiveRecord::Base
validates_length_of :description, :maximum => (64 * 1024)
validates_presence_of :user_id
DOWNVOTABLE_DAYS = 14
# after this many minutes old, a story cannot be edited
MAX_EDIT_MINS = 30
@ -117,7 +119,8 @@ class Story < ActiveRecord::Base
end
def calculated_hotness
order = Math.log([ score.abs, 1 ].max, 10)
# don't immediately kill stories at 0 by bumping up score by one
order = Math.log([ (score + 1).abs, 1 ].max, 10)
if score > 0
sign = 1
elsif score < 0
@ -230,6 +233,14 @@ class Story < ActiveRecord::Base
:vote => 0).count
end
def is_downvotable?
if self.created_at
Time.now - self.created_at <= DOWNVOTABLE_DAYS.days
else
false
end
end
def is_editable_by_user?(user)
if user && user.is_moderator?
return true
@ -401,4 +412,27 @@ class Story < ActiveRecord::Base
def url_or_comments_url
self.url.blank? ? self.comments_url : self.url
end
def vote_summary_for(user)
r_counts = {}
r_whos = {}
Vote.where(:story_id => self.id, :comment_id => nil).each do |v|
r_counts[v.reason.to_s] ||= 0
r_counts[v.reason.to_s] += v.vote
if user && user.is_moderator?
r_whos[v.reason.to_s] ||= []
r_whos[v.reason.to_s].push v.user.username
end
end
r_counts.keys.sort.map{|k|
if k == ""
"+#{r_counts[k]}"
else
"#{r_counts[k]} " +
(Vote::STORY_REASONS[k] || Vote::OLD_STORY_REASONS[k]) +
(user && user.is_moderator?? " (#{r_whos[k].join(", ")})" : "")
end
}.join(", ")
end
end

View file

@ -103,8 +103,12 @@ class User < ActiveRecord::Base
if is_new?
return false
elsif obj.is_a?(Story)
# user can unvote
return obj.vote == -1
if obj.is_downvotable?
return true
elsif obj.vote == -1
# user can unvote
return true
end
elsif obj.is_a?(Comment)
if obj.is_downvotable?
return true

View file

@ -11,6 +11,18 @@ class Vote < ActiveRecord::Base
"" => "Cancel",
}
STORY_REASONS = {
"O" => "Off-topic",
"A" => "Already Posted",
"T" => "Poorly Tagged",
"L" => "Poorly Titled",
"S" => "Spam",
"" => "Cancel",
}
OLD_STORY_REASONS = {
"Q" => "Low Quality",
}
def self.votes_by_user_for_stories_hash(user, stories)
votes = {}

View file

@ -10,6 +10,11 @@ class="story <%= story.vote == 1 ? "upvoted" : "" %> <%= story.vote == -1 ?
<%= link_to "", login_url, :class => "upvoter" %>
<% end %>
<div class="score"><%= story.score %></div>
<% if @user && @user.can_downvote?(story) %>
<a class="downvoter"></a>
<% else %>
<span class="downvoter downvoter_stub"></span>
<% end %>
</div>
<div class="details">
<span class="link">
@ -83,6 +88,12 @@ class="story <%= story.vote == 1 ? "upvoted" : "" %> <%= story.vote == -1 ?
(story.comments_count == 1 ? "" : "s") %></a>
</span>
<% end %>
<% if defined?(single_story) && single_story %>
<% if story.downvotes > 0 %>
| <%= story.vote_summary_for(@user).downcase %>
<% end %>
<% end %>
<% end %>
</div>
</div>

View file

@ -42,6 +42,7 @@ Lobsters::Application.routes.draw do
resources :stories do
post "upvote"
post "downvote"
post "unvote"
post "undelete"
post "hide"