diff --git a/app/components/comment_component/comment_component.html.erb b/app/components/comment_component/comment_component.html.erb index 0bcd59987..63494df20 100644 --- a/app/components/comment_component/comment_component.html.erb +++ b/app/components/comment_component/comment_component.html.erb @@ -53,13 +53,13 @@ <%= link_to upvote_icon, comment_comment_votes_path(comment_id: comment.id, score: "1"), class: "comment-upvote-link inactive-link", method: :post, remote: true %> <% end %> - <% if policy(CommentVote).can_see_votes? %> - <%= link_to comment_votes_path(search: { comment_id: comment.id }, variant: "compact"), class: "inactive-link" do %> - <%= comment.score %> + + <% if policy(CommentVote).can_see_votes? %> + <%= link_to comment.score, comment_votes_path(search: { comment_id: comment.id }, variant: "compact"), class: "inactive-link" %> + <% else %> + <%= comment.score %> <% end %> - <% else %> - <%= comment.score %> - <% end %> + <% if current_user.is_anonymous? %> <%= link_to downvote_icon, login_path(url: request.fullpath), class: "comment-downvote-link inactive-link" %> diff --git a/app/components/comment_component/comment_component.scss b/app/components/comment_component/comment_component.scss index d50b8ca61..e0e1fd756 100644 --- a/app/components/comment_component/comment_component.scss +++ b/app/components/comment_component/comment_component.scss @@ -36,6 +36,7 @@ article.comment { text-align: center; min-width: 1.25em; white-space: nowrap; + vertical-align: middle; } .icon { diff --git a/app/components/comment_votes_tooltip_component.rb b/app/components/comment_votes_tooltip_component.rb new file mode 100644 index 000000000..4645ffe8c --- /dev/null +++ b/app/components/comment_votes_tooltip_component.rb @@ -0,0 +1,32 @@ +# This component represents the tooltip that displays when you hover over a comment's score. +class CommentVotesTooltipComponent < ApplicationComponent + attr_reader :comment, :current_user + delegate :upvote_icon, :downvote_icon, to: :helpers + + def initialize(comment:, current_user:) + super + @comment = comment + @current_user = current_user + end + + def votes + comment.votes.active.includes(:user).order(id: :desc) + end + + def upvote_count + votes.select(&:is_positive?).length + end + + def downvote_count + votes.select(&:is_negative?).length + end + + def upvote_ratio + return nil if votes.length == 0 + sprintf("(%.1f%%)", 100.0 * upvote_count / votes.length) + end + + def vote_icon(vote) + vote.is_positive? ? upvote_icon : downvote_icon + end +end diff --git a/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.html.erb b/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.html.erb new file mode 100644 index 000000000..170a5c6ce --- /dev/null +++ b/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.html.erb @@ -0,0 +1,15 @@ +
diff --git a/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.js b/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.js new file mode 100644 index 000000000..6501131ee --- /dev/null +++ b/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.js @@ -0,0 +1,65 @@ +import Utility from "../../javascript/src/javascripts/utility.js"; +import { delegate, hideAll } from 'tippy.js'; +import 'tippy.js/dist/tippy.css'; + +class CommentVotesTooltipComponent { + // Trigger on the comment score link; see CommentComponent. + static TARGET_SELECTOR = "span.comment-score"; + static SHOW_DELAY = 125; + static HIDE_DELAY = 125; + static DURATION = 250; + static instance = null; + + static initialize() { + if ($(CommentVotesTooltipComponent.TARGET_SELECTOR).length === 0) { + return; + } + + CommentVotesTooltipComponent.instance = delegate("body", { + allowHTML: true, + appendTo: document.querySelector("#comment-votes-tooltips"), + delay: [CommentVotesTooltipComponent.SHOW_DELAY, CommentVotesTooltipComponent.HIDE_DELAY], + duration: CommentVotesTooltipComponent.DURATION, + interactive: true, + maxWidth: "none", + target: CommentVotesTooltipComponent.TARGET_SELECTOR, + theme: "common-tooltip", + touch: false, + + onShow: CommentVotesTooltipComponent.onShow, + onHide: CommentVotesTooltipComponent.onHide, + }); + } + + static async onShow(instance) { + let $target = $(instance.reference); + let $tooltip = $(instance.popper); + let commentId = $target.parents("[data-id]").data("id"); + + hideAll({ exclude: instance }); + + try { + $tooltip.addClass("tooltip-loading"); + + instance._request = $.get(`/comments/${commentId}/votes`, { variant: "tooltip" }); + let html = await instance._request; + instance.setContent(html); + + $tooltip.removeClass("tooltip-loading"); + } catch (error) { + if (error.status !== 0 && error.statusText !== "abort") { + Utility.error(`Error displaying votes for comment #${commentId} (error: ${error.status} ${error.statusText})`); + } + } + } + + static async onHide(instance) { + if (instance._request?.state() === "pending") { + instance._request.abort(); + } + } +} + +$(document).ready(CommentVotesTooltipComponent.initialize); + +export default CommentVotesTooltipComponent; diff --git a/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.scss b/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.scss new file mode 100644 index 000000000..db9b794a9 --- /dev/null +++ b/app/components/comment_votes_tooltip_component/comment_votes_tooltip_component.scss @@ -0,0 +1,15 @@ +.comment-votes-tooltip { + max-height: 240px; + + .upvote-icon { + color: var(--post-upvote-color); + } + + .downvote-icon { + color: var(--post-downvote-color); + } + + .comment-voter { + max-width: 160px; + } +} diff --git a/app/components/post_votes_tooltip_component/post_votes_tooltip_component.html.erb b/app/components/post_votes_tooltip_component/post_votes_tooltip_component.html.erb index a50e1e3e0..b90f76119 100644 --- a/app/components/post_votes_tooltip_component/post_votes_tooltip_component.html.erb +++ b/app/components/post_votes_tooltip_component/post_votes_tooltip_component.html.erb @@ -1,4 +1,4 @@ -