/** * Interactions. */ import { fetchJson } from './utils/requests'; import { $$ } from './utils/dom'; const endpoints = { fave: `${window.booru.apiEndpoint}interactions/fave`, vote: `${window.booru.apiEndpoint}interactions/vote`, hide: `${window.booru.apiEndpoint}interactions/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( $$(`.js-post-root[data-post-id="${id}"]`).map(elem => elem.querySelector(selector)).filter(elem => elem !== null), cb); } function setScore(imageId, data) { onImage(imageId, '.score', el => el.textContent = data.score); onImage(imageId, '.votes', el => el.textContent = data.votes); onImage(imageId, '.favorites', el => el.textContent = data.favourites); 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, value) { return fetchJson('PUT', endpoints[type], { class: 'Post', id: imageId, value }) .then(res => res.json()) .then(res => setScore(imageId, res)); } function displayInteractionSet(interactions) { interactions.forEach(i => { switch (i.interaction_type) { case 'faved': showFaved(i.post_id); break; case 'hidden': showHidden(i.post_id); break; default: if (i.value === 'up') showUpvoted(i.post_id); if (i.value === 'down') showDownvoted(i.post_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 interactionActions = { u(postId, newState) { interact('vote', postId, newState ? 'up' : 'false').then(() => { resetVoted(postId); if (newState) showUpvoted(postId); }); }, d(postId, newState) { interact('vote', postId, newState ? 'down' : 'false').then(() => { resetVoted(postId); if (newState) showDownvoted(postId); }); }, f(postId, newState) { interact('fave', postId, newState ? 'true' : 'false') .then(() => { if (newState) { resetVoted(postId); showUpvoted(postId); showFaved(postId); } else { resetFaved(postId); } }); }, h(postId, newState) { interact('hide', postId, newState ? 'true' : 'false') .then(() => { if (newState) { showHidden(postId); } else { resetHidden(postId); } }); } }; function bindInteractions() { [].forEach.call($$('.interaction--upvote, .interaction--downvote, .interaction--fave, .interaction--hide'), elem => { elem.addEventListener('click', evt => { if (evt.button !== 0) return; /* Skip anything but a left click */ evt.preventDefault(); const evtButton = evt.target.closest('button'); const evtMediaBox = evtButton.closest('.js-post-root'); const evtPostId = evtMediaBox.dataset.postId; const interaction = evtButton.getAttribute('value'); interactionActions[interaction](evtPostId, !evtButton.classList.contains('active')); }) }); } function loggedOutInteractions() { [].forEach.call(document.querySelectorAll('.interaction--fave,.interaction--upvote,.interaction--downvote'), a => a.setAttribute('href', '/users/sign_in')); } function setupInteractions() { if (window.booru.userIsSignedIn) { bindInteractions(); loadInteractions(); } else { loggedOutInteractions(); } } export { setupInteractions, displayInteractionSet };