PERF: Paginate public polls.
This commit is contained in:
parent
e66c51fd85
commit
a36203ff78
|
@ -3,29 +3,14 @@ import User from 'discourse/models/user';
|
||||||
import PollVoters from 'discourse/plugins/poll/components/poll-voters';
|
import PollVoters from 'discourse/plugins/poll/components/poll-voters';
|
||||||
|
|
||||||
export default PollVoters.extend({
|
export default PollVoters.extend({
|
||||||
@computed("pollsVoters", "poll.options", "showMore", "isExpanded", "numOfVotersToShow")
|
@computed("poll.voters", "pollsVoters")
|
||||||
users(pollsVoters, options, showMore, isExpanded, numOfVotersToShow) {
|
canLoadMore(voters, pollsVoters) {
|
||||||
var users = [];
|
return pollsVoters.length < voters;
|
||||||
var voterIds = [];
|
|
||||||
const shouldLimit = showMore && !isExpanded;
|
|
||||||
|
|
||||||
options.forEach(option => {
|
|
||||||
option.voter_ids.forEach(voterId => {
|
|
||||||
if (shouldLimit) {
|
|
||||||
if (!(users.length > numOfVotersToShow - 1)) {
|
|
||||||
users.push(pollsVoters[voterId]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
users.push(pollsVoters[voterId]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
return users;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("pollsVoters", "numOfVotersToShow")
|
@computed("poll.options", "offset")
|
||||||
showMore(pollsVoters, numOfVotersToShow) {
|
voterIds(options) {
|
||||||
return !(Object.keys(pollsVoters).length < numOfVotersToShow);
|
const ids = [].concat(...(options.map(option => option.voter_ids)));
|
||||||
|
return this._getIds(ids);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,8 +2,6 @@ import round from "discourse/lib/round";
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
export default Em.Component.extend({
|
export default Em.Component.extend({
|
||||||
tagName: "span",
|
|
||||||
|
|
||||||
@computed("poll.options.@each.{html,votes}")
|
@computed("poll.options.@each.{html,votes}")
|
||||||
totalScore() {
|
totalScore() {
|
||||||
return _.reduce(this.get("poll.options"), function(total, o) {
|
return _.reduce(this.get("poll.options"), function(total, o) {
|
||||||
|
|
|
@ -3,23 +3,13 @@ import User from 'discourse/models/user';
|
||||||
import PollVoters from 'discourse/plugins/poll/components/poll-voters';
|
import PollVoters from 'discourse/plugins/poll/components/poll-voters';
|
||||||
|
|
||||||
export default PollVoters.extend({
|
export default PollVoters.extend({
|
||||||
@computed("pollsVoters", "option.voter_ids", "showMore", "isExpanded", "numOfVotersToShow")
|
@computed("option.votes", "pollsVoters")
|
||||||
users(pollsVoters, voterIds, showMore, isExpanded, numOfVotersToShow) {
|
canLoadMore(voters, pollsVoters) {
|
||||||
var users = [];
|
return pollsVoters.length < voters;
|
||||||
|
|
||||||
if (showMore && !isExpanded) {
|
|
||||||
voterIds = voterIds.slice(0, numOfVotersToShow);
|
|
||||||
}
|
|
||||||
|
|
||||||
voterIds.forEach(voterId => {
|
|
||||||
users.push(pollsVoters[voterId]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return users;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("option.votes", "numOfVotersToShow")
|
@computed("option.voter_ids", "offset")
|
||||||
showMore(numOfVotes, numOfVotersToShow) {
|
voterIds(ids) {
|
||||||
return !(numOfVotes < numOfVotersToShow);
|
return this._getIds(ids);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,11 +3,51 @@ export default Ember.Component.extend({
|
||||||
tagName: 'ul',
|
tagName: 'ul',
|
||||||
classNames: ["poll-voters-list"],
|
classNames: ["poll-voters-list"],
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
numOfVotersToShow: 20,
|
numOfVotersToShow: 0,
|
||||||
|
offset: 0,
|
||||||
|
loading: false,
|
||||||
|
pollsVoters: null,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super();
|
||||||
|
this.set("pollsVoters", []);
|
||||||
|
},
|
||||||
|
|
||||||
|
_fetchUsers() {
|
||||||
|
this.set("loading", true);
|
||||||
|
|
||||||
|
Discourse.ajax("/polls/voters.json", {
|
||||||
|
type: "get",
|
||||||
|
data: { user_ids: this.get("voterIds") }
|
||||||
|
}).then(result => {
|
||||||
|
if (this.isDestroyed) return;
|
||||||
|
this.set("pollsVoters", this.get("pollsVoters").concat(result.users));
|
||||||
|
this.incrementProperty("offset");
|
||||||
|
this.set("loading", false);
|
||||||
|
}).catch((error) => {
|
||||||
|
Ember.logger.log(error);
|
||||||
|
bootbox.alert(I18n.t('poll.error_while_fetching_voters'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_getIds(ids) {
|
||||||
|
const numOfVotersToShow = this.get("numOfVotersToShow");
|
||||||
|
const offset = this.get("offset");
|
||||||
|
return ids.slice(numOfVotersToShow * offset, numOfVotersToShow * (offset + 1));
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
Ember.run.schedule("afterRender", () => {
|
||||||
|
this.set("numOfVotersToShow", Math.round(this.$().width() / 25) * 2);
|
||||||
|
if (this.get("voterIds").length > 0) this._fetchUsers();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
toggleExpand() {
|
loadMore() {
|
||||||
this.toggleProperty("isExpanded");
|
this._fetchUsers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,6 @@ export default Ember.Controller.extend({
|
||||||
isRandom : Ember.computed.equal("poll.order", "random"),
|
isRandom : Ember.computed.equal("poll.order", "random"),
|
||||||
isClosed: Ember.computed.equal("poll.status", "closed"),
|
isClosed: Ember.computed.equal("poll.status", "closed"),
|
||||||
isPublic: Ember.computed.equal("poll.public", "true"),
|
isPublic: Ember.computed.equal("poll.public", "true"),
|
||||||
pollsVoters: Ember.computed.alias("post.polls_voters"),
|
|
||||||
|
|
||||||
// shows the results when
|
// shows the results when
|
||||||
// - poll is closed
|
// - poll is closed
|
||||||
|
@ -152,10 +151,6 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
this.setProperties({ vote: votes, showResults: true });
|
this.setProperties({ vote: votes, showResults: true });
|
||||||
this.set("model", Em.Object.create(poll));
|
this.set("model", Em.Object.create(poll));
|
||||||
|
|
||||||
if (poll.public) {
|
|
||||||
this.get("pollsVoters")[currentUser.get("id")] = currentUser;
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
bootbox.alert(I18n.t("poll.error_while_casting_votes"));
|
bootbox.alert(I18n.t("poll.error_while_casting_votes"));
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{{{averageRating}}}
|
<div class="poll-results-number-rating">
|
||||||
|
{{{averageRating}}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{#if poll.public}}
|
{{#if poll.public}}
|
||||||
{{poll-results-number-voters poll=poll pollsVoters=pollsVoters}}
|
{{poll-results-number-voters poll=poll}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if poll.public}}
|
{{#if poll.public}}
|
||||||
{{poll-results-standard-voters option=option pollsVoters=pollsVoters}}
|
{{poll-results-standard-voters option=option}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="poll-voters">
|
<div class="poll-voters">
|
||||||
{{#each users as |user|}}
|
{{#each pollsVoters as |user|}}
|
||||||
<li>
|
<li>
|
||||||
<a data-user-card={{unbound user.username}}>
|
<a data-user-card={{unbound user.username}}>
|
||||||
{{avatar user imageSize="tiny" ignoreTitle="true"}}
|
{{avatar user imageSize="tiny" ignoreTitle="true"}}
|
||||||
|
@ -8,12 +8,10 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<div class="poll-voters-toggle-expand">
|
<div class="poll-voters-toggle-expand">
|
||||||
{{#if showMore}}
|
{{#if canLoadMore}}
|
||||||
{{#if isExpanded}}
|
{{#conditional-loading-spinner condition=loading size="small"}}
|
||||||
<a {{action "toggleExpand"}}>{{fa-icon "chevron-up"}}</a>
|
<a {{action "loadMore"}}>{{fa-icon "chevron-down"}}</a>
|
||||||
{{else}}
|
{{/conditional-loading-spinner}}
|
||||||
<a {{action "toggleExpand"}}>{{fa-icon "chevron-down"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
<div class="poll-container">
|
<div class="poll-container">
|
||||||
{{#if showingResults}}
|
{{#if showingResults}}
|
||||||
{{#if isNumber}}
|
{{#if isNumber}}
|
||||||
{{poll-results-number poll=poll pollsVoters=pollsVoters}}
|
{{poll-results-number poll=poll}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{poll-results-standard poll=poll pollsVoters=pollsVoters}}
|
{{poll-results-standard poll=poll}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -29,10 +29,6 @@ function initializePolls(api) {
|
||||||
const post = this.get('model.postStream').findLoadedPost(msg.post_id);
|
const post = this.get('model.postStream').findLoadedPost(msg.post_id);
|
||||||
if (post) {
|
if (post) {
|
||||||
post.set('polls', msg.polls);
|
post.set('polls', msg.polls);
|
||||||
|
|
||||||
if (msg.user) {
|
|
||||||
post.set(`polls_voters.${msg.user.id}`, msg.user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -80,7 +76,6 @@ function initializePolls(api) {
|
||||||
const post = helper.getModel();
|
const post = helper.getModel();
|
||||||
api.preventCloak(post.id);
|
api.preventCloak(post.id);
|
||||||
const votes = post.get('polls_votes') || {};
|
const votes = post.get('polls_votes') || {};
|
||||||
post.set("polls_voters", (post.get("polls_voters") || {}));
|
|
||||||
|
|
||||||
post.pollsChanged();
|
post.pollsChanged();
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ div.poll {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
& > span {
|
.poll-results-number-rating {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,10 +97,11 @@ div.poll {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
margin: 5px 0;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-voters-toggle-expand {
|
.poll-voters-toggle-expand {
|
||||||
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,3 +67,4 @@ en:
|
||||||
|
|
||||||
error_while_toggling_status: "There was an error while toggling the status of this poll."
|
error_while_toggling_status: "There was an error while toggling the status of this poll."
|
||||||
error_while_casting_votes: "There was an error while casting your votes."
|
error_while_casting_votes: "There was an error while casting your votes."
|
||||||
|
error_while_fetching_voters: "There was an error while displaying the voters."
|
||||||
|
|
|
@ -185,7 +185,7 @@ after_initialize do
|
||||||
class DiscoursePoll::PollsController < ::ApplicationController
|
class DiscoursePoll::PollsController < ::ApplicationController
|
||||||
requires_plugin PLUGIN_NAME
|
requires_plugin PLUGIN_NAME
|
||||||
|
|
||||||
before_filter :ensure_logged_in
|
before_filter :ensure_logged_in, except: [:voters]
|
||||||
|
|
||||||
def vote
|
def vote
|
||||||
post_id = params.require(:post_id)
|
post_id = params.require(:post_id)
|
||||||
|
@ -214,11 +214,22 @@ after_initialize do
|
||||||
render_json_error e.message
|
render_json_error e.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def voters
|
||||||
|
user_ids = params.require(:user_ids)
|
||||||
|
|
||||||
|
users = User.where(id: user_ids).map do |user|
|
||||||
|
UserNameSerializer.new(user).serializable_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: { users: users }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
DiscoursePoll::Engine.routes.draw do
|
DiscoursePoll::Engine.routes.draw do
|
||||||
put "/vote" => "polls#vote"
|
put "/vote" => "polls#vote"
|
||||||
put "/toggle_status" => "polls#toggle_status"
|
put "/toggle_status" => "polls#toggle_status"
|
||||||
|
get "/voters" => 'polls#voters'
|
||||||
end
|
end
|
||||||
|
|
||||||
Discourse::Application.routes.append do
|
Discourse::Application.routes.append do
|
||||||
|
@ -299,26 +310,4 @@ after_initialize do
|
||||||
return unless post_custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD].present?
|
return unless post_custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD].present?
|
||||||
post_custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD].has_key?("#{scope.user.id}")
|
post_custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD].has_key?("#{scope.user.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
add_to_serializer(:post, :polls_voters) do
|
|
||||||
voters = {}
|
|
||||||
|
|
||||||
user_ids = post_custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD].keys
|
|
||||||
|
|
||||||
User.where(id: user_ids).map do |user|
|
|
||||||
voters[user.id] = UserNameSerializer.new(user).serializable_hash
|
|
||||||
end
|
|
||||||
|
|
||||||
voters
|
|
||||||
end
|
|
||||||
|
|
||||||
add_to_serializer(:post, :include_polls_voters?) do
|
|
||||||
return unless post_custom_fields.present?
|
|
||||||
return unless post_custom_fields[DiscoursePoll::POLLS_CUSTOM_FIELD].present?
|
|
||||||
return unless post_custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD].present?
|
|
||||||
|
|
||||||
post_custom_fields[DiscoursePoll::POLLS_CUSTOM_FIELD].any? do |_, value|
|
|
||||||
value["public"] == "true"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -173,5 +173,4 @@ describe ::DiscoursePoll::PollsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe "DiscoursePoll endpoints" do
|
||||||
|
describe "fetch voters from user_ids" do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
|
it "should return the right response" do
|
||||||
|
get "/polls/voters.json", { user_ids: [user.id] }
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
json = JSON.parse(response.body)["users"].first
|
||||||
|
|
||||||
|
expect(json["name"]).to eq(user.name)
|
||||||
|
expect(json["title"]).to eq(user.title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue