biovision-vote 0.4.190905.0 → 0.6.200627.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9622a08b05afd38c9d1c3114c2abc19e83f12b64729060216cfd5f7acf8df19
4
- data.tar.gz: 5c561a9230de49435f88ecec4e7aab3d04bc60973aa942af44d17268ac9bc2ac
3
+ metadata.gz: d0b3721a3296b82fb23854ea3bdd4365a278fef68ea522d0d84da05c3e6162d5
4
+ data.tar.gz: 6596c3c780a7e2ddc47bac92924fdad775495f5531a67f87e7083ee0f032a4dd
5
5
  SHA512:
6
- metadata.gz: b9fdc9b015b3cac71815c8a0c340b7130338b777c001f645abd758da328b8b762519bc742d32db0f35a882d240b2415ea9ee236d353ab59d6712b2e1473f1ea6
7
- data.tar.gz: 39e630aeb2517455a084bdcdda72fb51bb43b6ad45e9041de6792a7df8cdb9fad052845713a336156d07546ad22806ae9ad6191ae4f308b2bc14c549f52faaf9
6
+ metadata.gz: 250ac4ffb39d351bea23e77056173fbe7d73834318121ba74923cf1305aa175bae7b676cd5880116976ddf38559374b31d8326e0db0d10b6c4296cbe8f975d8f
7
+ data.tar.gz: 7276e36d6d53e06091b01711d671f1cb05782714cf2789cf4b6990a6e45798564b672c33c013d01070289dae306bed24428f3cbab9c01cdd89d5bb09ce887db4
@@ -1,97 +1,66 @@
1
- 'use strict';
2
-
3
- document.addEventListener('DOMContentLoaded', function () {
4
- document.querySelectorAll('.vote-block.active .vote').forEach(function (element) {
5
- element.addEventListener('click', function () {
6
- const $button = this;
7
- const $container = $button.closest('.vote-block');
8
- const votable_id = $container.getAttribute('data-id');
9
- const upvote = $button.classList.contains('upvote');
10
-
11
- document.getElementById('vote_delta_' + votable_id).value = upvote ? '1' : '-1';
12
-
13
- if ($container.classList.contains('active')) {
14
- const $form = $container.querySelector('form');
15
- const on_success = function () {
16
- const result = JSON.parse(this.responseText);
17
-
18
- $button.classList.remove('switch');
19
- if (result.hasOwnProperty('meta')) {
20
- const vote_type = result['meta']['vote_type'];
21
- const vote_result = result['meta']['vote_result'];
22
-
23
- $container.classList.remove('voted-none');
24
- $container.classList.add('voted-' + vote_type);
25
- $container.querySelector('.result').innerHTML = vote_result;
26
- }
27
- };
28
-
29
- const on_failure = function (result) {
30
- $button.classList.remove('switch');
31
- $button.classList.add('error');
32
- handle_ajax_failure(result);
33
- };
34
-
35
- const request = Biovision.jsonAjaxRequest('POST', $form.getAttribute('action'), on_success, on_failure);
36
-
37
- $button.classList.add('switch');
38
-
39
- request.send(new FormData($form));
1
+ "use strict";
2
+
3
+ const Votes = {
4
+ initialized: false,
5
+ autoInitComponents: true,
6
+ components: {}
7
+ };
8
+
9
+ Votes.components.voteButtons = {
10
+ initialized: false,
11
+ buttons: [],
12
+ url: "/votes",
13
+ selector: ".vote-block .vote",
14
+ init: function () {
15
+ document.querySelectorAll(this.selector).forEach(this.apply);
16
+ this.initialized = true;
17
+ },
18
+ apply: function (button) {
19
+ const component = Votes.components.voteButtons;
20
+ component.buttons.push(button);
21
+ button.addEventListener("click", component.click);
22
+ },
23
+ click: function (event) {
24
+ const component = Votes.components.voteButtons;
25
+ const button = event.target;
26
+ const delta = button.classList.contains("upvote") ? 1 : -1;
27
+ const container = button.closest(".vote-block");
28
+ const data = {
29
+ "vote": {
30
+ "votable_id": container.getAttribute("data-id"),
31
+ "votable_type": container.getAttribute("data-type"),
32
+ "delta": delta
40
33
  }
34
+ };
41
35
 
42
- $container.classList.remove('active');
36
+ container.querySelectorAll(".vote").forEach(function (element) {
37
+ element.disabled = true;
43
38
  });
44
- });
45
39
 
46
- document.querySelectorAll('[data-vote]').forEach(function (button) {
47
- const container = button.closest('[data-vote-url]');
48
- if (container) {
49
- if (container.classList.contains('vote-active')) {
50
- const url = container.getAttribute('data-vote-url');
40
+ const onSuccess = function () {
41
+ const result = JSON.parse(this.responseText);
42
+ if (result.hasOwnProperty("meta")) {
43
+ const meta = result.meta;
51
44
 
52
- button.addEventListener('click', function () {
53
- const delta = this.getAttribute('data-vote') === 'up' ? 1 : -1;
54
- const pressedButton = this;
45
+ container.classList.remove("voted-none");
46
+ container.classList.add("voted-" + meta["vote_type"]);
47
+ container.querySelector(".result").innerHTML = meta["vote_result"];
48
+ button.classList.remove("switch");
49
+ }
50
+ };
55
51
 
56
- /**
57
- * Во время голосования состояние переключается
58
- * на vote-inactive, поэтому проверяем ещё раз
59
- */
60
- if (container.classList.contains('vote-active')) {
61
- const data = {
62
- vote: {
63
- votable_id: container.getAttribute('data-votable-id'),
64
- votable_type: container.getAttribute('data-votable-type'),
65
- delta: delta,
66
- }
67
- };
52
+ const onFailure = function () {
53
+ button.classList.remove("switch");
54
+ button.classList.add("error");
68
55
 
69
- const request = Biovision.jsonAjaxRequest('POST', url, function () {
70
- if (this.responseText) {
71
- const response = JSON.parse(this.responseText);
56
+ Biovision.handleAjaxFailure();
57
+ };
72
58
 
73
- if (response.hasOwnProperty('meta')) {
74
- if (delta > 0) {
75
- pressedButton.innerHTML = response.meta['upvote_count'];
76
- } else {
77
- pressedButton.innerHTML = response.meta['downvote_count'];
78
- }
79
- container.classList.remove('voted-none');
80
- container.classList.add('voted-' + response.meta['vote_type']);
81
- }
82
- } else {
83
- container.classList.remove('vote-inactive');
84
- container.classList.add('vote-active');
85
- }
86
- });
59
+ const request = Biovision.jsonAjaxRequest("POST", component.url, onSuccess, onFailure);
87
60
 
88
- container.classList.remove('vote-active');
89
- container.classList.add('vote-inactive');
61
+ button.classList.add("switch");
62
+ request.send(JSON.stringify(data));
63
+ }
64
+ };
90
65
 
91
- request.send(JSON.stringify(data));
92
- }
93
- });
94
- }
95
- }
96
- });
97
- });
66
+ Biovision.components.votes = Votes;
@@ -5,23 +5,27 @@
5
5
 
6
6
  .vote {
7
7
  background: no-repeat center / cover;
8
+ border: none;
9
+ cursor: pointer;
8
10
  height: 1.2rem;
9
11
  width: 1.2rem;
10
- }
11
12
 
12
- &.active {
13
- .vote {
14
- cursor: pointer;
13
+ &:focus {
14
+ box-shadow: 0 0 .2rem rgba(0, 0, 0, .5);
15
+ }
16
+
17
+ &:disabled {
18
+ cursor: default;
15
19
  }
20
+ }
16
21
 
17
- &.voted-none {
18
- .upvote:hover {
19
- background-image: image_url('biovision/vote/icons/upvote-active.svg');
20
- }
22
+ &.voted-none {
23
+ .upvote:not(:disabled):hover {
24
+ background-image: image_url('biovision/vote/icons/upvote-active.svg');
25
+ }
21
26
 
22
- .downvote:hover {
23
- background-image: image_url('biovision/vote/icons/downvote-active.svg');
24
- }
27
+ .downvote:not(:disabled):hover {
28
+ background-image: image_url('biovision/vote/icons/downvote-active.svg');
25
29
  }
26
30
  }
27
31
 
@@ -9,8 +9,8 @@ class Admin::VotesController < AdminController
9
9
 
10
10
  private
11
11
 
12
- def component_slug
13
- Biovision::Components::VoteComponent::SLUG
12
+ def component_class
13
+ Biovision::Components::VotesComponent
14
14
  end
15
15
 
16
16
  def restrict_access
@@ -22,8 +22,8 @@ class VotesController < ApplicationController
22
22
 
23
23
  private
24
24
 
25
- def component_slug
26
- Biovision::Components::VoteComponent::SLUG
25
+ def component_class
26
+ Biovision::Components::VotesComponent
27
27
  end
28
28
 
29
29
  def set_entity
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Model has votes
3
4
  module VotableItem
4
5
  extend ActiveSupport::Concern
5
6
 
@@ -16,10 +17,10 @@ module VotableItem
16
17
  # @param [String|Vote] vote_or_slug
17
18
  def vote_data(vote_or_slug)
18
19
  {
19
- upvote_count: upvote_count,
20
- downvote_count: downvote_count,
21
- vote_result: vote_result,
22
- vote_type: voted(vote_or_slug)
20
+ upvote_count: data.dig('votes', 'up'),
21
+ downvote_count: data.dig('votes', 'down'),
22
+ vote_result: data.dig('votes', 'total'),
23
+ vote_type: voted(vote_or_slug)
23
24
  }
24
25
  end
25
26
 
@@ -30,4 +31,30 @@ module VotableItem
30
31
 
31
32
  vote.upvote? ? :upvote : :downvote
32
33
  end
34
+
35
+ # @param [Integer] delta
36
+ def add_vote_result(delta)
37
+ return unless has_attribute?(:data)
38
+
39
+ vote_data = data['votes'].to_h
40
+ key = delta.positive? ? 'up' : 'down'
41
+ vote_data[key] = vote_data.key?(key) ? vote_data[key].to_i + 1 : 1
42
+ vote_data['total'] = vote_data.key?('total') ? vote_data['total'] + delta : delta
43
+ self.data['votes'] = vote_data
44
+
45
+ save
46
+ end
47
+
48
+ # @param [Integer] delta
49
+ def discard_vote_result(delta)
50
+ return unless has_attribute?(:data)
51
+
52
+ vote_data = data['votes'].to_h
53
+ key = delta.positive? ? 'up' : 'down'
54
+ vote_data[key] = vote_data[key].to_i - 1 if vote_data[key].to_i.positive?
55
+ vote_data['total'] = vote_data['total'].to_i - delta
56
+ self.data['votes'] = vote_data
57
+
58
+ save
59
+ end
33
60
  end
@@ -10,10 +10,12 @@
10
10
  # slug [String]
11
11
  # updated_at [DateTime]
12
12
  # user_id [User], optional
13
+ # uuid [UUID]
13
14
  # votable_id [Integer]
14
15
  # votable_type [String]
15
16
  class Vote < ApplicationRecord
16
17
  include HasOwner
18
+ include HasUuid
17
19
 
18
20
  belongs_to :user, optional: true
19
21
  belongs_to :agent, optional: true
@@ -74,7 +76,7 @@ class Vote < ApplicationRecord
74
76
 
75
77
  # @param [User] user
76
78
  def editable_by?(user)
77
- owned_by?(user) || UserPrivilege.user_has_privilege?(:user, :moderator)
79
+ owned_by?(user) || user&.super_user?
78
80
  end
79
81
 
80
82
  def current_slug
@@ -84,17 +86,11 @@ class Vote < ApplicationRecord
84
86
  private
85
87
 
86
88
  def add_vote_result
87
- votable.vote_result = votable.vote_result + delta
88
- votable.upvote_count = votable.upvote_count + 1 if upvote?
89
- votable.downvote_count = votable.downvote_count + 1 if downvote?
89
+ votable.add_vote_result(delta)
90
90
  votable.vote_impact(delta) if votable.respond_to?(:vote_impact)
91
- votable.save
92
91
  end
93
92
 
94
93
  def discard_vote_result
95
- votable.vote_result = votable.vote_result - delta
96
- votable.upvote_count = votable.upvote_count - 1 if upvote?
97
- votable.downvote_count = votable.downvote_count - 1 if downvote?
98
- votable.save
94
+ votable.discard_vote_result(delta)
99
95
  end
100
96
  end
@@ -3,11 +3,10 @@
3
3
  module Biovision
4
4
  module Components
5
5
  # Component for votes
6
- class VoteComponent < BaseComponent
6
+ class VotesComponent < BaseComponent
7
7
  METRIC_VOTE_HIT = 'votes.any.hit'
8
8
  METRIC_UPVOTE_HIT = 'votes.upvote.hit'
9
9
  METRIC_DOWNVOTE_HIT = 'votes.downvote.hit'
10
- SLUG = 'vote'
11
10
 
12
11
  def self.privilege_names
13
12
  %w[moderator]
@@ -1,16 +1,10 @@
1
- <%
2
- block_class = votable.vote_applicable?(visitor_slug) ? 'active' : ''
3
- %>
4
- <div class="vote-block <%= block_class %> voted-<%= votable.voted(visitor_slug) %>" data-id="<%= votable.id %>">
5
- <div class="vote upvote"></div>
6
- <div class="result"><%= votable.vote_result %></div>
7
- <div class="vote downvote"></div>
8
- <% unless block_class.blank? %>
9
- <% entity = Vote.new(votable: votable, delta: 0) %>
10
- <%= form_with(model: entity, local: true) do %>
11
- <%= f.hidden_field :votable_id, id: "vote_votable_id_#{votable.id}" %>
12
- <%= f.hidden_field :votable_type, id: "vote_votable_type_#{votable.id}" %>
13
- <%= f.hidden_field :delta, id: "vote_delta_#{votable.id}" %>
14
- <% end %>
15
- <% end %>
1
+ <% postfix = votable.vote_applicable?(visitor_slug) ? '' : ' disabled' %>
2
+ <div
3
+ class="vote-block voted-<%= votable.voted(visitor_slug) %>"
4
+ data-id="<%= votable.id %>"
5
+ data-type="<%= votable.class.to_s %>"
6
+ >
7
+ <button class="vote upvote"<%= postfix %>></button>
8
+ <div class="result"><%= votable.data.dig('votes', 'total').to_i %></div>
9
+ <button class="vote downvote"<%= postfix %>></button>
16
10
  </div>
@@ -37,5 +37,5 @@ en:
37
37
  heading: "User votes"
38
38
  biovision:
39
39
  components:
40
- vote:
40
+ votes:
41
41
  name: "Vote"
@@ -35,7 +35,7 @@ ru:
35
35
  heading: "Голоса пользователей"
36
36
  biovision:
37
37
  components:
38
- vote:
38
+ votes:
39
39
  name: "Голосование"
40
40
  privileges:
41
41
  moderator: "Модератор"
@@ -37,5 +37,5 @@ sv:
37
37
  heading: "Röster av användare"
38
38
  biovision:
39
39
  components:
40
- vote:
40
+ votes:
41
41
  name: "Röster"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Rails.application.routes.draw do
2
4
  resources :votes, only: :destroy, defaults: { format: :json }
3
5
 
@@ -15,6 +15,7 @@ class CreateVotes < ActiveRecord::Migration[5.2]
15
15
 
16
16
  def create_votes
17
17
  create_table :votes, comment: 'Vote' do |t|
18
+ t.uuid :uuid, null: false
18
19
  t.references :user, foreign_key: { on_update: :cascade, on_delete: :cascade }
19
20
  t.references :agent, foreign_key: { on_update: :cascade, on_delete: :nullify }
20
21
  t.inet :ip
@@ -24,9 +25,11 @@ class CreateVotes < ActiveRecord::Migration[5.2]
24
25
  t.string :votable_type, null: false
25
26
  t.string :slug, index: true
26
27
  end
28
+
29
+ add_index :votes, :uuid, unique: true
27
30
  end
28
31
 
29
32
  def create_component
30
- BiovisionComponent.create(slug: Biovision::Components::VoteComponent::SLUG)
33
+ BiovisionComponent.create(slug: Biovision::Components::VotesComponent.slug)
31
34
  end
32
35
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add UUID column to votes and rename component to "votes"
4
+ class AddUuidToVotes < ActiveRecord::Migration[5.2]
5
+ def up
6
+ criteria = { slug: Biovision::Components::VotesComponent.slug }
7
+
8
+ return if BiovisionComponent.where(criteria).exists?
9
+
10
+ BiovisionComponent['vote']&.update!(criteria) || BiovisionComponent.create(criteria)
11
+
12
+ add_column :votes, :uuid, :uuid
13
+ add_index :votes, :uuid, unique: true
14
+
15
+ Vote.order('id asc').each(&:save!)
16
+ end
17
+
18
+ def down
19
+ # No rollback needed
20
+ end
21
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Biovision
4
4
  module Vote
5
- VERSION = '0.4.190905.0'
5
+ VERSION = '0.6.200627.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,43 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: biovision-vote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.190905.0
4
+ version: 0.6.200627.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxim Khan-Magomedov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-04 00:00:00.000000000 Z
11
+ date: 2020-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rails
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '5.2'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '5.2'
27
- - !ruby/object:Gem::Dependency
28
- name: rails-i18n
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '5.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '5.0'
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: biovision-base
43
15
  requirement: !ruby/object:Gem::Requirement
@@ -140,8 +112,8 @@ files:
140
112
  - app/models/biovision/vote/application_record.rb
141
113
  - app/models/concerns/votable_item.rb
142
114
  - app/models/vote.rb
143
- - app/services/biovision/components/vote_component.rb
144
- - app/views/admin/components/links/_vote.html.erb
115
+ - app/services/biovision/components/votes_component.rb
116
+ - app/views/admin/components/links/_votes.html.erb
145
117
  - app/views/admin/votes/_nav_item.html.erb
146
118
  - app/views/admin/votes/entity/_in_list.html.erb
147
119
  - app/views/admin/votes/index.html.erb
@@ -153,6 +125,7 @@ files:
153
125
  - config/locales/votes-sv.yml
154
126
  - config/routes.rb
155
127
  - db/migrate/20170330000001_create_votes.rb
128
+ - db/migrate/20200207101010_add_uuid_to_votes.rb
156
129
  - lib/biovision/vote.rb
157
130
  - lib/biovision/vote/engine.rb
158
131
  - lib/biovision/vote/version.rb
@@ -176,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
149
  - !ruby/object:Gem::Version
177
150
  version: '0'
178
151
  requirements: []
179
- rubygems_version: 3.0.4
152
+ rubygems_version: 3.1.4
180
153
  signing_key:
181
154
  specification_version: 4
182
155
  summary: Voting functionality for biovision-based applications