/** * Interactions. */ import { fetchJson } from './utils/requests'; const endpoints = { vote(imageId) { return `/images/${imageId}/vote` }, fave(imageId) { return `/images/${imageId}/fave` }, hide(imageId) { return `/images/${imageId}/hide` }, }; const spoilerDownvoteMsg = 'Neigh! - Remove spoilered tags from your filters to downvote from thumbnails'; /* Quick helper function to less verbosely iterate a QSA */ function onImage(id, selector, cb) { [].forEach.call( document.querySelectorAll(`${selector}[data-image-id="${id}"]`), cb); } function setScore(imageId, data) { onImage(imageId, '.score', el => el.textContent = data.score); onImage(imageId, '.favorites', el => el.textContent = data.faves); onImage(imageId, '.upvotes', el => el.textContent = data.upvotes); onImage(imageId, '.downvotes', el => el.textContent = data.downvotes); } /* These change the visual appearance of interaction links. * Their classes also effect their behavior due to event delegation. */ function showUpvoted(imageId) { onImage(imageId, '.interaction--upvote', el => el.classList.add('active')); } function showDownvoted(imageId) { onImage(imageId, '.interaction--downvote', el => el.classList.add('active')); } function showFaved(imageId) { onImage(imageId, '.interaction--fave', el => el.classList.add('active')); } function showHidden(imageId) { onImage(imageId, '.interaction--hide', el => el.classList.add('active')); } function resetVoted(imageId) { onImage(imageId, '.interaction--upvote', el => el.classList.remove('active')); onImage(imageId, '.interaction--downvote', el => el.classList.remove('active')); } function resetFaved(imageId) { onImage(imageId, '.interaction--fave', el => el.classList.remove('active')); } function resetHidden(imageId) { onImage(imageId, '.interaction--hide', el => el.classList.remove('active')); } function interact(type, imageId, method, data = {}) { return fetchJson(method, endpoints[type](imageId), data) .then(res => res.json()) .then(res => setScore(imageId, res)); } function displayInteractionSet(interactions) { interactions.forEach(i => { switch (i.interaction_type) { case 'faved': showFaved(i.image_id); break; case 'hidden': showHidden(i.image_id); break; default: if (i.value === 'up') showUpvoted(i.image_id); if (i.value === 'down') showDownvoted(i.image_id); } }); } function loadInteractions() { /* Set up the actual interactions */ displayInteractionSet(window.booru.interactions); /* Next part is only for image index pages * TODO: find a better way to do this */ if (!document.getElementById('imagelist-container')) return; /* Users will blind downvote without this */ window.booru.imagesWithDownvotingDisabled.forEach(i => { onImage(i, '.interaction--downvote', a => { // TODO Use a 'js-' class to target these instead const icon = a.querySelector('i') || a.querySelector('.oc-icon-small'); icon.setAttribute('title', spoilerDownvoteMsg); a.classList.add('disabled'); a.addEventListener('click', event => { event.stopPropagation(); event.preventDefault(); }, true); }); }); } const targets = { /* Active-state targets first */ '.interaction--upvote.active'(imageId) { interact('vote', imageId, 'DELETE') .then(() => resetVoted(imageId)); }, '.interaction--downvote.active'(imageId) { interact('vote', imageId, 'DELETE') .then(() => resetVoted(imageId)); }, '.interaction--fave.active'(imageId) { interact('fave', imageId, 'DELETE') .then(() => resetFaved(imageId)); }, '.interaction--hide.active'(imageId) { interact('hide', imageId, 'DELETE') .then(() => resetHidden(imageId)); }, /* Inactive targets */ '.interaction--upvote:not(.active)'(imageId) { interact('vote', imageId, 'POST', { up: true }) .then(() => { resetVoted(imageId); showUpvoted(imageId); }); }, '.interaction--downvote:not(.active)'(imageId) { interact('vote', imageId, 'POST', { up: false }) .then(() => { resetVoted(imageId); showDownvoted(imageId); }); }, '.interaction--fave:not(.active)'(imageId) { interact('fave', imageId, 'POST') .then(() => { resetVoted(imageId); showFaved(imageId); showUpvoted(imageId); }); }, '.interaction--hide:not(.active)'(imageId) { interact('hide', imageId, 'POST') .then(() => { showHidden(imageId); }); }, }; function bindInteractions() { document.addEventListener('click', event => { if (event.button === 0) { // Is it a left-click? for (const target in targets) { /* Event delegation doesn't quite grab what we want here. */ const link = event.target && event.target.closest(target); if (link) { event.preventDefault(); targets[target](link.dataset.imageId); } } } }); } function loggedOutInteractions() { [].forEach.call(document.querySelectorAll('.interaction--fave,.interaction--upvote,.interaction--downvote'), a => a.setAttribute('href', '/sessions/new')); } function setupInteractions() { if (window.booru.userIsSignedIn) { bindInteractions(); loadInteractions(); } else { loggedOutInteractions(); } } export { setupInteractions, displayInteractionSet };