diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 6f69ba5a..ed71a42e 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -415,13 +415,48 @@ p.time { } } -.upvotes { - font-size: 14px; - font-weight: bold; - color: #468847; - text-align: right; - padding: 4px; - margin: 2px; +.votes { + font-size: 13px; + line-height: 15px; + .progress { + height: 4px; + margin: 0; + .bar { + float: left; + height: 100%; + } + .bar-success { + background-color: #468847; + @include bg-gradient(#62C462, #51A351); + } + .bar-danger { + background-color: #B94A48; + @include bg-gradient(#EE5F5B, #BD362F); + } + } + .upvotes { + display: inline-block; + color: #468847; + } + .downvotes { + display: inline-block; + color: #B94A48; + } +} +.votes-block { + margin: 14px 6px 6px 0; + .downvotes { + float: right; + } +} +.votes-inline { + display: inline-block; + margin: 0 8px; + .progress { + display: inline-block; + padding: 0 0 2px; + width: 45px; + } } /* Fix for readme code (stopped it from being yellow) */ diff --git a/app/models/issue.rb b/app/models/issue.rb index 6409eeba..96a54907 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,6 +1,6 @@ class Issue < ActiveRecord::Base include IssueCommonality - include Upvote + include Votes acts_as_taggable_on :labels diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 2e457f72..184ac5fc 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit") class MergeRequest < ActiveRecord::Base include IssueCommonality - include Upvote + include Votes BROKEN_DIFF = "--broken-diff" diff --git a/app/models/note.rb b/app/models/note.rb index d8494edd..4c46c7df 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -105,6 +105,12 @@ class Note < ActiveRecord::Base def upvote? note.start_with?('+1') || note.start_with?(':+1:') end + + # Returns true if this is a downvote note, + # otherwise false is returned + def downvote? + note.start_with?('-1') || note.start_with?(':-1:') + end end # == Schema Information # diff --git a/app/roles/upvote.rb b/app/roles/upvote.rb deleted file mode 100644 index 7efa6f20..00000000 --- a/app/roles/upvote.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Upvote - # Return the number of +1 comments (upvotes) - def upvotes - notes.select(&:upvote?).size - end -end diff --git a/app/roles/votes.rb b/app/roles/votes.rb new file mode 100644 index 00000000..043a6feb --- /dev/null +++ b/app/roles/votes.rb @@ -0,0 +1,32 @@ +module Votes + # Return the number of +1 comments (upvotes) + def upvotes + notes.select(&:upvote?).size + end + + def upvotes_in_percent + if votes_count.zero? + 0 + else + 100.0 / votes_count * upvotes + end + end + + # Return the number of -1 comments (downvotes) + def downvotes + notes.select(&:downvote?).size + end + + def downvotes_in_percent + if votes_count.zero? + 0 + else + 100.0 - upvotes_in_percent + end + end + + # Return the total number of votes + def votes_count + upvotes + downvotes + end +end diff --git a/app/views/issues/_show.html.haml b/app/views/issues/_show.html.haml index 8500cd40..22101aa1 100644 --- a/app/views/issues/_show.html.haml +++ b/app/views/issues/_show.html.haml @@ -34,5 +34,5 @@ - else   - - if issue.upvotes > 0 - %span.badge.badge-success= "+#{issue.upvotes}" + - if issue.votes_count > 0 + = render 'votes/votes_inline', votable: issue diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index dce8cf6a..9b1c72a3 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -8,22 +8,22 @@ %span.right - if can?(current_user, :admin_project, @project) || @issue.author == current_user - if @issue.closed - = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small" + = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped success" - else - = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small", title: "Close Issue" + = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close Issue" - if can?(current_user, :admin_project, @project) || @issue.author == current_user - = link_to edit_project_issue_path(@project, @issue), class: "btn small" do + = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do %i.icon-edit Edit - %br - - if @issue.upvotes > 0 - .upvotes#upvotes= "+#{pluralize @issue.upvotes, 'upvote'}" +.right + .span3#votes= render 'votes/votes_block', votable: @issue .back_link = link_to project_issues_path(@project) do ← To issues list + .main_box .top_box_content %h4 diff --git a/app/views/merge_requests/_merge_request.html.haml b/app/views/merge_requests/_merge_request.html.haml index 74996090..9d94d670 100644 --- a/app/views/merge_requests/_merge_request.html.haml +++ b/app/views/merge_requests/_merge_request.html.haml @@ -23,5 +23,6 @@ authored by #{merge_request.author_name} = time_ago_in_words(merge_request.created_at) ago - - if merge_request.upvotes > 0 - %span.badge.badge-success= "+#{merge_request.upvotes}" + + - if merge_request.votes_count > 0 + = render 'votes/votes_inline', votable: merge_request diff --git a/app/views/merge_requests/show/_mr_title.html.haml b/app/views/merge_requests/show/_mr_title.html.haml index 3ae1050d..8708469c 100644 --- a/app/views/merge_requests/show/_mr_title.html.haml +++ b/app/views/merge_requests/show/_mr_title.html.haml @@ -23,10 +23,8 @@ %i.icon-edit Edit - %br - - if @merge_request.upvotes > 0 - .upvotes#upvotes= "+#{pluralize @merge_request.upvotes, 'upvote'}" - +.right + .span3#votes= render 'votes/votes_block', votable: @merge_request .back_link = link_to project_merge_requests_path(@project) do diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml new file mode 100644 index 00000000..bded53b2 --- /dev/null +++ b/app/views/votes/_votes_block.html.haml @@ -0,0 +1,6 @@ +.votes.votes-block + .progress + .bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"} + .bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"} + .upvotes= "#{votable.upvotes} up" + .downvotes= "#{votable.downvotes} down" diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml new file mode 100644 index 00000000..91bd200d --- /dev/null +++ b/app/views/votes/_votes_inline.html.haml @@ -0,0 +1,6 @@ +.votes.votes-inline + .upvotes= votable.upvotes + .progress + .bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"} + .bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"} + .downvotes= votable.downvotes diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index ca6307e7..34192da9 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -12,7 +12,7 @@ describe Issue do describe 'modules' do it { should include_module(IssueCommonality) } - it { should include_module(Upvote) } + it { should include_module(Votes) } end subject { Factory.create(:issue) } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index d1253b35..523e823d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -8,6 +8,6 @@ describe MergeRequest do describe 'modules' do it { should include_module(IssueCommonality) } - it { should include_module(Upvote) } + it { should include_module(Votes) } end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index dddfd34c..7809953f 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -24,6 +24,13 @@ describe Note do it "recognizes a neutral note" do note = Factory(:note, note: "This is not a +1 note") note.should_not be_upvote + note.should_not be_downvote + end + + it "recognizes a neutral emoji note" do + note = build(:note, note: "I would :+1: this, but I don't want to") + note.should_not be_upvote + note.should_not be_downvote end it "recognizes a +1 note" do @@ -31,19 +38,19 @@ describe Note do note.should be_upvote end - it "recognizes a -1 note as no vote" do - note = Factory(:note, note: "-1 for this") - note.should_not be_upvote - end - it "recognizes a +1 emoji as a vote" do note = build(:note, note: ":+1: for this") note.should be_upvote end - it "recognizes a neutral emoji note" do - note = build(:note, note: "I would :+1: this, but I don't want to") - note.should_not be_upvote + it "recognizes a -1 note" do + note = Factory(:note, note: "-1 for this") + note.should be_downvote + end + + it "recognizes a -1 emoji as a vote" do + note = build(:note, note: ":-1: for this") + note.should be_downvote end end diff --git a/spec/roles/upvote_spec.rb b/spec/roles/upvote_spec.rb deleted file mode 100644 index 24288ada..00000000 --- a/spec/roles/upvote_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe Issue, "Upvote" do - let(:issue) { create(:issue) } - - it "with no notes has a 0/0 score" do - issue.upvotes.should == 0 - end - - it "should recognize non-+1 notes" do - issue.notes << create(:note, note: "No +1 here") - issue.should have(1).note - issue.notes.first.upvote?.should be_false - issue.upvotes.should == 0 - end - - it "should recognize a single +1 note" do - issue.notes << create(:note, note: "+1 This is awesome") - issue.upvotes.should == 1 - end - - it "should recognize multiple +1 notes" do - issue.notes << create(:note, note: "+1 This is awesome") - issue.notes << create(:note, note: "+1 I want this") - issue.upvotes.should == 2 - end -end diff --git a/spec/roles/votes_spec.rb b/spec/roles/votes_spec.rb new file mode 100644 index 00000000..98666022 --- /dev/null +++ b/spec/roles/votes_spec.rb @@ -0,0 +1,132 @@ +require 'spec_helper' + +describe Issue do + let(:issue) { create(:issue) } + + describe "#upvotes" do + it "with no notes has a 0/0 score" do + issue.upvotes.should == 0 + end + + it "should recognize non-+1 notes" do + issue.notes << create(:note, note: "No +1 here") + issue.should have(1).note + issue.notes.first.upvote?.should be_false + issue.upvotes.should == 0 + end + + it "should recognize a single +1 note" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.upvotes.should == 1 + end + + it "should recognize multiple +1 notes" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.upvotes.should == 2 + end + end + + describe "#downvotes" do + it "with no notes has a 0/0 score" do + issue.downvotes.should == 0 + end + + it "should recognize non--1 notes" do + issue.notes << create(:note, note: "Almost got a -1") + issue.should have(1).note + issue.notes.first.downvote?.should be_false + issue.downvotes.should == 0 + end + + it "should recognize a single -1 note" do + issue.notes << create(:note, note: "-1 This is bad") + issue.downvotes.should == 1 + end + + it "should recognize multiple -1 notes" do + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "-1 Away with this") + issue.downvotes.should == 2 + end + end + + describe "#votes_count" do + it "with no notes has a 0/0 score" do + issue.votes_count.should == 0 + end + + it "should recognize non notes" do + issue.notes << create(:note, note: "No +1 here") + issue.should have(1).note + issue.votes_count.should == 0 + end + + it "should recognize a single +1 note" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.votes_count.should == 1 + end + + it "should recognize a single -1 note" do + issue.notes << create(:note, note: "-1 This is bad") + issue.votes_count.should == 1 + end + + it "should recognize multiple notes" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "+1 I want this") + issue.votes_count.should == 3 + end + end + + describe "#upvotes_in_percent" do + it "with no notes has a 0% score" do + issue.upvotes_in_percent.should == 0 + end + + it "should count a single 1 note as 100%" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.upvotes_in_percent.should == 100 + end + + it "should count multiple +1 notes as 100%" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.upvotes_in_percent.should == 100 + end + + it "should count fractions for multiple +1 and -1 notes correctly" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "+1 me too") + issue.upvotes_in_percent.should == 75 + end + end + + describe "#downvotes_in_percent" do + it "with no notes has a 0% score" do + issue.downvotes_in_percent.should == 0 + end + + it "should count a single -1 note as 100%" do + issue.notes << create(:note, note: "-1 This is bad") + issue.downvotes_in_percent.should == 100 + end + + it "should count multiple -1 notes as 100%" do + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "-1 Away with this") + issue.downvotes_in_percent.should == 100 + end + + it "should count fractions for multiple +1 and -1 notes correctly" do + issue.notes << create(:note, note: "+1 This is awesome") + issue.notes << create(:note, note: "+1 I want this") + issue.notes << create(:note, note: "-1 This is bad") + issue.notes << create(:note, note: "+1 me too") + issue.downvotes_in_percent.should == 25 + end + end +end