// Reaction and share functionality (function() { // Generate or retrieve visitor ID function getVisitorId() { let visitorId = localStorage.getItem('visitorId'); if (!visitorId) { visitorId = 'visitor_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); localStorage.setItem('visitorId', visitorId); } return visitorId; } // Load reactions for a post async function loadReactions(postId) { try { const response = await fetch(`/api/reaction.php?postId=${postId}`); const data = await response.json(); if (data.success) { updateReactionButtons(postId, data.counts); } } catch (error) { console.error('Error loading reactions:', error); } } // Send reaction async function sendReaction(postId, reaction) { const visitorId = getVisitorId(); try { const response = await fetch('/api/reaction.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ postId: postId, reaction: reaction, visitorId: visitorId }) }); if (!response.ok) { console.error('API error:', response.status, response.statusText); return; } const data = await response.json(); console.log('Reaction response:', data); // Debug log if (data.success && data.counts) { updateReactionButtons(postId, data.counts); } else { console.error('Invalid response:', data); } } catch (error) { console.error('Error sending reaction:', error); } } // Update reaction button displays function updateReactionButtons(postId, counts) { const container = document.querySelector(`#post-${postId} .reactions-buttons`); if (!container) return; ['like', 'love', 'helpful'].forEach(reaction => { const button = container.querySelector(`.reaction-btn[data-reaction="${reaction}"]`); if (button) { const count = counts[reaction] || 0; const countSpan = button.querySelector('.reaction-count'); if (countSpan) { countSpan.textContent = count; } } }); } // Track share event async function trackShare(postId, platform) { const visitorId = getVisitorId(); try { await fetch('/api/track.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'share', page: `/blog/post.html?id=${postId}`, visitorId: visitorId, platform: platform }) }); } catch (error) { console.error('Error tracking share:', error); } } // Inline SVG icons - using currentColor for theme support const iconSVGs = { like: '', love: '', helpful: '', mastodon: '', bluesky: '', copy: '' }; // Create reaction buttons HTML function createReactionButtons(postId) { return `
React
`; } // Create share buttons HTML function createShareButtons(postId) { return `
Share
`; } // Handle reaction clicks document.addEventListener('click', function(e) { if (e.target.closest('.reaction-btn')) { const button = e.target.closest('.reaction-btn'); const postId = button.dataset.post; const reaction = button.dataset.reaction; sendReaction(postId, reaction); } }); // Handle share clicks document.addEventListener('click', function(e) { if (e.target.closest('.share-btn')) { const button = e.target.closest('.share-btn'); const platform = button.dataset.platform; const postId = button.dataset.post; const postTitle = document.querySelector(`#post-${postId} .blog-post-title`)?.textContent || ''; // Construct URL without port (for .onion compatibility) const origin = window.location.protocol + '//' + window.location.hostname; const url = origin + '/blog/post.html?id=' + postId; if (platform === 'mastodon') { trackShare(postId, 'mastodon'); // Open Mastodon share - user can paste the URL const mastodonUrl = `https://defcon.social/share?text=${encodeURIComponent(postTitle + ' ' + url)}`; window.open(mastodonUrl, '_blank', 'width=550,height=420'); } else if (platform === 'bluesky') { trackShare(postId, 'bluesky'); // Open Bluesky share const blueskyUrl = `https://bsky.app/intent/compose?text=${encodeURIComponent(postTitle + ' ' + url)}`; window.open(blueskyUrl, '_blank', 'width=550,height=420'); } else if (platform === 'copy') { trackShare(postId, 'copy'); navigator.clipboard.writeText(url).then(() => { const icon = button.querySelector('.share-icon-img'); if (icon) { const originalHTML = icon.outerHTML; const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark'; const checkColor = isDarkMode ? '#59C99C' : 'currentColor'; icon.outerHTML = ``; setTimeout(() => { const checkIcon = button.querySelector('.share-icon-img'); if (checkIcon) { checkIcon.outerHTML = originalHTML; } }, 2000); } }); } } }); // Export functions for use in blog.js window.blogReactions = { createReactionButtons: createReactionButtons, createShareButtons: createShareButtons, loadReactions: loadReactions }; })();