diff --git a/lib/MetaCPAN/Web/Model/ReleaseInfo.pm b/lib/MetaCPAN/Web/Model/ReleaseInfo.pm
index fc28bfc242..637250941c 100644
--- a/lib/MetaCPAN/Web/Model/ReleaseInfo.pm
+++ b/lib/MetaCPAN/Web/Model/ReleaseInfo.pm
@@ -75,6 +75,7 @@ sub _fetch {
sub {
my ($dist) = @_;
return (
+ [ favorites => $self->_favorite->by_dist($dist) ],
[ plussers => $self->_favorite->find_plussers($dist) ],
[ versions => $self->_release->versions($dist) ],
[ distribution => $self->_distribution->get($dist) ],
diff --git a/root/inc/favorite.tx b/root/inc/favorite.tx
index 8008863b72..aa8009fbeb 100644
--- a/root/inc/favorite.tx
+++ b/root/inc/favorite.tx
@@ -1,4 +1,15 @@
-
+
+
+
+
+
+
%% if $github && $repository.url {
diff --git a/root/static/js/cpan.js b/root/static/js/cpan.js
index 8f56c36db3..25a5144030 100644
--- a/root/static/js/cpan.js
+++ b/root/static/js/cpan.js
@@ -13,6 +13,9 @@ require('bootstrap/js/dropdown.js');
require('bootstrap/js/modal.js');
require('bootstrap/js/tooltip.js');
+function setFavTitle(button) {
+ button.setAttribute('title', button.classList.contains('active') ? 'Remove from favorites' : 'Add to favorites');
+}
async function processUserData() {
let user_data;
@@ -49,6 +52,20 @@ async function processUserData() {
document.querySelector('.logged-in-icon').replaceWith(avatar);
}
+ // process users current favs
+ for (const fav of user_data.faves) {
+ const distribution = fav.distribution;
+
+ // On the page... make it deltable and styled as 'active'
+ const fav_display = document.querySelector(`#${distribution}-fav`);
+
+ if (fav_display) {
+ fav_display.querySelector('input[name="remove"]').value = 1;
+ var button = fav_display.querySelector('button');
+ button.classList.add('active');
+ setFavTitle(button);
+ }
+ }
}
function set_page_size(selector, storage_name) {
@@ -155,6 +172,10 @@ for (const el of document.querySelectorAll('.ellipsis')) {
createAnchors(document.querySelectorAll('.anchors'));
+for (const favButton of document.querySelectorAll('.breadcrumbs .favorite')) {
+ setFavTitle(favButton);
+}
+
jQuery('.dropdown-toggle').dropdown(); // bootstrap
const toc = document.querySelector(".content .toc")
@@ -203,6 +224,54 @@ if (changes) {
});
}
+for (const favForm of document.querySelectorAll('form[action="/account/favorite/add"]')) {
+ favForm.addEventListener('submit', async e => {
+ e.preventDefault();
+ const formData = new FormData(favForm);
+ const response = await fetch(favForm.action, {
+ method: favForm.method,
+ headers: {
+ 'Accept': 'application/json',
+ },
+ body: formData,
+ });
+ if (!response.ok) {
+ alert("Error adding favorite!");
+ }
+
+ const button = favForm.querySelector('button');
+ button.classList.toggle('active');
+ setFavTitle(button);
+ const counter = button.querySelector('span');
+ const count = counter.innerText;
+ if (button.classList.contains('active')) {
+ counter.innerText = count ? parseInt(count, 10) + 1 : 1;
+ // now added let users remove
+ favForm.querySelector('input[name="remove"]').value = 1;
+ if (!count)
+ button.classList.toggle('highlight');
+ }
+ else {
+ // can't delete what's already deleted
+ favForm.querySelector('input[name="remove"]').value = 0;
+
+ counter.textContent = parseInt(count, 10) - 1;
+
+ if (counter.textContent == 0) {
+ counter.textContent = '';
+ button.classList.toggle('highlight');
+ }
+ }
+ });
+}
+
+for (const favButton of document.querySelectorAll('.fav-not-logged-in')) {
+ favButton.addEventListener('click', e => {
+ e.preventDefault();
+ alert('Please sign in to add favorites');
+ });
+}
+
for (const sel of document.querySelectorAll('.select-navigator')) {
sel.addEventListener('change', () => {
document.location.href = sel.value;
diff --git a/t/controller/shared/release-info.t b/t/controller/shared/release-info.t
index ff3ccb1b47..5d5145ec87 100644
--- a/t/controller/shared/release-info.t
+++ b/t/controller/shared/release-info.t
@@ -16,6 +16,7 @@ my $rt_prefix = $model->RT_URL_PREFIX;
# Not all tests apply to all releases.
my @optional = qw(
+ favorited
home_page
repository
issues
@@ -69,6 +70,7 @@ test_psgi app, sub {
home_page => 0,
reviews => 0,
repository => 0,
+ favorited => 0
},
);
@@ -125,6 +127,18 @@ test_psgi app, sub {
# TODO: latest version (should be where we already are)
# TODO: author
+ # not in release-info.html but should be shown on both:
+
+ my $favs = '//*[contains-token(@class, "favorite")]';
+ $tx->like( $favs, qr/\+\+$/, 'tag for favorites (++)' );
+
+ optional_test favorited => sub {
+ ok(
+ $tx->find_value("$favs/span") > 0,
+ "$req_uri has been marked as favorite"
+ );
+ };
+
# Info about a release (either the one we're looking at or the one the module belongs to)
# TODO: Download