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