diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index aa27a280a18f6489ea463af8acb6a57e82c52e0d..829bece3776081634183ad3e843cff0739677366 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -406,13 +406,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 6409eebac6310044091634cbaaf26b817aa116e0..96a54907ca3cbd2cc2b4445880f53a8e9674d84f 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 542817b0eeae90adbf3a6871f14613794bfef61a..3376e31b6f7e9fa2c33726cfc6bc3f06ba9a79be 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 d8494edd5328d1f86f62efc39c0c060f16859fc7..4c46c7dfffa80215a34577f723791bfa920eeb99 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 7efa6f20cfcc321968f4e00dd00b8420ac3171b5..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..043a6feb7775b9e0cdd98811f31c0c7d49a20df0 --- /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 8500cd40a6e683dac9bb9269828e1637e53dbfef..22101aa1dce99830b7c94b3a33d60d605397f2c1 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 dce8cf6a59de2e9d6fd0bdee191e2c32c259179f..9b1c72a3a12c6e5331edfa59596b653f81c1586c 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 7499609066a37f0e10942c21ec360f3c968bc16e..9d94d67052501ea43ff2d7d9015a48bc29da2c44 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 3ae1050d1691c5885832983d6ae7eef897676a71..8708469cc5deb1c1f47730f5841c529b5c554870 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 0000000000000000000000000000000000000000..bded53b2f21e1bef0a6a5db9ff5604892f74da4d --- /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 0000000000000000000000000000000000000000..91bd200df44fd261d770910906723a5f09d73cfc --- /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 ca6307e72b13bbca84f8f666feb5864d4d2a5f52..34192da94ad0bb0d1d1a2ae3cd6da12644daca70 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 d1253b35ae5edd5d0c82b624e204ba6dc39ddd95..523e823de34358add83f11fa88805e0ceff725fe 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 dddfd34c499d98418768eb8c5ecc2ec10cc9c424..7809953f5b32fd8d386e8aa5779f7351df0593a5 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 24288ada0fe8bd3812ab5b5c4bf650249194d301..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..98666022a8fcbd7f22b70862937dbbdad25adc58 --- /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