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 +4 -4
- data/app/assets/javascripts/biovision/vote/biovision-vote.js +56 -87
- data/app/assets/stylesheets/biovision/vote/vote.scss +15 -11
- data/app/controllers/admin/votes_controller.rb +2 -2
- data/app/controllers/votes_controller.rb +2 -2
- data/app/models/concerns/votable_item.rb +31 -4
- data/app/models/vote.rb +5 -9
- data/app/services/biovision/components/{vote_component.rb → votes_component.rb} +1 -2
- data/app/views/admin/components/links/{_vote.html.erb → _votes.html.erb} +0 -0
- data/app/views/votes/_vote_block.html.erb +9 -15
- data/config/locales/votes-en.yml +1 -1
- data/config/locales/votes-ru.yml +1 -1
- data/config/locales/votes-sv.yml +1 -1
- data/config/routes.rb +2 -0
- data/db/migrate/20170330000001_create_votes.rb +4 -1
- data/db/migrate/20200207101010_add_uuid_to_votes.rb +21 -0
- data/lib/biovision/vote/version.rb +1 -1
- metadata +6 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0b3721a3296b82fb23854ea3bdd4365a278fef68ea522d0d84da05c3e6162d5
|
4
|
+
data.tar.gz: 6596c3c780a7e2ddc47bac92924fdad775495f5531a67f87e7083ee0f032a4dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 250ac4ffb39d351bea23e77056173fbe7d73834318121ba74923cf1305aa175bae7b676cd5880116976ddf38559374b31d8326e0db0d10b6c4296cbe8f975d8f
|
7
|
+
data.tar.gz: 7276e36d6d53e06091b01711d671f1cb05782714cf2789cf4b6990a6e45798564b672c33c013d01070289dae306bed24428f3cbab9c01cdd89d5bb09ce887db4
|
@@ -1,97 +1,66 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
36
|
+
container.querySelectorAll(".vote").forEach(function (element) {
|
37
|
+
element.disabled = true;
|
43
38
|
});
|
44
|
-
});
|
45
39
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
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
|
-
|
70
|
-
|
71
|
-
const response = JSON.parse(this.responseText);
|
56
|
+
Biovision.handleAjaxFailure();
|
57
|
+
};
|
72
58
|
|
73
|
-
|
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
|
-
|
89
|
-
|
61
|
+
button.classList.add("switch");
|
62
|
+
request.send(JSON.stringify(data));
|
63
|
+
}
|
64
|
+
};
|
90
65
|
|
91
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
&.voted-none {
|
23
|
+
.upvote:not(:disabled):hover {
|
24
|
+
background-image: image_url('biovision/vote/icons/upvote-active.svg');
|
25
|
+
}
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
}
|
27
|
+
.downvote:not(:disabled):hover {
|
28
|
+
background-image: image_url('biovision/vote/icons/downvote-active.svg');
|
25
29
|
}
|
26
30
|
}
|
27
31
|
|
@@ -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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
data/app/models/vote.rb
CHANGED
@@ -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) ||
|
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.
|
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.
|
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
|
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]
|
File without changes
|
@@ -1,16 +1,10 @@
|
|
1
|
-
<%
|
2
|
-
|
3
|
-
%>
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
<
|
8
|
-
|
9
|
-
|
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>
|
data/config/locales/votes-en.yml
CHANGED
data/config/locales/votes-ru.yml
CHANGED
data/config/locales/votes-sv.yml
CHANGED
data/config/routes.rb
CHANGED
@@ -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::
|
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
|
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
|
+
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:
|
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/
|
144
|
-
- app/views/admin/components/links/
|
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.
|
152
|
+
rubygems_version: 3.1.4
|
180
153
|
signing_key:
|
181
154
|
specification_version: 4
|
182
155
|
summary: Voting functionality for biovision-based applications
|