favorites: show favlist when hovering over favcount.

Changes:

* Make it so you can click or hover over a post's favorite count to see
  the list of public favorites.
* Remove the "Show »" button next to the favorite count.
* Make the favorites list visible to all users. Before favorites were
  only visible to Gold users.
* Make the /favorites page show the list of all public favorites,
  instead of redirecting to the current user's favorites.
* Add /posts/:id/favorites endpoint.
* Add /users/:id/favorites endpoint.

This is for several reasons:

* To make viewing favorites work the same way as viewing upvotes.
* To make posts load faster for Gold users. Before, we loaded all the
  favorites when viewing a post, even when the user didn't look at them.
  This made pageloads slower for posts that had hundreds or thousands of
  favorites. Now we only load the favlist if the user hovers over the favcount.
* To make the favorite list visible to all users. Before, it wasn't
  visible to non-Gold users, because of the performance issue listed above.
* To make it more obvious that favorites are public by default. Before,
  since regular users could only see the favcount, they may have
  mistakenly believed other users couldn't see their favorites.
This commit is contained in:
evazion
2021-11-19 20:54:02 -06:00
parent c4ad50bbba
commit 3ae62d08eb
23 changed files with 229 additions and 78 deletions

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
# This component represents the tooltip that displays when you hover over a post's favorite count.
class FavoritesTooltipComponent < ApplicationComponent
attr_reader :post, :current_user
def initialize(post:, current_user:)
super
@post = post
@current_user = current_user
end
def favorites
post.favorites.includes(:user).order(id: :desc)
end
def favoriter_name(favorite)
if policy(favorite).can_see_favoriter?
link_to_user(favorite.user)
else
tag.i("hidden")
end
end
end

View File

@@ -0,0 +1,9 @@
<div class="favorites-tooltip thin-scrollbar">
<div class="post-favoriters">
<% favorites.each do |favorite| %>
<div class="post-favoriter truncate">
<%= favoriter_name(favorite) %>
</div>
<% end %>
</div>
</div>

View File

@@ -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 FavoritesTooltipComponent {
// Trigger on the post favcount link.
static TARGET_SELECTOR = "span.post-favcount a";
static SHOW_DELAY = 125;
static HIDE_DELAY = 125;
static DURATION = 250;
static instance = null;
static initialize() {
if ($(FavoritesTooltipComponent.TARGET_SELECTOR).length === 0) {
return;
}
FavoritesTooltipComponent.instance = delegate("body", {
allowHTML: true,
appendTo: document.querySelector("#post-favorites-tooltips"),
delay: [FavoritesTooltipComponent.SHOW_DELAY, FavoritesTooltipComponent.HIDE_DELAY],
duration: FavoritesTooltipComponent.DURATION,
interactive: true,
maxWidth: "none",
target: FavoritesTooltipComponent.TARGET_SELECTOR,
theme: "common-tooltip",
touch: false,
onShow: FavoritesTooltipComponent.onShow,
onHide: FavoritesTooltipComponent.onHide,
});
}
static async onShow(instance) {
let $target = $(instance.reference);
let $tooltip = $(instance.popper);
let postId = $target.parents("[data-id]").data("id");
hideAll({ exclude: instance });
try {
$tooltip.addClass("tooltip-loading");
instance._request = $.get(`/posts/${postId}/favorites?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 favorites for post #${postId} (error: ${error.status} ${error.statusText})`);
}
}
}
static async onHide(instance) {
if (instance._request?.state() === "pending") {
instance._request.abort();
}
}
}
$(document).ready(FavoritesTooltipComponent.initialize);
export default FavoritesTooltipComponent;

View File

@@ -0,0 +1,13 @@
.favorites-tooltip {
font-size: var(--text-xs);
max-height: 240px;
.post-favoriter {
max-width: 160px;
}
}
span.post-favcount a {
color: var(--text-color);
&:hover { text-decoration: underline; }
}